how to inject context to HttpModule - nestjs

I want inject context.heanders to Httpmodule when it register.how?
nestjs 6.0
i create a global Module like below, i want add context.header in this place
#Global()
#Module({
imports: [
HttpModule.register({
timeout: 20000,
// (how to get context.header in this place )
// headers: {
// 'x-sec-profile': '281567',
// 'accessToken': 'f647e353790fd5c54dbd8c06be4575a58f369476a569c71a',
// },
}),
],
})
export class AxiosModule {}
this is cats.resolver.ts
#Resolver(Cat)
export class CatsResolver {
constructor(private readonly catsService: CatsService) {}
#Query(returns => Cat, { name: 'cat' })
async findDetail(#Args('id') id: number, #Context('headers') headers) {
console.log(arguments.length)
return await this.catsService.findDetail(id)
}
}
use cats.service.ts
#Injectable()
export class CatsService {
constructor(private readonly httpService: HttpService) { }
async findDetail(id) {
const a = await this.httpService.get('https://yuangong.com/api/contract-web/contract/contract-detail',
{params: { contractId: 1000320 } }).toPromise()
return {
contractId: a.data.data.id,
}
}
}

Related

how to inject my services layer into Auth Guard? NestJS - AuthGuards

I am creating an authentication system via UseGuards, but how can I inject dependencies into my guards?
I'd like to do it in a global way, to avoid repeating code and every controller importing the injections.
I am using the mode of dependency inversion and injecting the classes, I am also making the classes only depend on implementations and interface rules...
My AuthServices
export class AuthServices implements IMiddlewareAuth {
constructor(
private readonly jwt: IJWTDecrypt,
private readonly authUserRepo: IAuthUserContract,
) {}
public async intercept(
req: IMiddlewareAuth.Attrs,
): Promise<IMiddlewareAuth.Return> {
const token = req.headers['Authorization'];
if (!token) {
new Fails('Access denied', 403);
}
const parts = token.split(' ');
if (parts.length !== 2) {
return new Fails('Anauthorized', 401);
}
const [scheme, access] = parts;
if (!/^Bearer$/i.test(scheme)) {
return new Fails('Anauthorized', 400);
}
const id = await this.jwt.decrypt(access);
if (!id) {
return new Fails('Anauthorized', 400);
}
const user = await this.authUserRepo.findById(id);
if (!user) {
return new Fails('Anauthorized', 400);
}
return user;
}
}
my Auth.Guards
#Injectable()
export class AuthorizationGuard implements CanActivate {
constructor(
#Inject('AuthServices')
private authServices: IMiddlewareAuth,
) {}
public async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const user = await this.authServices.intercept(request);
if (user instanceof Fails) {
throw new HttpException(
{
statusCode: user.statusCode,
error: user.message,
},
user.statusCode,
);
}
request.user = {
email: user.email,
id: user.id,
name: user.name,
} as ISessionLogin;
return true;
}
}
my Auth.Module
#Module({
imports: [ConfigModule.forRoot(), TypeOrmModule.forFeature([UserEntity])],
providers: [
{
provide: AuthUserRepository,
useFactory: (dataSource: DataSource) => {
return new AuthUserRepository(
dataSource.getMongoRepository(UserEntity),
);
},
inject: [getDataSourceToken()],
},
{
provide: JsonWebToken,
useFactory: () => {
return new JsonWebToken();
},
},
{
provide: AuthServices,
useFactory: (
jwt: JsonWebToken,
authUserRepository: AuthUserRepository,
) => {
return new AuthServices(jwt, authUserRepository);
},
inject: [JsonWebToken, AuthUserRepository],
},
],
exports: [AuthServices],
})
export class AuthModule {}
Error
Use #Inject(AuthService) instead of #Inject('AuthService'). The important distinction is that 'AuthService' is a string token that matches AuthService.name and AuthService is a class reference. Also, make sure the CreateUsersModule has AuthModule in its imports array

Nestjs with PassportModule custom AuthGuard inject ConfigService?

i want know how to inject ConfigService in PassportModule AuthGuard with custom params?
here is my code
auth.module.ts
#Module({
imports: [
PassportModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService): Promise<IAuthModuleOptions> => {
return {}
}
}),
],
// ...
})
export class AuthModule {}
github-auth.guard.ts
#Controller('auth')
export class AuthController {
// ...
#Get('github-oauth')
#UseGuards(new GithubAuthGuard({}))
async githubOAuth(#Req() req: Request, #Res() res: Response) {
const user = req.user
return await this.handleOauth(user, req, res)
}
}
github-auth.guard.ts
import { ConfigService } from '#nestjs/config'
import { AuthGuard } from '#nestjs/passport'
#Injectable()
export class GithubAuthGuard extends AuthGuard('github') {
constructor(private readonly configService: ConfigService) {
super()
}
//...
}
i call the UseGuards with new GithubAuthGuard({}) because i want pass custom params.
Update:
AuthGuard('github') return a wraped class can accept options the then pass down to my custom strategy's authenticate function as the second argument.
here is my github.strategy.ts
import { Strategy } from 'passport'
class StrategyFoo extends Strategy {
constructor(options, verify) {
//...
}
// options from AuthGuard('github')
authenticate(req, options) {
const self = this
const redirect_uri = options.callbackURL || this._options.callbackURL
// ...
}
}
#Injectable()
export class GithubBarStrategy extends PassportStrategy(StrategyFoo, 'github') {
//...
}
export const GithubStrategy = GithubBarStrategy
after some research i figure it out
Nest will not inject anything on a manually instantiated class
so just call #UseGuards(GithubAuthGuard) and then inject ConfigService in github-auth.guard.ts or github.strategy.ts like:
inject in github-auth.guard.ts
#Injectable()
export class GithubAuthGuard extends AuthGuard('github') {
// #Inject(ConfigService)
// private configService: ConfigService
constructor(private readonly configService: ConfigService) {
//
const options = {
callbackURL: configService.get('OAuth.github.callbackURL'),
//...
}
super(options)
}
getAuthenticateOptions(context) {
// this also works
return {
callbackURL: this.configService.get('OAuth.github.callbackURL'),
//...
}
}
// ...
}
or
inject in github.strategy.ts
class StrategyFoo extends Strategy {
private _options: any
constructor(options, verify) {
//...
this._options = options
// ...
}
authenticate(req, options) {
// ...
let _options = {
...options,
...this._options
}
// ...
}
}
#Injectable()
export class GithubBarStrategy extends PassportStrategy(StrategyFoo, 'github') {
constructor(private readonly configService: ConfigService) {
super({
passReqToCallback: true,
callbackURL: this.configService.get('OAuth.github.callbackURL'),
// ...
})
}
// ...
}

Mock Injected Twilio Service in Unit Testing Nest.js

I have a problem with overriding provider/setup module testing in nest.js application for testing.
Module file: smsModule.ts:
import { TwilioService } from './twilio/twilio.service';
import { DynamicModule, Module } from '#nestjs/common';
import { TwilioConfig, SMS_TWILIO_CONFIG } from './twilio.config';
import { TwilioClientCustom } from './twilio/twilio-client-custom';
#Module({
imports: [],
providers: [TwilioService],
})
export class SmsModule {
static register(options: TwilioConfig): DynamicModule {
return {
module: SmsModule,
imports: [HttpModule],
providers: [
{
provide: SMS_TWILIO_CONFIG,
useValue: options,
},
TwilioService,
TwilioClientCustom,
],
exports: [TwilioService],
};
}
}
Twilio client, config files:
//client
import { TwilioConfig, SMS_TWILIO_CONFIG } from '../twilio.config';
import { Twilio } from 'twilio';
import { Inject, Injectable } from '#nestjs/common';
#Injectable()
export class TwilioClientCustom extends Twilio {
constructor(#Inject(SMS_TWILIO_CONFIG) twilioConfig: TwilioConfig) {
super(twilioConfig.accountSid, twilioConfig.authToken);
}
}
//config
import { IsString, IsNotEmpty, NotContains, IsOptional, IsArray } from 'class-validator';
const INFO = 'Must be ....';
export class TwilioConfig {
#IsString()
#IsNotEmpty()
#NotContains('OVERRIDE_WITH_', { message: INFO })
accountSid: string;
#IsString()
#IsNotEmpty()
authToken: string;
#IsArray()
#IsOptional()
#IsNotEmpty('OVERRIDE_WITH_', { message: INFO })
serviceSid: string;
}
export const SMS_TWILIO_CONFIG = 'smsTwilioConfig';
Twilio service file: twilio.service.tst:
import { HttpService } from '#nestjs/axios';
import { TwilioConfig, SMS_TWILIO_CONFIG } from '../twilio.config';
import { SendSmsTwilioService } from './../sendsms.service';
import { Inject, Injectable } from '#nestjs/common';
import { TwilioClientCustom } from './twilio-client-custom';
#Injectable()
export class TwilioService implements SendSmsTwilioService {
constructor(
#Inject(SMS_TWILIO_CONFIG) private readonly config: TwilioConfig,
private readonly client: TwilioClientCustom,
private readonly httpService: HttpService
) {}
async sendSMS(to: string, from: string, body: string): Promise<string> {
......
return this.client.messages
.create({
to, //Recipinet's number
from, //Twilio number
body, //Messages to Recipient
})
.then((message) => message.sid)
.catch(() => {
throw new Error('TWILIO accountSid or authToken not valid');
});
}
I would like to test my service:
test file:
import { Test, TestingModule } from '#nestjs/testing';
//import { TWILIO_CONFIG_SPEC } from './test.config';
import { TwilioClientCustom } from '../src/twilio/twilio-client-custom';
import { HttpService } from '#nestjs/axios';
import { TwilioConfig } from './../src/twilio.config';
import { TwilioService } from './../src/twilio/twilio.service';
import nock from 'nock';
describe('TwilioService', () => {
let service: TwilioService;
let client: TwilioClientCustom;
let httpService: HttpService;
afterEach(() => {
nock.cleanAll();
});
//const smsServiceMock = {};
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
TwilioService,
{
provide: HttpService,
useValue: {
method1: jest.fn(),
method2: jest.fn(),
method3: jest.fn(),
},
},
TwilioService,
],
imports: [
NestConfigModule.forRoot({
config: TwilioConfig,
} as Record<string, unknown>),
],
}).compile();
//getting service module from main module
httpService = moduleRef.get<HttpService>(HttpService);
client = moduleRef.get<TwilioClientCustom>(TwilioClientCustom);
service = moduleRef.get<TwilioService>(TwilioService);
});
//check service is avaible
it('Should be defined', () => {
expect(client).toBeDefined();
expect(service).toBeDefined();
expect(httpService).toBeDefined();
});
And after running test I get following errors:
Nest can't resolve dependencies of the TwilioService (?, TwilioClientCustom, HttpService). Please make sure that the argument smsTwilioConfig at index [0] is available in the RootTestModule context.
Potential solutions:
- If smsTwilioConfig is a provider, is it part of the current RootTestModule?
- If smsTwilioConfig is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing smsTwilioConfig */ ]
})
How can I solve this problem ?
smsTwilioConfig is registered with Nest's IOC via SmsModule.register(opts).
However, it seems you're attempting to test TwilioService directly with createTestingModule. Which is fine, but it does mean you need include the config with a provider or import in your test.
My guess is that you thought NestConfigModule... would do that, but that's not setting the config at the right level.
I would think the following is the right direction
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
TwilioService,
{
provide: HttpService,
useValue: {
method1: jest.fn(),
method2: jest.fn(),
method3: jest.fn(),
},
},
{
// added this
provide: SMS_TWILIO_CONFIG,
useValue: testConfig
},
],
imports: [
// removed NestConfigModule
],
}).compile();

Nest js. Using firstValueFrom for send in ClientKafka not async

I am using #nestjs/microservices for work with kafka in microservices. And ClientKafka one of them. The method "send" returning Observable, but I wanna Promise, so I used rxjs/firstValueFrom to get Promise from Observable. But when I await it, I don't get async.
import { HttpException, Inject, Injectable } from "#nestjs/common";
import { ClientKafka } from "#nestjs/microservices";
import { firstValueFrom } from "rxjs";
import { AssetGetOneDto } from "./dto/asset.get.one.dto";
import { AssetTransferDto } from "./dto/asset.transfer.dto";
#Injectable()
export class AssetService {
constructor(
#Inject("ASSET_SERVICE")
protected readonly assetClient: ClientKafka
){}
async onModuleInit() {
this.assetClient.subscribeToResponseOf('asset.get.one')
await this.assetClient.connect()
}
async onModuleDestroy() {
await this.assetClient.close();
}
async oneAsset(assetGetOneDto: AssetGetOneDto) {
const response = await firstValueFrom(this.assetClient.send('asset.get.one', assetGetOneDto))
console.log("response=",response)
if(response.status == 200){
return response.message;
}
return undefined;
}
}
Client creating:
import { Module } from "#nestjs/common";
import { ClientsModule, Transport } from "#nestjs/microservices";
import { getuid } from "process";
import { ConfigModule } from "src/config/config.moudle";
import { ConfigService } from "src/config/config.service";
import { AssetService } from "./asset.service";
#Module({
imports: [
ClientsModule.registerAsync([
{
name: "ASSET_SERVICE",
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
transport: Transport.KAFKA,
options: {
client: {
clientId: 'asset',
brokers: [configService.getBroker()]
},
consumer: {
groupId: 'asset-consumer-'+process.pid,
},
}
}),
}
])
],
providers: [AssetService],
exports: [AssetService]
})
export class AssetModule {}

nestjs isn't injecting the typeorm connection in a custom validator

I am in the process of working out a custom validator to be used to verify if a value exists in the database
I managed to get the code to work by using getConnection() from the typeorm package. But I would ideally want to inject the typeorm connection object constructor(private connection: Connection) {} this gives me more flexibility with the testing.
import { BadRequestException, Injectable } from '#nestjs/common';
import {
ValidationArguments,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
import { Connection, getConnection } from 'typeorm';
#ValidatorConstraint({ name: 'ExistsRule', async: true })
#Injectable()
export class ExistsRule implements ValidatorConstraintInterface {
constructor(private connection: Connection) {}
async validate(value: number | string, args: ValidationArguments) {
const { constraints } = args;
if (constraints.length === 0) {
throw new BadRequestException(`Failed validating ${value} exists.`);
}
const str = constraints[0].split(':');
const tableName = str[0];
const columnName = str[1];
const result = await getConnection().query(
`select count(*) from ${tableName} where ${columnName}=$1`,
[value],
);
// The record already exists. Failing the validation.
if (result[0].count > 0) {
return false;
}
return true;
}
defaultMessage(args: ValidationArguments) {
const { property, value } = args;
return `${property} ${value} is already taken.`;
}
}
DTO that is using this validator. (For completeness)
import {
IsEmail,
IsNotEmpty,
IsPhoneNumber,
IsString,
Validate,
} from 'class-validator';
import { ExistsRule } from 'src/customer/exists.validator';
export class CreateCustomerDto {
#IsString()
firstName: string;
#IsString()
lastName: string;
#IsEmail()
#Validate(ExistsRule, ['customer:email'])
email: string;
#IsPhoneNumber()
mobileNumber: string;
#IsNotEmpty()
password: string;
}
#Module({
imports: [
TypeOrmModule.forFeature([Customer]),
JwtModule.register({
secret: 'secret-to-be-put-in-the-env',
signOptions: { expiresIn: '300s' },
}),
CommonModule,
],
controllers: [BookingsController, AuthController],
providers: [CustomerService, AuthService, LocalStrategy, JwtStrategy],
})
export class CustomerModule {}
import { Module } from '#nestjs/common';
import { UniqueRule } from './unique.validator';
#Module({
providers: [UniqueRule],
})
export class CommonModule {}
Thanks to #JayMcDoniel's comments, and guidence. The solution was to edit main.ts and add the useContainer from class-validator module to instruct it to use the nestjs container.
import { ValidationPipe } from '#nestjs/common';
import { NestFactory } from '#nestjs/core';
import { useContainer } from 'class-validator';
import { AppModule } from './app.module';
import { CommonModule } from './common/common.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
}),
);
useContainer(app.select(CommonModule), { fallbackOnErrors: true });
await app.listen(3000);
}
bootstrap();

Resources