Unable to access the this context inside the callback in NestJs - nestjs

import {ApiTags} from "#nestjs/swagger";
import {Body, Controller, Get, Param, Post, Res,Req} from "#nestjs/common";
import {ReceiptService} from "../service/ReceiptService";
import {ReceiptDto} from "../common/domain/ReceiptDto";
import {Response,Request} from 'express';
import {HttpService} from "#nestjs/axios";
#ApiTags('Receipt')
#Controller({version:'1',path:'Receipt'})
export class ReceiptController{
constructor(private readonly receiptService:ReceiptService,private readonly httpService:HttpService) {
}
#Post()
generateReport(#Body() receiptDto:ReceiptDto){
return this.receiptService.getReceiptData(receiptDto.OrderId);
}
#Get(':OrderId')
async displayReceipt(#Param('OrderId') orderId:number,#Res() res:Response,#Req() req:Request){
const data=await this.receiptService.getReceiptData(orderId);
res.render('OrderReceipt',{orderData:data},function (err,html) {
res.send(html).on('finish',async function (){
const result=await this.httpService.get(`http://localhost:51971/pdfgenerator?url=localhost:4200/v1/Receipt/${orderId}`).bind(this);
console.log(result);
});
});
}
}
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'get' of undefined Error has been thrown on this.httpService.get() line. I don't know how to solve this problem. Thanks and regards.

Change the function in the res.render callback to an arrow function to preserve the this context of the class.
#ApiTags('Receipt')
#Controller({version:'1',path:'Receipt'})
export class ReceiptController{
constructor(private readonly receiptService:ReceiptService,private readonly httpService:HttpService) {
}
#Post()
generateReport(#Body() receiptDto:ReceiptDto){
return this.receiptService.getReceiptData(receiptDto.OrderId);
}
#Get(':OrderId')
async displayReceipt(#Param('OrderId') orderId:number,#Res() res:Response,#Req() req:Request){
const data=await this.receiptService.getReceiptData(orderId);
res.render('OrderReceipt',{orderData:data}, (err,html) => {
res.send(html).on('finish',async () => {
const result=await this.httpService.get(`http://localhost:51971/pdfgenerator?url=localhost:4200/v1/Receipt/${orderId}`).bind(this);
console.log(result);
});
});
}
}

Related

NestJS testing that interception has been called on a controller

I'm looking to see if there is a recommended way to write a JEST test that an Interceptor has been called. In the example below LoggingInterceptor was called? The purpose of test is verify that NestJS Binding interceptors is in place.
import { Controller, Get, UseInterceptors } from '#nestjs/common';
import { AppService } from './app.service';
import { LoggingInterceptor, TransformInterceptor } from './transform.interceptor';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#UseInterceptors(LoggingInterceptor)
#Get()
getHello(): string {
return this.appService.getHello();
}
}```
I would advise against testing that. Think about what you're testing: you're testing that the framework is doing what it says it will, something that the framework itself already tests. Sure you could use the Reflect API and verify that that metadata does exist, but that's something the framework should assert, it's a feature you should just be able to use with peace of mind.
One option I used to create a Jest test to verify that a binding interceptor(Which transformed the response) on a controller was called and produced the expected response was by using NestJS Supertest lib to simulate an end to end test.
Related NestJS doc:
List item
https://docs.nestjs.com/fundamentals/testing#end-to-end-testing
Test Code Sample:
import { INestApplication } from '#nestjs/common';
import { Test } from '#nestjs/testing';
import * as request from 'supertest';
import { CatsModule } from '../../src/cats/cats.module';
import { CatsService } from '../../src/cats/cats.service';
import { CoreModule } from '../../src/core/core.module';
describe('Test interceptor response binding was triggerred', () => {
const aServiceResponse = { findAll: () => ['test'] };
const expectedResponseAfterTransformation = { code: 200, data: [ 'test' ] };
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [CatsModule, CoreModule],
})
.overrideProvider(CatsService)
.useValue(aServiceResponse)
.compile();
app = moduleRef.createNestApplication();
await app.init();
});
it(`/GET cats returns transformed data respsone`, () => {
return request(app.getHttpServer()).get('/cats').expect(200).expect({
data: expectedResponseAfterTransformation,
});
});
afterAll(async () => {
await app.close();
});
});

NestJS testing with Jest custom repository (CassandraDB)

The code I am trying to test the driver / repository for my nodeJS project:
import { Injectable, OnModuleInit } from '#nestjs/common';
import { mapping, types } from 'cassandra-driver';
import { Products } from './poducts.model';
import { CassandraService } from '../database/cassandra/cassandra.service';
import Uuid = types.Uuid;
#Injectable()
export class ProductsRepository implements OnModuleInit {
constructor(private cassandraService: CassandraService) {}
productsMapper: mapping.ModelMapper<Products>;
onModuleInit() {
const mappingOptions: mapping.MappingOptions = {
models: {
Products: {
tables: ['products'],
mappings: new mapping.UnderscoreCqlToCamelCaseMappings(),
},
},
};
this.productsMapper = this.cassandraService
.createMapper(mappingOptions)
.forModel('Products');
}
async getProducts() {
return (await this.productsMapper.findAll()).toArray(); // <-----Breaks here with findAll()
}
}
I am trying to write something like this:
describe('product repository get all', () => {
it('calls the repository get all', async () => {
const await productsRepository.getProducts();
expect().DoSomething()
});
});
This is the error I am getting:
Cannot read property 'findAll' of undefined
How would I accomplish a meaning-full test with Jest to get proper code coverage?
When I try to use jest to spy on the this.products.Mapper.findAll() it seems to break every time.

TypeError: metadata_1.Public is not a function (NestJS SetMetaData)

My e2e test is returning TypeError: metadata_1.Public is not a function for a controller that is using the custom decorator #Public()
Some code is omitted for clarity
it(`/GET forks`, async () => {
const fork: ForksModel = {
type: 'Full Copy',
};
await request(app.getHttpServer())
.get('/forks')
.expect(200)
.expect({ fork: expectedForks});
});
#Public()
public async getAccountForks(#Req() req: Request) {
const { account } = req;
const fork = await this.service.getAccountForks(account);
return { fork, account };
}
public.decorator.ts
import { SetMetadata } from "#nestjs/common";
export const Public = () => SetMetadata( "isPublic", true );
I don't know what is happening here, it doesn't complain this when running nest
This is imported
import { Public } from '#app/utils/metadata';
So i just forgot to export my metadata files from the root utils index.ts!
But Nest didn't complain and the decorator was functional on my Guard when testing!

Node.js Express API with TypeScript 3 Update a record

I have setup Node.js Express API with TypeScript 3 and it is working fine.
I got an issue when I try to update the record.
RecordsRouter.ts
import { Router } from 'express';
import {RecordComponent} from '../../components';
const router: Router = Router();
router.get('/', RecordComponent.findAll);
router.get('/:id', RecordComponent.findOne);
router.post('/', RecordComponent.create);
router.delete('/:id', RecordComponent.remove);
router.put('/:id', RecordComponent.update);
export default router;
My RecordComponent.ts
export async function update(req: Request, res: Response, next: NextFunction): Promise <void> {
try {
const record: IRecord = await RecordService.put(req.body)
res.status(201).json(record);
} catch (error) {
next(new HttpError(error.message.status, error.message));
}
}
and my IRepository.ts
export interface IRepository<T> {
findAll(): Promise<T[]>;
findOne(code: string): Promise<T>;
insert(T: any): Promise<T>;
remove(id: string): Promise<T>;
put:(T: any)=>Promise<T>;
}
Service.ts
async put(data: IRecord): Promise {
try {
const validate: Joi.ValidationResult = RecordValidation.updateRecord(data);
if(validate.error) {
throw new Error(validate.error.message);
}
return await RecordModel.findOneAndUpdate(data);
} catch (error) {
throw new Error(error.message);
}
},
Did I did all correctly or something is missing because I am getting the error
That means you didn't implement all of the interface members in RecordService. Either implement them or mark them as optional in the IRepository interface by adding a question mark before the colon:
export interface IRepository<T> {
findAll()?: Promise<T[]>;
findOne(code: string)?: Promise<T>;
insert(T: any)?: Promise<T>;
remove(id: string)?: Promise<T>;
put(id: string)?: Promise<T>;
}
So, You should implement RecordService in next way:
const RecordService:IRepository<IRecord>={
// ...some code for findAll, findOne ....
remove:(id: string)=>Promise.resolve(),
put:(id: string)=>Promise.resolve(),
}
UPDATE
Your RecordModel.findOneAndUpdate(data) should receive 0 arguments:
RecordModel.findOneAndUpdate() or RecordModel.findOneAndUpdate(data, other, another)
Please get familiar with mongoose docs

typescript + node How to get instanse from methods?

After server rebuild, compiller creates instanse in included api controller here:
NewController.ts
import express = require("express");
import INew = require("../interface/INew");
import NewRepository = require("../repositories/NewRepository");
class NewController {
private _newRepository: INew;
constructor() {
this._newRepository = new NewRepository();
this._newRepository.findById(5);
}
retrieve(req: express.Request, res: express.Response): void {
try {
console.log('-----------retrieve--------------------');
this._newRepository.findById(2);
}
catch (e) {
console.log(e);
}
}
}
Object.seal(NewController);
export = NewController;
constructor works: i see console message:
-------------NewRepository------------------
5 'RESULT'
NewRepository.ts:
import INew = require("../interface/INew");
import bluebird = require("bluebird");
class NewRepository implements INew {
sd: string;
constructor() {
console.log('-------------NewRepository------------------');
}
findById(id: number): void {
setTimeout(function () {
console.log(id, 'RESULT');
}, 3000);
}
}
export = NewRepository;
INew.ts
interface INew {
findById: (id: number) => void;
sd: string;
}
export = INew;
Buut when i use controller's method 'retrieve', visit rout '/new' then i get error [TypeError: Cannot read property '_newRepository' of undefined] instead : 2 'RESULT'
Angular 2 helps me with routing:
.............
getCarsRestful(): Promise<New[]> {
console.log('-------------------------------');
return this.http.get('api/new')
.toPromise()
.then(response => response.json())
.catch(this.handleError);
}
...................
and execute backend:
NewRoutes.ts
import express = require("express");
import NewController = require('../controllers/NewController');
var router = express.Router();
class NewRoutes {
private _newController: NewController;
constructor() {
this._newController = new NewController()
}
get routes() {
var controller = this._newController;
router.get("/new", controller.retrieve);
return router;
}
}
Object.seal(NewRoutes);
export = NewRoutes;
my created instanse '_newRepository' doesn't exist already, why? i get console log:
-----------retrieve--------------------
[TypeError: Cannot read property '_newRepository' of undefined]
Help please, how to make 'singltone' in ts
i don't want to create it in every controller's method, though, that works:
.................
retrieve(req: express.Request, res: express.Response): void {
try {
var _newRepository: INew;
_newRepository = new NewRepository();
_newRepository.findById(2);
.............
Try explicitly set this in router config:
router.get("/new", controller.retrieve.bind(controller));

Resources