LearningComponent > should create
TypeError: this.translate.addLangs is not a function
Getting above error of my custom component(LearningComponent) while mocking up the Translate Service
If you need addLangs method, then you need to add it to your Mock. For example:
class MockTranslateService {
addLangs(langs: Array<string>): void {
}
}
In your providers array:
providers: [
{provide: TranslateService, useClass: MockTranslateService}
]
Related
I'm facing the following issue. I have a service used by a controller. The service (in the snippets below QueueService) injects a provider imported from a package. I aim to reuse the QueueService across the controller methods, but I also need to dynamically specify which provider QueueService should use.
My question is, how can I achieve this behaviour?
import { PubsubService } from '#myorg/queue'
#Module({
imports: [
ConfigModule.forRoot({
SHARED_RESOURCES_PROJECT_ID: Joi.string().required()
})
})
],
controllers: [AppController],
providers: [
{
provide: 'PUBSUB',
useValue: new PubsubService()
},
{
provide: 'INTEGRATION_PUBSUB',
useValue: new PubsubService({ projectId: process.env.SHARED_RESOURCES_PROJECT_ID })
}
]
})
export class AppModule {}
#Controller()
export class AppController {
constructor(private queueService: QueueService) {}
#Post()
async create() {
...
// here I want to use queueService with `PUBSUB` injected
return this.queueService.sendMessage(...)
}
#Patch()
async update() {
...
// here I want to use queueService with `INTEGRATION_PUBSUB` injected
return this.queueService.sendMessage(...)
}
}
#Injectable()
export class QueueService {
constructor(
// how can I dynamically change `#Inject('PUBSUB')` to `#Inject('INTEGRATION_PUBSUB')`?
#Inject('PUBSUB') private readonly pubsubService: PubsubService
) {}
async sendMessage(payload): Promise<void> {
return this.pubsubService.sendMessage(payload)
}
}
dynamic inject is not possible after object(in this case controller) created . so you have two option
1- create two QueueService (one for PUBSUB and another for INTEGRATION_PUBSUB) and inject both to controller. use those in your controller functions. (i recommend this)
2- inject both PUBSUB and INTEGRATION_PUBSUB into QueueService and pass another param in sendMessage function . so check this param to choose between PUBSUB and INTEGRATION_PUBSUB
My implementation is based on this article: https://dev.to/nestjs/advanced-nestjs-how-to-build-completely-dynamic-nestjs-modules-1370
I want to test my generic, Twilio-based SMS sender service that I share between multiple parts of my application. I want to configure it when I'm importing it from somewhere else, so I'm writing it as a dynamic module. On top of that, the options that I pass to the dynamic module are themselves constructed dynamically, they are read from my .env file. I'm using the factory pattern when I'm registering my provider:
// app.module.ts
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: [
'.env',
],
validationSchema,
}),
SharedSmsModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService<EnvironmentVariables>) => {
return {
accountSid: configService.get('TWILIO_ACCOUNT_SID'),
authToken: configService.get('TWILIO_AUTH_TOKEN'),
smsSenderPhoneNumber: configService.get(
'TWILIO_SMS_SENDER_PHONE_NUMBER'
),
};
},
}),
],
})
export class AppModule {}
My shared-sms module calls the function provided in the registerAsync method in app.module.ts:
// shared-sms.module.ts
export interface SharedSmsModuleOptions {
accountSid: string;
authToken: string;
smsSenderPhoneNumber: string;
}
export interface SharedSmsModuleAsyncOptions extends ModuleMetadata {
imports: any[];
inject: any[];
useFactory?: (
...args: any[]
) => Promise<SharedSmsModuleOptions> | SharedSmsModuleOptions;
}
#Module({})
export class SharedSmsModule {
static registerAsync(
sharedSmsModuleAsyncOptions: SharedSmsModuleAsyncOptions
): DynamicModule {
return {
global: true,
module: SharedSmsModule,
imports: sharedSmsModuleAsyncOptions.imports,
providers: [
{
provide: 'SHARED_SMS_OPTIONS',
useFactory: sharedSmsModuleAsyncOptions.useFactory,
inject: sharedSmsModuleAsyncOptions.inject || [],
},
SharedSmsService,
],
exports: [SharedSmsService],
};
}
}
Now I have access to the options variables in my shared-sms.service:
// shared-sms.service
#Injectable()
export class SharedSmsService {
private twilioClient: Twilio;
constructor(
#Inject('SHARED_SMS_OPTIONS') private options: SharedSmsModuleOptions
) {
this.twilioClient = new Twilio(
this.options.accountSid,
this.options.authToken
);
}
async sendSms(sendSmsDto: SendSmsDto): Promise<MessageInstance> {
await validateOrReject(plainToInstance(SendSmsDto, sendSmsDto));
const smsData = {
from: this.options.smsSenderPhoneNumber,
to: sendSmsDto.to,
body: sendSmsDto.body,
};
return await this.twilioClient.messages.create(smsData);
}
}
So long everything seems to be working. But I'm having issues when I'm trying to test the service's sendSms function. I can write tests that work when I'm providing hardcoded Twilio test account values in my test file. But I don't want to commit them to the repository, so I would want to get them from my .env file. I have tried providing everything to the Test.createTestingModule function when I'm creating my moduleRef, based on what I did in the code that I already wrote, but I couldn't specify the Twilio test account values dynamically. As I don't see documentation regarding this issue, I feel like that I'm either missing a conceptual point (providing so many things in the test seems like an overkill) or there is a trivial work-around. Please help me figure out how to pass those values to my tests from my .env file
I am have an issue with this package #ntegral/nestjs-sentry in nestjs. I have a custom logger I use in my application
#Injectable()
export class CustomLogger implements LoggerService {
constructor(#InjectSentry() private readonly client: SentryService) {}
log(message: any, ...optionalParams: any[]) {
this.client.instance().captureMessage(message, ...optionalParams);
}
}
I then inject the into User Controller and in the user.controller.spec.ts
describe('UsersController', () => {
let controller: UsersController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [
CustomLogger,
UsersService,
SentryService,
],
}).compile();
controller = module.get<UsersController>(UsersController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
I get this error
FAIL src/users/users.controller.spec.ts (9.449 s)
● UsersController › should be defined
Nest can't resolve dependencies of the CustomLogger (?). Please make sure that the argument Symbol(SentryToken) at index [0] is available in the RootTestModule context.
Potential solutions:
- If Symbol(SentryToken) is a provider, is it part of the current RootTestModule?
- If Symbol(SentryToken) is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing Symbol(SentryToken) */ ]
})
I have tried adding the SentryService to the spec providers but that does not fix the error. Has anyone encountered this and how did you fix it.
I run in exactly the same issue. It seemed to be that the library uses a different token for its own Inject annotation. I was able to fix it in my tests by using the provided token for the SentryService mock.
import { SENTRY_TOKEN } from '#ntegral/nestjs-sentry';
// ...
const module: TestingModule = await Test.createTestingModule({
providers: [
// ...
{
provide: SENTRY_TOKEN,
useValue: { debug: jest.fn() }, // provide SentryService Mock here
},
],
})
I need to add an additional url parameter (which is dynamic and not fixed) to the fields calls that happen on the PDP page. I have tried extending the the product service but that doesnt fire any of my overide functions.
I have now ended up implementing the product adapter so I just want to confirm this is 100% correct.
export class MyProductAdapter implements ProductAdapter {
if you want to add a (fixed) value in your fields call you can override the default call and add your missing value. Create a file yourOccProductDetails.config.ts
export const yourOccProductDetailsConfig: OccConfig = {
backend: {
occ: {
endpoints: {
product: {
details: 'products/${productCode}?fields=averageRating,stock(DEFAULT),description,availableForPickup,code,url,price(DEFAULT),numberOfReviews,manufacturer,categories(FULL),priceRange,multidimensional,tags,images(FULL),yourParam',
},
},
},
},
},
And in your module add the config to your providers array
import { yourOccProductDetailsConfig } from './yourOccProductDetails.config'
#NgModule({
imports: [...],
declarations: [YourProductDetailsComponent],
exports: [YourProductDetailsComponent],
providers: [provideConfig(yourOccProductDetailsConfig)],
})
export class YourProductDetailsModule {}
Here is the documentation part for a problem you have faced:
https://sap.github.io/spartacus-docs/connecting-to-other-systems/#configuring-endpoints
I could use a little help.
Context I am making a angular 5 module for an authentication service.
How do I pass a POJO into a class as parameters?
See my answer below.
I need to use HttpClient inside this auth service
I am getting this error:
Error: Can't resolve all parameters for AuthService: ([object Object], ?).
[object Object] is angular 5 HttpClient
Can some one please explain why I am getting this error and how to resolve it?
#NgModule()
export class AuthtModule {
static forRoot(params?: iParams) {
return {
ngModule: AuthModule,
provides: [
HttpClient,
{
provide: AuthService,
useFactory: setupAuthService,
deps: [ HttpClient, params ]
}
],
imports: [ HttpClientModule ],
exports: [ AuthService ]
}
}
}
Thanks in advance.
Cheers
I figured out how to do this, as it turns out the problem was with the second parameter (hence the question mark) not the first. the reason this error occurs is because in order for parameters to be passed into a class they must first be turned into a injectable.
Here is how you do it.
First create a class model with the params
foo-params.model.ts
export class FooParams {
public foo1: string;
public foo2: number;
}
Then In the Module class attributes set the class FooParams to use the values that are a POJO
app.module.ts
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { FooParams } from './foo-params';
#NgModule({
imports : [ HttpClientModule ],
providers: [{
HttpClient
{ provider: FooParams, useValue: params },
{
provider: BarService,
useFactory: setupBarService
deps: [ HttpClient, FooParams ]
}
}]
})
export class AppModule {}
And this is what the class that consumes the pojo would look like.
bar.service.ts
import { HttpClient } from '#angular/common/http';
import { FooParams } from './foo.service.ts';
expecto function setupBarService(http: HttpClient, params: FooParams) {
return new BarService(http, params);
}
#Injectable()
export class BarService {
constructor(http: HttpClient, params: FooParams) {}
//DO STUFF
}
Try by importing import 'core-js/es7/reflect'; in polyfills.ts