APP_INITIALIZER for lazy loaded module - javascript

I am developing a module that suppport forRoot and forChild. Whenever this module is imported with forRoot, forChild some methods of a custom service should be called. It works well for forRoot since I use APP_INITIALIZER, but the factory is not called anymore in the forChild of a lazy loaded module. How can I execute the methods that I have in my factory in the forChild so that it also works with lazy loaded modules? Is there some sort of equivelant of the APP_INITIALIZER for lazy loaded modules or a workaround?
export const TRANSLATION_CONFIGURATION = new InjectionToken<TranslationConfiguration<any>>('');
function initializeAppFactory(config: TranslationConfiguration<any> & { language: Language }, locutus: LocutusService): () => void {
return () => {
if (config.language) {
locutus.registerRoot(config);
return;
}
locutus.registerChild({
scope: config.scope,
loaders: config.loaders
});
};
}
#NgModule({
declarations: [
NgxLocutusComponent,
LocutusDirective,
LocutussPipe
],
imports: [
CommonModule
],
exports: [
NgxLocutusComponent,
LocutusDirective,
LocutussPipe
]
})
export class LocutusModule {
static forRoot<T>(config: TranslationConfiguration<T> & { language: Language }): ModuleWithProviders<LocutusModule> {
return {
ngModule: LocutusModule,
providers: [
{
provide: TRANSLATION_CONFIGURATION,
useValue: config,
},
{
provide: APP_INITIALIZER,
useFactory: initializeAppFactory,
deps: [
TRANSLATION_CONFIGURATION,
LocutusService
],
multi: true
},
]
};
}
static forChild<T>(config: TranslationConfiguration<T>): ModuleWithProviders<LocutusModule> {
return {
ngModule: LocutusModule,
providers: [
{
provide: TRANSLATION_CONFIGURATION,
useValue: config,
},
{
provide: APP_INITIALIZER,
useFactory: initializeAppFactory,
deps: [
TRANSLATION_CONFIGURATION,
LocutusService
],
multi: true
},
]
};
}
}

Related

Cannot test e2e because Nestjs doesn't resolve dependencies

I have the following error :
Nest can't resolve dependencies of the ParametrageRepository (?). Please make sure that the argument DataSource at index [0] is available in the TypeOrmModule context.
My test code :
describe("ParametrageController (e2e)", () => {
let parametrage: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [ParametrageModule],
}).compile();
parametrage = moduleFixture.createNestApplication();
await parametrage.init();
});
it("/ (POST)", () =>
request(parametrage.getHttpServer())
.post("/parametrage")
.send({
libelle: "GROUPE_TYPE",
liste: ["TEAM", "SERVICE", "ORGANISATION"],
})
.expect(201));
});
My module code:
#Module({
imports: [TypeOrmModule.forFeature([Parametrage])],
exports: [TypeOrmModule],
controllers: [ParametrageController],
providers: [ParametrageService, ParametrageRepository, ParametrageActions, Logger],
})
export class ParametrageModule {}
I cannot tel why I have this error since I followed the Nestjs documentation. Maybe I skipped a part I don't know. Maybe it's because of my providers that has parameters in their constructors :
My ParametrageRepository provider :
#Injectable()
export class ParametrageRepository
extends RepositoryStarter<Parametrage, IParametrageListFilter>
implements IParametrageRepository
{
constructor(#InjectDataSource() datasource: DataSource) {
super(datasource.getRepository(Parametrage));
}
I tried to add providers injection :
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [ParametrageModule],
** providers: [
{ provide: ParametrageActions, useValue: ParametrageActions },
{ provide: ParametrageRepository, useValue: ParametrageRepository },
{ provide: Logger, useValue: Logger ,
],**
}).compile();
But stil the same error, it didn't solved anything
Thanks in advance

Nest.js can't resolve the dependencies of CacheConfigService

I want to use the CacheModule in the AuthModule and I have written this code but still getting the error in the console:
The Auth.module.ts file in which I want to import the cache module:
#Module({
providers: [CacheConfigService, SupertokensService],
exports: [],
imports: [CacheConfigModule, UsersModule],
controllers: [],
})
export class AuthModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes('*');
}
static forRoot({
connectionURI,
apiKey,
appInfo,
}: AuthModuleConfig): DynamicModule {
return {
// providers and exports
imports: [CacheConfigModule],
};
}
}
The cache config.module.ts file code. The cache config.service.ts file contains the logic:
#Module({
providers: [CacheConfigService],
exports: [CacheConfigService],
imports: [
CacheModule.register<RedisClientOptions>({
isGlobal: true,
// store: redisStore,
url: 'redis://' + process.env.REDIS_HOST + ':' + process.env.REDIS_PORT,
}),
],
})
export class CacheConfigModule {}
I want to use the cache service in the following class:
#Injectable()
export class SupertokensService {
private redisClient = redis.createClient({
url: this.cacheConfigService.url,
});
constructor(
#Inject(forwardRef(() => UsersService)) private userService: UsersService,
private cacheConfigService: CacheConfigService
) {
supertokens.init({
appInfo: this.config.appInfo,
supertokens: {
connectionURI: this.config.connectionURI,
apiKey: this.config.apiKey,
},
recipeList: [
ThirdPartyEmailPassword.init({
providers: [
ThirdPartyEmailPassword.Google({
clientSecret: 'TODO: GOOGLE_CLIENT_SECRET',
clientId:
'CLIENT_ID',
}),
],
signUpFeature: {
...signup logic
},
override: {
apis: (originalImplementation: any) => {
return {
...originalImplementation,
emailPasswordSignInPOST: async (input: any) => {
if (
originalImplementation.emailPasswordSignInPOST === undefined
) {
throw Error('Should never come here');
}
let response: any =
await originalImplementation.emailPasswordSignInPOST(input);
// retrieving the input from body logic
const { email } = inputObject;
const user = await this.userService.findOneByEmail(email);
const id = user?.id;
const token = jwt.sign({email, id}, 'mysecret';, {
expiresIn: '2h',
});
response.token = token;
await this.redisClient.set("Token", token, {
EX: 60 * 60 * 24,
});
return response;
},
};
},
},
}),
],
});
}
}
The error has been thrown because you have used ConfigService in CacheConfigService, but it has never been imported into CacheConfigModule.
If you want to have ConfigService then you must import it into AppModule, as well as CacheConfigModule.
app.module.ts:
import { ConfigModule } from '#nestjs/config';
import configuration from './config/configuration'; // or wherever your config file exists
#Module({
imports: [
ConfigModule.forRoot({
load: [configuration],
}),
// rest of code
],
// rest of code
})
export class AppModule {}
config.module.ts:
import { ConfigModule } from '#nestjs/config';
#Module({
providers: [CacheConfigService],
exports: [CacheConfigService],
imports: [
CacheModule.register<RedisClientOptions>({
isGlobal: true,
// store: redisStore,
url: 'redis://' + process.env.REDIS_HOST + ':' + process.env.REDIS_PORT,
}),
ConfigModule,
],
})
export class CacheConfigModule {}

How to pass constructor arguments, to a NestJS provider?

I have some Service, which requires a config object literal in its constructor, something like this:
#Injectable()
export class BatteriesService {
constructor(private config: Config) { }//The provider needs a config object
}
If I simply add this class in the providers array of the module, I obviously get an error, being that a constructor argument is missing.
So, instead of just referencing the BatteriesService class, I need to somehow create an instance. I tried this:
#Module({
controllers: [BatteriesController],
providers: [{
useFactory: ()=>{
return new BatteriesService({'someProp': 'someValue'})
},
provide:'BatteriesService'
}]
})
And this:
#Module({
controllers: [BatteriesController],
providers: [{
useValue:new BatteriesService({'someProp': 'someValue'}),
provide:'BatteriesService'
}]
})
In both cases I get the following error:
Error: Nest can't resolve dependencies of the BatteriesController (?).
Please make sure that the argument BatteriesService at index [0] is
available in the BatteriesModule context.
How can this done, without "resorting" to bypassing the DI system, or creating another "inner" provider(config)?
When you need to have DI in service that has to be defined in the module.
In your case
#Injectable()
export class BatteriesService {
constructor(private config: ConfigService) { }
}
#Module({
imports: [ConfigModule.forRoot({})], // Configure it as needed
providers: [BatteriesService]
})
Your mistake is that you don't actually import ConfigModule while your service is dependent on it.
If you wish to use useFactory method then it would look like
#Module({
providers: [{
useFactory: (config: ConfigService) => {
return new BatteriesService(config);
},
provide: BatteriesService,
inject: [ConfigService]
}]
})
I assumed your Config is actually Nest ConfigModule.
But if it's some custom Module you still need to import it as in the above examples.
If you want to pass an object literal as a config try this
interface MyConfigType = {
something: string;
}
#Injectable()
export class BatteriesService {
constructor(#Inject('CONFIG') private config: MyConfigType) { }
}
#Module({
providers: [{
provide: 'CONFIG',
useValue: {
something: 'my-value'
}
}]
})

Angular 4, Error: Can't resolve all parameters for StateObservable: (?)

i am writing unit test on one of my service. I am keep getting Error: Can't resolve all parameters for StateObservable: (?). and my test is failing.
can anyone tell me what is the issue?
spec file code
fdescribe("Physician Service", () => {
let physicianService: PhysicianService;
let httpTestingController: HttpTestingController;
let backend: MockBackend;
let initialResponse: any;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule
],
providers: [
Http,
ConnectionBackend,
UtilService,
StateObservable,
RouterModule,
SessionService,
HttpClientTestingModule,
BaseRequestOptions,
MockBackend,
PhysicianService,
AuthService,
BrowserXhr,
XHRBackend,
Store,
{
deps: [
MockBackend,
BaseRequestOptions
],
provide: Http,
useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
],
schemas:[
NO_ERRORS_SCHEMA
]
});
});
fit("Should Create Physicians Service",
inject([PhysicianService], (physicianService: PhysicianService) => {
expect(physicianService).toBeTruthy();
}));
});
can anyone help me in this regard
Thanks
This should have an actual answer instead of being in the comments.
In the TestBed.configureTestingModule imports add StoreModule.forRoot({})
TestBed.configureTestingModule({
imports: [
...
StoreModule.forRoot({})
],
providers: [
...
]
});

Override environment variables in jasmine test

I have a Angular service that looks something like this:
import {environment} from '../environment'
....
public something() {
if(environment.production)
{
// do stuf
} else {
// do something else
}
}
Now i want to test both cases (dev and prod environemnt). How do I "mock" when the environment is imported ?
So i came up with a solution where I didn't have to check for the environment in my service:
I used the useFactory:
NgModule({
declarations: [],
imports: [],
providers: [
{
provide: Service,
useFactory: (httpClient: HttpClient) => {
if (environment.production) {
return new MyCustomService(httpClient);
} else {
return new Service();
}
},
deps: [HttpClient],
}
],
bootstrap: [AppComponent]
})
This way i manage which service is provided when the application is started up

Categories

Resources