NestJs Testing with CACHE_MANAGER injected - javascript

I have some service in which I inject the CACHE_MANAGER in the constructor
import { CACHE_MANAGER, Inject, Injectable } from '#nestjs/common';
import { Cache } from 'cache-manager';
...
export class ManagerService {
constructor(#Inject(CACHE_MANAGER) private cacheManager: Cache) {}
...
}
That gives me an error when I test the modules the import those service
Nest can't resolve dependencies of the ManagerService (?). Please make sure that the argument CACHE_MANAGER at index [0] is available in the Web3ManagerService context.
I'm relatively new to NestJs so I really cannot figure out how to solve it

To inject the cache manager provider under the CACHE_MANAGER, you need to import the nestjs module that creates this provider into the module that has the ManagerService
#Module({
imports: [CacheModule.register()], // <<<<
providers: [ManagerService],
})
export class AppModule {}
like the docs shows https://docs.nestjs.com/techniques/caching

I just had the same problem and resolved it this way:
manager.service.spec.ts
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ManagerService, { provide: CACHE_MANAGER, useValue: {} }],
}).compile();
service = module.get<ManagerService>(ManagerService);
});

Set isGlobal: true:
CacheModule.register({
url: process.env.REDIS_URL,
db: 0,
port: parseInt(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD,
isGlobal: 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

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'
}
}]
})

Keycloak - how to disable sharing session between two (Angular) client apps?

I have two angular client applications that use Keycloak's angular client for authentication. The problem is, that when i log in on one of them, i'm automatically being logged to another one. I have separate client for each one though.
Keycloak service initialization looks like this in both apps:
#NgModule({
declarations: [AppComponent],
imports: [
AppRoutingModule,
BrowserModule,
KeycloakAngularModule
],
providers: [KeycloakService],
entryComponents: [AppComponent]
})
export class AppModule implements DoBootstrap {
constructor(private keycloakService: KeycloakService) {
}
ngDoBootstrap(app: ApplicationRef) {
this.keycloakService
.init({config: environment.keycloak, initOptions: {onLoad: 'login-required'}})
.then(() => app.bootstrap(AppComponent));
}
}
Keycloak config for the first app:
const keycloakConfig: KeycloakConfig = {
realm: 'quick',
url: 'http://localhost:8000/auth',
clientId: 'quick-ui-customer'
};
export const environment = {
production: false,
keycloak: keycloakConfig,
};
And for the second one:
const keycloakConfig: KeycloakConfig = {
realm: 'quick',
url: 'http://localhost:8000/auth',
clientId: 'quick-ui-employee'
};
export const environment = {
production: false,
keycloak: keycloakConfig,
};
That is a core feature of Single Sign On protocol. But if you don't want to share session, then create clients in different realms.

How can I use interceptor in neo4j-graphql-js endpoint with nestjs framework?

I am using the neo4j-graphql-js library to translate graphql queries to cypher and I need to implement an interceptor to verify that what is returning belongs to the user who is asking for it. For this I need to implement the interceptor but the problem I have is that I have no resolvers since it generates the liberia. How can I make it go through the interceptor? If the interceptor cannot be used, is there a way to implement a middleware in the response?
I'm using nestjs framework. And I use a neo4j database.
Thank you.
Module:
#Module({
imports: [
GraphQLModule.forRootAsync({
useClass: GraphqlConfigService,
}),
],
providers: [neo4jProvider],
})
export class GraphqlModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(GraphQLAuthMiddleware,GraphQLRoleMiddleware)
.forRoutes('graphql');
}
}
#Injectable()
export class GraphqlConfigService implements GqlOptionsFactory {
async createGqlOptions(): Promise<GqlModuleOptions> {
const schema = buildSchema();
return {
playground: true,
schema: schema,
path: '/graphql/queries',
context: {
driver: neo4j.driver(
'bolt://neo4j_db:7687',
neo4j.auth.basic('neo4j', 'root')
)
}
};
}
}
function buildSchema(): GraphQLSchema {
return makeAugmentedSchema({
typeDefs,
config: {
query: true,
mutation: true
}
});
}
You can bind your interceptor globally either by using the app.useGlobalInterceptors(MyCustomInterceptor) method in your main.ts or in any module you can add the interceptor in your providers array
#Module({
imports: [/* your imports*/],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: MyCustomInterceptor
},
/* the rest of your providers*/
],
})
export class GraphqlModule {}
APP_INTERCEPTOR is imported from #nestjs/core. Keep in mind, this does bind the interceptor globally. All requests to your server will go through this interceptor.

Uncaught Error: Can't resolve all parameters for AppComponent: (?)

When adding the formbuilder in the constructor. I've been getting the error. I've added the ReactiveFormsModule in the app.module already.
import { Component } from '#angular/core';
import { FormBuilder, FormGroup, Validators, FormControl } from '#angular/forms';
#Component({
selector: 'app',
template: require('./app.component.pug'),
styles: [require('./app.component.scss')]
})
export class AppComponent {
private loginForm: FormGroup;
constructor(private fb: FormBuilder) {}
}
Sample app.module.ts
import { ReactiveFormsModule } from '#angular/forms';
#NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [AppComponent],
providers: [],
bootstrap: [AppComponent]
})
Here is the error:
Added emitDecoratorMetadata: true to tsconfig.json file
This works for me in a Stackblitz example.
Some comments though:
Why do you require the pug file as a template? If you are using pug (and pug-watch), the html file should be there as well.
why do you require the css? Just use simply styleUrls: [ './app.component.css' ] .
Are you sure your node, npm and angular version is up to date and compatible?
Edit:
If removing the 'require' parts helps, try this:
#Component({
selector: 'app',
template: './app.component.html',
styles: ['./app.component.scss']
})
There is no native pug support in angular yet, so you should rely on other tools to translate your page to a html file. In the #Component decorator you should try to stick to the official angular documentation.
Personally, I use Gulp js for starting my dev app and a pug watcher at the same time.
Here is my gulpfile:
const gulp = require('gulp');
const clean = require('gulp-clean');
const pug = require('gulp-pug');
const git = require('gulp-git');
const file = require('gulp-file');
const spawn = require('child_process').spawn;
const shell = require('gulp-shell');
const install = require('gulp-install');
const notify = require('gulp-notify');
// *********
// BUILD APP
// *********
gulp.task('npm-install', function () {
gulp.src(['./package.json'])
.pipe(install());
});
gulp.task('clean-dist', ['npm-install'], function () {
return gulp.src('./dist', {read: false})
.pipe(clean())
});
gulp.task('pug-build', function buildHTML() {
return gulp.src('./src/**/*.pug')
.pipe(pug({doctype: 'html', pretty: false}))
.on('error', notify.onError(function (error) {
return 'An error occurred while compiling pug.\nLook in the console for details.\n' + error;
}))
.pipe(gulp.dest('./src'))
});
gulp.task('ng-build', ['clean-dist', 'pug-build'], function (cb) {
spawn('npm', ['run', 'ngbuild'], {stdio: 'inherit'})
});
gulp.task('build', ['ng-build'], function () {
});
// ****************
// DEVELOPMENT MODE
// ****************
gulp.task('pug-watch', ['pug-build'], function (cb) {
gulp.watch('./src/**/*.pug', ['pug-build'])
});
gulp.task('ng-serve', function (cb) {
spawn('ng', ['serve'], {stdio: 'inherit'});
});
gulp.task('dev-start', ['pug-watch', 'ng-serve']);
(in the package.json I have an entry:"ngbuild": "ng build --aot --progress=false" )

Categories

Resources