2
I have a subscriber for NestJS to listen to any create, update or delete events (TypeORM). When one of these events is fired, I'd like to use an injected service in order to create a new revision entry.
However, it seems I cannot get the dependency . I getting error Nest can't resolve dependencies of the JobInterviewerSubscriber (?). Please make sure that the argument Connection at index [0] is available in the JobPostingModule context
Keys Files:
Jobs.entity.ts
JobPostingModule.ts
Jobs.entity.ts
import { BaseEntity, PrimaryGeneratedColumn, Column, Entity, CreateDateColumn, UpdateDateColumn, OneToMany, ManyToOne, ManyToMany, JoinTable, EventSubscriber, EntitySubscriberInterface, InsertEvent, Repository, UpdateEvent, Connection } from 'typeorm';
import { Panelist } from './panelist.entity';
import { JobStatus, JobLocation, JobType, JobPriority } from '../job-posting/enum/job.enum';
import { Agency } from './agency.entity';
import { Candidates } from '../entities/candidates.entity';
import { JobInterviewers } from './job-interviewers.entity';
import { User } from '../entities/user.entity';
import { Injectable } from '#nestjs/common';
import { InjectConnection } from '#nestjs/typeorm/dist/common/typeorm.decorators';
#Entity()
export class Jobs extends BaseEntity{
#PrimaryGeneratedColumn()
id: number;
#Column({type: 'varchar', length: 100})
title: string;
#Column({type: 'enum', enum: JobPriority})
priority: JobPriority;
#Column({type: 'int2', default: 0})
roundCount: number;
#OneToMany(type => JobInterviewers, jobInterviewers => jobInterviewers.jobs, {eager:true})
jobInterviewers: JobInterviewers[];
#ManyToMany(type => Agency, agency => agency.jobs)
#JoinTable({name: 'job_agency_table'})
agencies: Agency[];
#UpdateDateColumn()
updatedAt: Date;
}
#EventSubscriber()
#Injectable()
export class JobInterviewerSubscriber
implements EntitySubscriberInterface<JobInterviewers> {
constructor(
#InjectConnection() readonly connection: Connection,
) {
connection.subscribers.push(this);
}
listenTo() {
return JobInterviewers;
}
async afterInsert(event: InsertEvent<JobInterviewers>) {
const jobsRepo: Repository<Jobs> = event.connection.manager.getRepository<Jobs>('Jobs');
const jobInterviewersRepo: Repository<JobInterviewers> = event.connection.manager.getRepository<JobInterviewers>('JobInterviewers');
jobInterviewersRepo
.count({
where: { jobs: { id: event.entity.jobs.id } },
})
.then((count: number) => {
jobsRepo.update({ id: event.entity.jobs.id }, { roundCount: count });
});
}
}
JobPostingModule.ts
import { Module } from '#nestjs/common';
import { JobPostingController } from './job-posting.controller';
import { JobPostingService } from './job-posting.service';
import { AuthModule } from '../auth/auth.module';
import { RecruitmentService } from '../recruitment/recruitment.service';
import { JobInterviewerSubscriber } from 'src/entities/job-posting.entity';
#Module({
imports: [
AuthModule
],
controllers: [JobPostingController],
providers: [JobPostingService, RecruitmentService,JobInterviewerSubscriber]
})
export class JobPostingModule {}
Hmm, there any many different possibilities:
is the connection loaded globally?
is the connection properly configured at all? Does it give any errors of connection?
I see that your module does not import TypeOrmModule.forFeature([Jobs]). If your TypeORM configuration doesn't load entities automatically, you should load them as imports manually
Related
Nest-Keycloak-connect is protecting all DTOs, Public() Decorator not working when querying a housing. Getting always Unauthorized when i want to query public items.
import { ID, ObjectType } from "#nestjs/graphql";
import { FilterableField, Relation } from "#ptc-org/nestjs-query-graphql";
import { Public, Unprotected } from "nest-keycloak-connect";
import { PropertyDTO } from "../property/property.dto";
#Public()
#ObjectType("Housing")
#Relation("property",() => PropertyDTO, {disableRemove: true})
export class HousingDTO {
#FilterableField(() => ID)
id !: number
}
Housing Module
import { Module } from "#nestjs/common";
import { NestjsQueryGraphQLModule } from "#ptc-org/nestjs-query-graphql";
import { NestjsQueryTypeOrmModule } from "#ptc-org/nestjs-query-typeorm";
import { HousingDTO } from "./housing.dto";
import { HousingEntity } from "./housing.entity";
import { HousingInputDTO } from "./housing.input.dto";
#Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([HousingEntity])],
resolvers: [{
DTOClass: HousingDTO,
EntityClass: HousingEntity,
CreateDTOClass: HousingInputDTO,
}]
})
],
})
export class HousingModule {}
Keycloak configured and working when logged in. But i need also to query the housing when not logged in.
#Module({
imports: [
....
HousingModule,
....
KeycloakConnectModule.register({
authServerUrl: 'https://auth.xxx.com/auth/',
realm: 'xxx',
clientId: 'xxx',
secret: process.env.KEYCLOAK_SECRET,
policyEnforcement: PolicyEnforcementMode.PERMISSIVE, // optional
tokenValidation: TokenValidation.ONLINE, // optional
logLevels: ['warn', 'error'],
}),
],
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
{
provide: APP_GUARD,
useClass: ResourceGuard,
},
{
provide: APP_GUARD,
useClass: RoleGuard,
}
],
})
export class AppModule {}
It won't work, because you must use the Public decorator on the endpoints.
Simply put the decorator on one of your controller endpoints to achieve the unauthenticated access.
Example for that:
import { Resource, Roles, Scopes, Public, RoleMatchingMode } from 'nest-keycloak-connect';
import { Controller, Get, Delete, Put, Post, Param } from '#nestjs/common';
import { Product } from './product';
import { ProductService } from './product.service';
#Controller()
#Resource(Product.name)
export class ProductController {
constructor(private service: ProductService) {}
/**
* if you use decorator here, the endpoint will
* be accessible without authentication
**/
#Get()
#Public() // <-- Used here
async findAll() {
return await this.service.findAll();
}
}
You can read more here.
I am creating a small app using NestJS and TypeORM and I am having trouble inserting into a table that has a composite foreign key.
Whenever the insert is executed, all columns get filled, except for the foreign keys, which stay as null.
Here are my entities:
Employee
import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm';
#Entity({ name: 'employees' })
export class Employee {
#PrimaryGeneratedColumn('uuid')
id: string;
#PrimaryColumn()
version: number;
#Column({ name: 'employee_name', type: 'varchar' })
employeeName: string;
#Column({ name: 'employee_salary', type: 'numeric' })
employeeSalary: string;
}
Employee Payroll
import {
Column,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Employee } from '../../employee/entities/employee.entity';
#Entity({ name: 'employee_payrolls' })
export class EmployeePayroll {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ name: 'employee_payroll_name', nullable: true })
employeePayrollName: string;
#ManyToOne(() => Employee)
#JoinColumn([
{ name: 'employee_id', referencedColumnName: 'id' },
{ name: 'employee_version', referencedColumnName: 'version' },
])
employee: Employee;
}
Employee Payroll Service
And here is the code that is doing the insert:
export class EmployeePayrollDTO {
employeePayrollName: string;
employeeId: string;
employeeVersion: number;
}
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { EmployeePayrollDTO } from 'src/employee-payroll/dto/employee-payroll.dto';
import { EmployeePayroll } from 'src/employee-payroll/entities/employee-payroll.entity';
import { Repository } from 'typeorm';
#Injectable()
export class EmployeePayrollService {
constructor(
#InjectRepository(EmployeePayroll)
private readonly employeeRepository: Repository<EmployeePayroll>,
) {}
async addEmployeePayroll(employeePayroll: EmployeePayrollDTO) {
return await this.employeeRepository
.createQueryBuilder()
.insert()
.into(EmployeePayroll)
.values(employeePayroll)
.execute();
}
}
The Problem
The problem is that while the insert is successful, the values of the columns: employee_id and employee_version are null:
id
employee_payroll_name
employee_id
employee_version
53de51fd-6c9e-4b96-8906-edd1f6eea26c
Payroll 1
null
null
64b8a147-acee-4f43-9ea1-b64c2c036369
Payroll 2
null
null
Can you help me?
As per #Michael Levi's comment, the problem was that I wasn't setting the employee object correctly.
Here is what worked for me:
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { EmployeePayrollDTO } from 'src/employee-payroll/dto/employee-payroll.dto';
import { EmployeePayroll } from 'src/employee-payroll/entities/employee-payroll.entity';
import { Repository } from 'typeorm';
#Injectable()
export class EmployeePayrollService {
constructor(
#InjectRepository(EmployeePayroll)
private readonly employeeRepository: Repository<EmployeePayroll>,
) {}
async addEmployeePayroll(employeePayroll: EmployeePayrollDTO) {
return await this.employeeRepository
.createQueryBuilder()
.insert()
.into(EmployeePayroll)
.values({
employeePayrollName: employeePayroll.employeePayrollName,
employee: {
id: employeePayroll.employeeId,
version: employeePayroll.employeeVersion,
},
})
.execute();
}
}
Please note that this only inserts records in the EmployeePayroll table, and if I try to use a new id or version inside the employee object, I get a foreign key violation error.
I'm new to TypeORM and NestJs and i'm trying to join leave table with leave category table, but i'm getting error.
Here is my code.
Leave.entity.ts
import {Entity, Column, PrimaryGeneratedColumn, ManyToOne, OneToMany, OneToOne, JoinColumn } from "typeorm";
import { tbLeaveRequestDetails } from "./leave.entity";
#Entity("tbLeaveType")
export class tbLeaveType {
#PrimaryGeneratedColumn()
intLeaveTypeId : number;
#Column({ nullable: true, default: null, length: 300})
vchLeaveTypeName: string;
#Column()
intNumberOfLeaves: number;
#Column()
TypeStatus: number;
#OneToMany(type => tbLeaveRequestDetails, image => image.type)
leave: tbLeaveRequestDetails[];
}
LeaveType.entity.ts
import {Entity, Column, PrimaryGeneratedColumn, ManyToOne, OneToMany, JoinColumn, JoinTable, OneToOne } from "typeorm";
import { tbLeaveType } from "./leaveType.entity";
#Entity("tbLeaveRequestDetails")
export class tbLeaveRequestDetails {
#PrimaryGeneratedColumn()
intLeaveRequestId : number;
#Column()
intRegistrationId: number;
#Column()
intLeaveTypeId: number;
#ManyToOne(type => tbLeaveType, type => type.leave)
#JoinColumn({name: 'intLeaveTypeId'})
type: tbLeaveType;
}
Leave.service.ts
import { Injectable, NotFoundException } from "#nestjs/common";
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { tbLeaveType } from "../Entities/leaveType.entity";
import { tbLeaveRequestDetails } from "../Entities/leave.entity";
#Injectable()
export class leaveService {
constructor(
#InjectRepository(tbLeaveRequestDetails) private leave: Repository<tbLeaveRequestDetails>,
#InjectRepository(tbLeaveType) private leaveType: Repository<tbLeaveType>,
){}
async getLeaves(): Promise<any> {
try {
const query = await this.leave.createQueryBuilder('leave')
.innerJoinAndSelect("leave.leave", "leavetype");
return query.getQuery();
} catch(error){
throw new NotFoundException('Error in Getting ', error);
}
}
}
Why this code getting error...?
Anybody, please help.
Thanks.
in nest js Documentation i've read about Basic RBAC implementation but the last thing of this section says
"To make sure this example works, your User class must look as follows"
class User {
roles: Role[];
}
where should this line is going to be in
Check out authentication part of documentation. In implementing passport strategies paragraph you have UsersService defined like this:
import { Injectable } from '#nestjs/common';
// This should be a real class/interface representing a user entity
export type User = any;
#Injectable()
export class UsersService {
private readonly users = [
{
userId: 1,
username: 'john',
password: 'changeme',
},
{
userId: 2,
username: 'maria',
password: 'guess',
},
];
async findOne(username: string): Promise<User | undefined> {
return this.users.find(user => user.username === username);
}
}
You can create user.ts file near this service and import it here instead of defining type. How this class should look depends on source from which you get it. In this example users are hard-coded but usually that would be some kind of database entity.
Hard-coded example
For this hard-coded example I would do User class like this:
user.ts
import { Role } from "./role.enum";
export class User {
userId: number;
username: string;
password: string;
roles: Role[];
}
Where roles are in enum defined in authorization part of documentation
role.enum.ts
export enum Role {
User = 'user',
Admin = 'admin',
}
All this is joined inside service like this:
users.service.ts
import { Injectable } from '#nestjs/common';
import { User } from './user.entity';
import { Role } from "./role.enum";
#Injectable()
export class UsersService {
private readonly users: User[] = [
{
userId: 1,
username: 'john',
password: 'changeme',
roles: [Role.Admin]
},
{
userId: 2,
username: 'maria',
password: 'guess',
roles: [Role.User]
},
];
async findOne(username: string): Promise<User | undefined> {
return this.users.find(user => user.username === username);
}
}
Database example
Usually you would use some kind of database (more on database integration here), when using TypeOrm those classes would look like this:
user.entity.ts
import { Role } from "../role.enum";
import { Injectable } from '#nestjs/common';
import { UserEntity } from '../hard-coded/user';
import { InjectRepository } from "#nestjs/typeorm";
import { Repository } from "typeorm";
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn() userId: number;
#Column() username: string;
#Column() password: string;
// should have some kind of join table
#ManyToMany() roles: Role[];
}
users.service.ts
#Injectable()
export class UsersService {
constructor(#InjectRepository(UserEntity) private usersRepository: Repository<UserEntity>){}
async findOne(username: string): Promise<UserEntity | undefined> {
return this.usersRepository.findOne({ username });
}
}
I have two modules-
courses
-courses.module.ts
-courses.services.ts
-courses.schema.ts
-courses.controller.ts
groups
-groups.module.ts
-groups.services.ts
-groups.schema.ts
-groups.controller.ts
Content of courses.module.ts is-
import { Module } from '#nestjs/common';
import { CoursesService } from './courses.service';
import { CoursesController } from './courses.controller';
import { MongooseModule, getModelToken } from '#nestjs/mongoose';
import { CourseSchema } from './schemas/course.schema';
import { GroupsService } from 'src/groups/groups.service';
import { GroupsModule } from 'src/groups/groups.module';
#Module({
imports: [
MongooseModule.forFeature([{
name: 'Course', schema: CourseSchema }]),
GroupsModule,
],
providers: [
CoursesService,
],
controllers: [CoursesController],
exports: [CoursesService],
})
export class CoursesModule {}
Content of groups.module.ts is-
import { Module } from '#nestjs/common';
import { GroupsService } from './groups.service';
import { GroupsController } from './groups.controller';
import { MongooseModule } from '#nestjs/mongoose';
import { GroupSchema } from './schemas/group.schema';
#Module({
imports: [
MongooseModule.forFeature([{ name: 'Group', schema: GroupSchema }]),
],
providers: [GroupsService],
controllers: [GroupsController],
exports:[
GroupsService,
]
})
export class GroupsModule {}
I want to call methods of GroupsService inside courses.schema.ts like this-
import * as mongoose from 'mongoose';
import { GroupsService } from 'src/groups/groups.service';
import { GroupsModule } from 'src/groups/groups.module';
import { GroupSchema } from 'src/groups/schemas/group.schema';
export const CourseSchema = new mongoose.Schema({
groupId: {
type: Number,
isAsync: true,
validate(value) {
if(GroupsService.getByCode()==='xyz'){
return false;
}
}
},
})
How can I achieve this? I have gone through the documentation of NestJS but didn't find any appropriate method to inject service directly inside schema and access the service methods during custom validation.
In your main.ts exports the app reference.
export let app;
async function bootstrap() {
app = await NestFactory.create(AppModule);
}
bootstrap();
Then in your schema file you may do something like this
import { app } from 'path/to/main.ts';
const service = app.get(GroupsService);
Here you are using the service container to get a reference from the GroupsService.