Why model has undefined state with repository pattern? - javascript

I'm trying to use repository pattern with TypeScript
Now I have base.repository that implements all of the functions that I need, I made it a generic type, and I wanna pass the model while injecting it in constructor, but for some reason, while passing the value, I have undefined state of the particular model, what am I doing wrong?
In the console.log() it shows me that the model is undefined while in file register.service.ts it shows me also undefined, but I passed it as generic.
register.service.ts
import { BaseRepository } from "../repositories/base.repository";
import { Creator } from '../data/models/Creator'
import { RegisterDto } from "../types/dtos/register.dto";
import { Injectable } from '#nestjs/common'
import { RegisterMapper } from '../mappers/register.mapper'
import { errors } from '../errors'
import { mailer } from '../utils/nodemailer'
#Injectable()
export class RegisterService {
constructor(
private readonly repository: BaseRepository<Creator>,
private readonly mapper: RegisterMapper
) { }
async createAccount (doc: RegisterDto) {
const emailExist = await this.existByEmail(doc.email)
if (emailExist) {
return errors.EMAIL_EXIST()
}
const created = await this.repository.create(this.mapper.toDomain(doc))
await mailer(doc.email)
return created
}
private async existByEmail(email: string): Promise<boolean> {
console.log(email)
console.log(this.repository)
const response = await this.repository.get({ email })
return !!response.email;
}
}
base.repository.ts
import { ModelType } from '#typegoose/typegoose/lib/types'
import { DuplicateKeyError } from '../errors/DuplicateKeyError'
import { DocumentNotFoundError } from '../errors/DocumentNotFoundError'
import { Model } from 'mongoose'
import { Inject, Injectable, Optional } from '#nestjs/common'
#Injectable()
export class BaseRepository<T = any> {
constructor(
#Optional() #Inject('MODEL') private Model: any
) { }
async create (object): Promise<T> {
const Model = this.Model
console.log(Model)
const uniqueKey = Model.getUniqueKey ? Model.getUniqueKey() : null
if (uniqueKey && object[uniqueKey]) {
const criteria = {
[uniqueKey]: object[uniqueKey]
}
const existing = await Model.findOne(criteria)
if (existing) {
throw new DuplicateKeyError(Model, criteria)
}
}
const model = new Model(object)
return model.save()
}
async update (criteria, object, options = {}) {
const Model = this.Model
const uniqueKey = Model.getUniqueKey ? Model.getUniqueKey() : '_id'
const data = { ...object }
delete data[uniqueKey]
delete data.createdAt
return this.updateRaw(criteria, { $set: { ...data } }, options)
}
async updateRaw (criteria, data, options = {}) {
const query = this._getDbQuery(criteria, options)
const result = await this.Model.findOneAndUpdate(query, data, { new: true, ...options })
if (!result) {
throw new DocumentNotFoundError(this.Model, query)
}
return result
}
async save (modelInstance) {
return modelInstance.save()
}
async get (criteria, options: any = {}): Promise<T | undefined> {
console.log(Model)
const promise = await this.Model.findOne(this._getDbQuery(criteria, options)).exec()
if (options.select) {
promise.select(options.select)
}
return promise
}
async find (criteria, options): Promise<ReturnType<ModelType<T>['find']>> {
return this.Model.find(this._getDbQuery(criteria, options))
}
async resolve (criteria): Promise<T> {
return this.Model.resolve(this._getDbQuery(criteria))
}
async count (query) {
return this.Model.countDocuments(this._getDbQuery(query))
}
async delete (criteria) {
return this.Model.remove(this._getDbQuery(criteria))
}
_getDbQuery (criteria, options: any = {}) {
if ('getDbQuery' in criteria) {
const dbQuery = criteria.getDbQuery(options)
return 'find' in dbQuery
? dbQuery.find
: dbQuery
} else {
return criteria
}
}
}
What should I do to get the actual model in this repository?

I have added inject tokens in each service before repository Injection
Now the code looks as follows
import { BaseRepository } from "../repositories/base.repository";
import { Creator } from '../data/models/Creator'
import { RegisterDto } from "../types/dtos/register.dto";
import { Injectable } from '#nestjs/common'
import { RegisterMapper } from '../mappers/register.mapper'
import { errors } from '../errors'
import { mailer } from '../utils/nodemailer'
import { CREATOR } from '../utils'
#Injectable()
export class RegisterService {
registerMapper = new RegisterMapper()
constructor(
#Inject(CREATOR) private readonly repository: BaseRepository<Creator>,
) { }
async createAccount (doc: RegisterDto) {
const emailExist = await this.existByEmail(doc.email)
if (emailExist) {
return errors.EMAIL_EXIST()
}
const created = await this.repository.create(this.mapper.toDomain(doc))
await mailer(doc.email)
return created
}
private async existByEmail(email: string): Promise<boolean> {
console.log(email)
console.log(this.repository)
const response = await this.repository.get({ email })
return !!response.email;
}
}
inject-tokens.ts
export const CREATOR = 'CREATOR'

Related

coverage issue in nestjs roles.guard file

I have below file in nestjs. test cases for this code is getting passed. but coverage issue is coming so pipeline getting failed.
import { Injectable, CanActivate, ExecutionContext, Logger } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
import { ROLES_KEY } from './roles.decorator';
import { Role } from './role.enum';
#Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
const { user } = context.switchToHttp().getRequest();
if (!user.role) {
Logger.error('User does not have a role set');
return false;
}
if (user.role === Role.Admin) {
return true;
}
if (!Array.isArray(requiredRoles) || !requiredRoles.length) {
// No #Roles() decorator set, deny access as not admin
return false;
}
if (requiredRoles.includes(Role.All)) {
return true;
}
return requiredRoles.includes(user.role);
}
}
I am writing below test case file for it.
import { createMock } from '#golevelup/ts-jest';
import { ExecutionContext } from "#nestjs/common";
import { Reflector } from "#nestjs/core";
import { RolesGuard } from "./roles.guard";
describe('RolesGuard', () => {
let guard: RolesGuard;
let reflector: Reflector;
beforeEach(() => {
reflector = new Reflector();
guard = new RolesGuard(reflector);
});
it('should be defined', () => {
expect(guard).toBeDefined();
});
it('should return false if user does not exist', () => {
reflector.getAllAndOverride = jest.fn().mockReturnValue(true);
const context = createMock<ExecutionContext>();
const canActivate = guard.canActivate(context);
expect(canActivate).toBe(false);
})
})
but coverage is not showing for below line
if (user.role === Role.Admin) {
return true;
}
if (!Array.isArray(requiredRoles) || !requiredRoles.length) {
// No #Roles() decorator set, deny access as not admin
return false;
}
if (requiredRoles.includes(Role.All)) {
return true;
}
return requiredRoles.includes(user.role);
could you please help me on the same? I am not able to understand how to handle coverage of these lines. I am invoking method but still it is showing.
I also asked similar question test case for roles guard file in nestjs

Class extends value undefined is not a constructor or null when testing in jest

I'm trying to create a base class that contains common logic between multiple AWS Lambdas. However, my test fails with:
Class extends value undefined is not a constructor or null
I tried to create a base handler like this:
import { APIGatewayEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { Logger } from '..';
export abstract class BaseHandler<T extends APIGatewayEvent> {
abstract process(event: T, context: Context): Promise<APIGatewayProxyResult>;
public handle = async (event: T, context: Context): Promise<APIGatewayProxyResult> => {
Logger.debug(`Received event: ${JSON.stringify(event)}`);
Logger.debug(`Received context: ${JSON.stringify(context)}`);
try {
const result: APIGatewayProxyResult = await this.process(event, context);
Logger.debug(`Returning response: ${JSON.stringify(result)}`);
return result;
} catch (e) {
Logger.error(`Caught error in handler: ${JSON.stringify(e)}`);
const errorResponse: APIGatewayProxyResult = {
statusCode: statusCode,
body: JSON.stringify({
code: e.statusCode,
message: e.message
})
};
return errorResponse;
}
};
}
module.exports.BaseHandler;
And then a specific lambda that inherits this:
import { BaseHandler } from '../base-handler.abstract.class';
import { APIGatewayProxyResult, Context } from 'aws-lambda';
import { InheritedLambdaRequest } from '../types/inherited-lambda-request.type';
class InheritedLambda extends BaseHandler<InheritedLambdaRequest> {
constructor() {
super();
}
public process = async (event: InheritedLambdaRequest, context: Context): Promise<APIGatewayProxyResult> => {
//business logic
}
}
export const handler = new InheritedLambda();
export const inheritedLambda = handler.process;
However, my jest test fails:
import { inheritedLambda } from './inherited-lambda';
describe('Handle Inherited Lambda', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should return 204', (done) => {
inheritedLambda(request, undefined).then((result) => {
expect(result.statusCode).toEqual(204);
expect(result.body).toEqual('');
done();
});
});
});

Map method is only showing one item from the list

I have created a React App and I am using .Net Core in the backend, the list of data from backend is successfully received, but in react while using Map it only shows one item from the list.I ma using MObX for state management.
My Code is :
import React, { useContext, useEffect } from 'react'
import { RootStoreContext } from '../../app/stores/rootStore';
import { observer } from 'mobx-react-lite';
import { Segment, Item, Icon, Button } from 'semantic-ui-react';
import { format } from 'date-fns';
import { Link } from 'react-router-dom';
const BookList: React.FC = () => {
const rootStore = useContext(RootStoreContext);
const { loadBooks, getAvailableBooks } = rootStore.bookStore;
useEffect(() => {
loadBooks();
}, [loadBooks]);
return (
<div>
{getAvailableBooks.map(books => (
<Segment.Group key={books.bookName}>
<Segment>
<Item.Group>
<Item>
<Item.Image size='tiny' circular src='/assets/user.png' />
<Item.Content>
<Item.Header as='a'>{books.bookName}</Item.Header>
</Item.Content>
</Item>
</Item.Group>
</Segment>
</Segment.Group>
))}
</div>
)
}
export default observer(BookList);
My BookStore is :
import { observable, action, computed, runInAction } from "mobx";
import agent from "../api/agent";
import { RootStore } from "./rootStore";
import { IBooks } from "../models/books";
export default class BookStore {
rootStore: RootStore;
constructor(rootStore: RootStore) {
this.rootStore = rootStore;
}
#observable bookRegistry = new Map();
#observable book: IBooks | null = null;
#observable loadingInitial = false;
#computed get getAvailableBooks() {
return Array.from(this.bookRegistry.values());
}
#action loadBooks = async () => {
this.loadingInitial = true;
try {
const books = await agent.Books.list();
runInAction("loading books", () => {
books.forEach((books) => {
books.issuedOn = new Date(books.issuedOn);
this.bookRegistry.set(books.id, books);
});
this.loadingInitial = false;
});
} catch (error) {
runInAction("load books error", () => {
this.loadingInitial = false;
});
}
};
}
and API is called from agent.ts
import axios, { AxiosResponse } from "axios";
import { history } from "../..";
import { toast } from "react-toastify";
import { IBooks } from "../models/books";
axios.defaults.baseURL = "https://localhost:44396/api";
const requests = {
get: (url: string) => axios.get(url).then(sleep(1000)).then(responseBody),
post: (url: string, body: {}) =>
axios.post(url, body).then(sleep(1000)).then(responseBody),
put: (url: string, body: {}) =>
axios.put(url, body).then(sleep(1000)).then(responseBody),
del: (url: string) => axios.delete(url).then(sleep(1000)).then(responseBody),
};
const Books = {
list: (): Promise<IBooks[]> => requests.get("/Book/GetBookList"),
};
export default {
User
};
export interface IBooks {
id: number;
bookname: string;
issuedOn: Date;
isReturned: boolean;
isRequested: boolean;
isAvailable: boolean;
isTaken: boolean;
name: string;
}
The response from API
from the screenshot of your API response, it seems that each "book" object does not have an id property. This might explain why you only see one element rendered, because in your loadBooks action, each time you try to do this.bookRegistry.set(books.id, books), you're using undefined as the key, and then on the next iteration you overwrite the value stored at that key.

Apollo RESTDataSource calling other RESTDataSource?

Say I have an 2 API classes that extend RESTDataSource:
class MoviesAPI extends RESTDataSource {
async getMovies() {}
}
class SongsAPI extends RESTDataSource {
async getSongs() {}
}
How can I call getSongs from getMovies within the existing Apollo server context?
You can get the apollo server context via this.context in the datasource class and get dataSources via this.context.dataSources.
E.g.
server.ts:
import { ApolloServer, gql } from 'apollo-server';
import { MoviesAPI } from './MoviesAPI';
import { SongsAPI } from './SongsAPI';
const typeDefs = gql`
type Query {
movies: String
}
`;
const resolvers = {
Query: {
movies: (_, __, { dataSources }) => {
return dataSources.moviesAPI.getMovies();
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => {
return {
moviesAPI: new MoviesAPI(),
songsAPI: new SongsAPI(),
};
},
});
server.listen().then(({ url }) => {
console.log(`Apollo server is listening on ${url}`);
});
MoviesAPI.ts:
import { RESTDataSource } from 'apollo-datasource-rest';
export class MoviesAPI extends RESTDataSource {
async getMovies() {
const songs = await this.context.dataSources.songsAPI.getSongs();
const movies = ['a', 'b'];
return JSON.stringify({ movies, songs });
}
}
SongsAPI.ts:
import { RESTDataSource } from 'apollo-datasource-rest';
export class SongsAPI extends RESTDataSource {
async getSongs() {
return ['x', 'y'];
}
}
Send a GraphQL query from the client-side:
query{
movies
}
Response payload:
{
"data": {
"movies": "{\"movies\":[\"a\",\"b\"],\"songs\":[\"x\",\"y\"]}"
}
}
Package versions: "apollo-datasource-rest": "^0.8.1", "apollo-server": "^2.12.0"
source code: https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/stackoverflow/61425326

getPastEvents undefined(re)

I had asked this question before.
I looked carefully at the exchange section, which I advised, and I think there is no problem with the exchange section.
At least in my opinion there is no problem and I hardly know the cause of the problem.
And I was so frustrated that I put everything in the code.
If anyone can provide us with a clue to this problem, please reply to me.
interactions.js
import Web3 from 'web3'
import {
web3Loaded,
web3AccountLoaded,
tokenLoaded,
exchangeLoaded,
cancelledOrdersLoaded
} from './actions'
import Token from '../abis/Token.json'
import Exchange from '../abis/Exchange.json'
export const loadWeb3 = (dispatch) => {
const web3 = new Web3(Web3.givenProvider || 'http://localhost:7545')
dispatch(web3Loaded(web3))
return web3
}
export const loadAccount = async (web3, dispatch) => {
const accounts = await web3.eth.getAccounts()
const account = accounts[0]
dispatch(web3AccountLoaded(account))
return account
}
export const loadToken = async (web3, networkId, dispatch) => {
try {
const token = new web3.eth.Contract(Token.abi, Token.networks[networkId].address)
dispatch(tokenLoaded(token))
return token
} catch (error) {
console.log('Contract not deployed to the current network. Please select another network with Metamask.')
return null
}
}
export const loadExchange = async (web3, networkId, dispatch) => {
try {
const exchange = new web3.eth.Contract(Exchange.abi, Exchange.networks[networkId].address)
dispatch(exchangeLoaded(exchange))
return exchange
} catch (error) {
console.log('Contract not deployed to the current network. Please select another network with Metamask.')
return null
}
}
export const loadAllOrders = async (exchange, dispatch) => {
// if (exchange) { // Make sure exchange has been defined
// const exchange = new web3.eth.Contract(Exchange.abi, Exchange.networks[networkId].address)
const cancelStream = await exchange.getPastEvents('Cancel', { fromBlock: 0, toBlock: 'latest' })
// // await loadAllOrders(this.props.exchange, dispatch)
console.log(cancelStream)
}
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Navbar from './Navbar'
import Web3 from 'web3';
import Content from './Content'
import { connect } from 'react-redux'
// import Token from '../abis/Token.json'
import {
loadWeb3,
loadAccount,
loadToken,
loadExchange
} from '../store/interactions'
import { contractsLoadedSelector } from '../store/selectors'
class App extends Component {
componentWillMount() {
this.loadBlockchainData(this.props.dispatch)
}
async loadBlockchainData(dispatch) {
const web3 = loadWeb3(dispatch)
const network = await web3.eth.net.getNetworkType()
const networkId = await web3.eth.net.getId()
const accounts = await loadAccount(web3, dispatch)
const token = await loadToken(web3, networkId, dispatch)
if(!token) {
window.alert('Token smart contract not detected on the current network. Please select another network with Metamask.')
return
}
const exchange = await loadExchange(web3, networkId, dispatch)
if(!exchange) {
window.alert('Exchange smart contract not detected on the current network. Please select another network with Metamask.')
return
}
}
render() {
return (
<div>
<Navbar />
{ this.props.contractsLoaded ? <Content /> : <div className="content"></div> }
</div>
);
}
}
function mapStateToProps(state) {
return {
contractsLoaded: contractsLoadedSelector(state)
}
}
export default connect(mapStateToProps)(App);
reducers.js
import { combineReducers } from 'redux';
function web3(state={}, action) {
switch (action.type) {
case 'WEB3_LOADED':
return { ...state, connection: action.connection }
case 'WEB3_ACCOUNT_LOADED':
return { ...state, account: action.account }
default:
return state
}
}
function token(state = {}, action) {
switch (action.type) {
case 'TOKEN_LOADED':
return { ...state, loaded: true, contract: action.contract }
default:
return state
}
}
function exchange(state = {}, action) {
switch (action.type) {
case 'EXCHANGE_LOADED':
return { ...state, loaded: true, contract: action.contract }
case 'CANCELLED_ORDERS_LOADED':
return { ...state, cancelledOrders: { loaded: true, data: action.cancelledOrders } }
// case 'FILLED_ORDERS_LOADED':
// return { ...state, filledOrders: { loaded: true, data: action.filledOrders } }
// case 'ALL_ORDERS_LOADED':
// return { ...state, allOrders: { loaded: true, data: action.allOrders } }
default:
return state
}
}
const rootReducer = combineReducers({
web3,
token,
exchange
})
export default rootReducer
Content.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { exchangeSelector } from '../store/selectors'
import { loadAllOrders } from '../store/interactions'
class Content extends Component {
componentWillMount() {
this.loadBlockchainData(this.props.dispatch)
}
// async loadBlockchainData(exchange, dispatch) {
async loadBlockchainData(dispatch) {
await loadAllOrders(this.props.exchange, dispatch)
// this.loadBlockchainData(this.props.exchange)
// await loadAllOrders(exchange, dispatch)
}
function mapStateToProps(state) {
return {
exchange: state.exchangeSelector
}
}
export default connect(mapStateToProps)(Content)
selectors.js
import { get } from 'lodash'
import { createSelector } from 'reselect'
const account = state => get(state, 'web3.account')
export const accountSelector = createSelector(account, a => a)
const tokenLoaded = state => get(state, 'token.loaded', false)
export const tokenLoadedSelector = createSelector(tokenLoaded, tl => tl)
const exchangeLoaded = state => get(state, 'exchange.loaded', false)
export const exchangeLoadedSelector = createSelector(exchangeLoaded, el => el)
const exchange = state => get(state, 'exchange.contract')
export const exchangeSelector = createSelector(exchange, e => e)
export const contractsLoadedSelector = createSelector(
tokenLoaded,
exchangeLoaded,
(tl, el) => (tl && el)
)
Check exchange to make sure not undefined
export const loadAllOrders = async (exchange, dispatch) => {
const cancelStream = exchange ?
await exchange.getPastEvents('Cancel', { fromBlock: 0, toBlock: 'latest' })
: null // Check if exchange defined then call getPastEvents
console.log(cancelStream)
}

Categories