Why am I getting a validation error? Nestjs, Sequelize , PostgressQL - node.js

I want to create a dependency where one User can have many InvestorCase, but one InvestorCase belongs to only one User. I need to have user_id field in InvestorCase.
User entity:
import { InvestorCase } from 'src/investor-case/entities/investor-case.entity';
import { ApiProperty } from '#nestjs/swagger';
import { Exclude } from 'class-transformer';
import {
AllowNull,
Column,
DataType,
Default,
HasMany,
IsIn,
Model,
Table,
} from 'sequelize-typescript';
import { UserRole, UserStatus } from 'src/shared/enums';
import { IUser } from 'src/shared/interfaces';
const userRoleValues = Object.values(UserRole);
const userStatusValues = Object.values(UserStatus);
#Table({ tableName: 'user' })
export class User extends Model<User, IUser> {
#ApiProperty({ example: '1', description: 'User`s Id' })
#Column({
type: DataType.INTEGER,
unique: true,
autoIncrement: true,
primaryKey: true,
})
public id: number;
#ApiProperty({ example: 'test#gmail.com', description: 'User`s Email' })
#Column({
type: DataType.STRING,
allowNull: false,
})
public email: string;
#ApiProperty({ example: 'password', description: 'User``s password' })
#Column({
type: DataType.STRING,
allowNull: true,
})
#Exclude()
public password: string;
#ApiProperty({ example: 'Barak', description: 'User`s name' })
#Column({
type: DataType.STRING,
allowNull: false,
})
public firstName: string;
#ApiProperty({ example: 'Obama', description: 'User`s surname' })
#Column({
type: DataType.STRING,
allowNull: false,
})
public lastName: string;
#ApiProperty({ example: '3806799599432', description: 'User`s phone number' })
#Column({
type: DataType.STRING,
})
public phoneNumber: string;
#ApiProperty({ example: 'verified', description: 'Account status' })
#IsIn({
args: [userStatusValues],
msg: `User status must one of the following:
${userStatusValues.join(', ')}`,
})
#Default(UserStatus.UNVERIFIED)
#Column
public status: UserStatus;
#ApiProperty({
example: 'developer',
description: 'User`s role',
enum: UserRole,
})
#IsIn({
args: [userRoleValues],
msg: `User role must one of the following:
${userRoleValues.join(', ')}`,
})
#Default(UserRole.INVESTOR)
#AllowNull(false)
#Column
public role: UserRole;
#HasMany(() => InvestorCase)
investorCases: InvestorCase[];
}
InvestorCare entity:
import { ApiProperty } from "#nestjs/swagger";
import { BelongsTo, Column, DataType, ForeignKey, IsIn, Model, PrimaryKey, Table } from "sequelize-typescript";
import { PaymentMethods } from 'src/shared/enums'
import { IInvestorCase } from 'src/shared/interfaces';
import { User } from "src/user/entities/user.entity";
const paymentMethods = Object.values(PaymentMethods);
#Table({ tableName: 'investor-case' })
export class InvestorCase extends Model<InvestorCase, IInvestorCase> {
#ApiProperty({ example: '1', description: 'Unique ID' })
#PrimaryKey
#Column({ type: DataType.INTEGER, unique: true, autoIncrement: true })
public id: number;
#ApiProperty({ example: '10000', description: 'The amount the investor will deposit initially.' })
#Column({ type: DataType.INTEGER, unique: true, allowNull: false, validate: { min: 1000 } })
public initialPayment: number;
#ApiProperty({ example: '1000', description: 'The amount that the investor will contribute monthly.' })
#Column({ type: DataType.INTEGER, allowNull: true, validate: { min: 500 } })
public monthlyPayment: number;
#ApiProperty({
example: 'true',
description: 'The payment method by which the investments will be made.',
enum: paymentMethods
})
#IsIn({
args: [paymentMethods],
msg: `The payment method must one of the following: ${paymentMethods.join(',')}`
})
#Column({ type: DataType.STRING, allowNull: false, defaultValue: PaymentMethods.Manually })
public paymentMethod: string;
#BelongsTo(() => User, {
foreignKey: 'userId',
as: 'UserId',
})
#ApiProperty({
example: '1',
description: 'Company representative user id',
})
#ForeignKey(() => User)
#Column({ type: DataType.INTEGER })
userId: number;
}
I try to create InvestorCase using this:
{
"initialPayment": 5000,
"monthlyPayment": 1000,
"paymentMethod": "Link a bank account",
"userId": 2
}
[Nest] 244 - 05/10/2022, 10:30:26 AM ERROR [ExceptionsHandler] Validation error
Error:
at Query.run (/app/node_modules/sequelize/src/dialects/postgres/query.js:76:25)
at /app/node_modules/sequelize/src/sequelize.js:643:28
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at PostgresQueryInterface.insert (/app/node_modules/sequelize/src/dialects/abstract/query-interface.js:773:21)
at InvestorCase.save (/app/node_modules/sequelize/src/model.js:4046:35)
at Function.create (/app/node_modules/sequelize/src/model.js:2253:12)
at InvestorCaseService.create (/app/src/investor-case/investor-case.service.ts:18:16)
at InvestorCaseController.create (/app/src/investor-case/investor-case.controller.ts:18:16)
at /app/node_modules/#nestjs/core/router/router-execution-context.js:46:28
at /app/node_modules/#nestjs/core/router/router-proxy.js:9:17
But alwways got error:
[Nest] 244 - 05/10/2022, 10:30:26 AM ERROR [ExceptionsHandler] Validation error
Error:
at Query.run (/app/node_modules/sequelize/src/dialects/postgres/query.js:76:25)
at /app/node_modules/sequelize/src/sequelize.js:643:28
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at PostgresQueryInterface.insert (/app/node_modules/sequelize/src/dialects/abstract/query-interface.js:773:21)
at InvestorCase.save (/app/node_modules/sequelize/src/model.js:4046:35)
at Function.create (/app/node_modules/sequelize/src/model.js:2253:12)
at InvestorCaseService.create (/app/src/investor-case/investor-case.service.ts:18:16)
at InvestorCaseController.create (/app/src/investor-case/investor-case.controller.ts:18:16)
at /app/node_modules/#nestjs/core/router/router-execution-context.js:46:28
at /app/node_modules/#nestjs/core/router/router-proxy.js:9:17

Related

getting graphql error Cannot return null for non-nullable field Account.groups. How to resolve?

I have a problem, when launching a query "Cannot return null for non-nullable field Account.groups".
I have graphql resolver:
#Resolver(of => Account)
export class AccountResolver {
constructor(
#Inject(AccountService) private accountService: AccountService,
) { }
#Query(returns => [Account], {
description: `To get all client's accounts`
})
#UseGuards(AuthGuard)
async accounts(
#Context('clientId') clientId: string,
): Promise<Account[]> {
return await this.accountService.getAllAccounts(clientId);
}
}
And model:
#ObjectType()
#Entity()
export class Account {
#Field()
#PrimaryGeneratedColumn('uuid')
id: string;
#Field()
#Column('text', {
default: ''
})
firstName: string;
#Field()
#Column('text', {
default: ''
})
lastName: string;
#Field()
#Column('text', {
nullable: true,
default: ''
})
avatar: string;
#Field()
#Column('text', {
unique: true,
})
username: string;
#Column('text', { nullable: false })
#Exclude({ toPlainOnly: true })
password: string;
#Field()
#Column({
type: 'varchar',
length: 300,
enum: Roles,
nullable: false
})
role: Roles
#Field()
#Column('text', { nullable: false, unique: true })
email: string;
#Field()
#Column({
default: false
})
emailVerified: boolean;
#Field()
#Column('text', { nullable: true, default: ''})
verificationLink: string;
#OneToMany(() => KitTask, kitTask => kitTask.account)
kitTasks: KitTask[];
#OneToMany(() => Trace, trace => trace.account)
traces: Trace[];
#Field(type => Client)
#ManyToOne(() => Client, client => client.accounts, { onDelete: 'CASCADE' })
client: Client;
#Field(type => Group)
#ManyToMany(() => Group, group => group.accounts)
#JoinTable()
groups: Group[];
}
I'm trying to launch query:
query accounts{
accounts{
id
firstName
lastName
avatar
username
role
email
emailVerified
client {
name
}
groups {
id
}
}
And getting the following error:
What is wrong?

Problem with include model M:N in multitenant app

I have a nodejs app using sequelize and postgres as a database, the architecture of the app is multitenant with separate schemas in the same database, the problem I present is that when including the permissions model in the user query, sequelize It doesn't take me the schema for the join table and therefore tries to query it in the default schema (public). these are my models:
Users:
export const USERS_TABLE = 'users';
#Table({
timestamps: false,
tableName: USERS_TABLE,
})
export class User extends Model {
#AfterCreate
static deletePassword(instance: User) {
instance.password = null;
}
#Column({
autoIncrement: false,
primaryKey: true,
type: DataType.STRING(36),
defaultValue: UUIDV4(),
field: 'user_id',
allowNull: false,
})
userId: string;
#Column({
type: DataType.STRING(20),
})
provider: string;
#Column({
type: DataType.STRING(50),
allowNull: false,
unique: true,
})
email: string;
#Column({
type: DataType.STRING(100),
field: 'customer_id',
unique: true,
})
customerId: string;
#Column({
type: DataType.STRING(50),
allowNull: false,
unique: true,
})
username: string;
#Column({
type: DataType.STRING(10),
unique: true,
})
phone: string;
#Column({
type: DataType.STRING(255),
field: 'recovery_token',
unique: true,
})
recoveryToken: string;
#Column({
type: DataType.STRING(10),
field: 'verify_code',
})
verifyCode: string;
#Column({
type: DataType.STRING(100),
})
password: string;
#Column({
type: DataType.TEXT,
})
devices: string;
#Column({
type: DataType.BOOLEAN,
defaultValue: false,
})
deleted: boolean;
#Column({
type: DataType.BOOLEAN,
defaultValue: false,
})
verified: boolean;
#Column({
type: DataType.INTEGER,
defaultValue: 1,
})
status: number;
#ForeignKey(() => Role)
#Column({
type: DataType.STRING(36),
field: 'role_id',
allowNull: false,
references: {
model: ROLES_TABLE,
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
})
roleId: string;
#ForeignKey(() => Profile)
#Column({
type: DataType.STRING,
field: 'profile_id',
allowNull: false,
references: {
model: PROFILES_TABLE,
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
})
profileId: string;
#ForeignKey(() => Subscription)
#Column({
type: DataType.STRING,
field: 'subscription_id',
references: {
model: SUBSCRIPTION_TABLE,
key: 'subscription_id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
})
subscriptionId: string;
#Column({
allowNull: false,
type: DataType.DATE,
field: 'created_at',
defaultValue: new Date(),
})
createdAt: Date;
#Column({
allowNull: false,
type: DataType.DATE,
field: 'updated_at',
defaultValue: new Date(),
})
updatedAt: Date;
// Prueba tenantId
#Column({
type: DataType.STRING(36),
field: 'tenant_id',
references: {
model: TENANT_TABLE,
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
})
tenantId?: string;
#ForeignKey(() => Seat)
#Column({
type: DataType.STRING(36),
field: 'seat_id',
references: {
model: SEAT_TABLE,
key: 'seat_id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
})
seatId: string;
// associations
#BelongsToMany(() => Permission, () => UserPermission)
Permissions: Permission[];
#BelongsTo(() => Subscription) Subscription: Subscription;
#BelongsTo(() => Profile) Profile: Profile;
#BelongsTo(() => Role) Role: Role;
#BelongsTo(() => Seat) Seat: Seat;
}
Permissions
export const PERMISSIONS_TABLE = 'permissions';
#Table({
timestamps: false,
tableName: PERMISSIONS_TABLE,
})
export class Permission extends Model {
#Column({
allowNull: false,
field: 'permission_id',
autoIncrement: false,
primaryKey: true,
type: DataType.STRING(36),
defaultValue: UUIDV4(),
})
permissionId: string;
#Column({
type: DataType.STRING(20),
allowNull: false,
})
name: string;
#Column({
type: DataType.STRING(150),
})
description: string;
#Column({
type: DataType.TEXT,
allowNull: false,
})
models: string;
#ForeignKey(() => Subscription)
#Column({
type: DataType.STRING,
allowNull: false,
field: 'subscription_id',
references: {
model: SUBSCRIPTION_TABLE,
key: 'subscription_id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
})
subscriptionId: string;
#Column({
type: DataType.BOOLEAN,
allowNull: false,
defaultValue: true,
})
status: boolean;
#Column({
type: DataType.STRING(36),
field: 'tenant_id',
references: {
model: TENANT_TABLE,
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
})
tenantId?: string;
#Column({
allowNull: false,
type: DataType.DATE,
field: 'created_at',
defaultValue: new Date(),
})
createdAt: Date;
#Column({
allowNull: false,
type: DataType.DATE,
field: 'updated_at',
defaultValue: new Date(),
})
updatedAt: Date;
// Associations
#BelongsToMany(() => User, () => UserPermission) Users: User[];
}
Join Table
export const USERS_PERMISSIONS_TABLE = 'user_permissions';
#Table({
timestamps: false,
tableName: USERS_PERMISSIONS_TABLE,
})
export class UserPermission extends Model {
#ForeignKey(() => User)
#Column({
type: DataType.STRING,
field: 'user_id',
allowNull: false,
references: {
model: USERS_TABLE,
key: 'user_id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
})
userId: string;
#ForeignKey(() => Permission)
#Column({
type: DataType.STRING,
field: 'permission_id',
allowNull: false,
references: {
model: PERMISSIONS_TABLE,
key: 'permission_id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
})
permissionId: string;
}
I pass the necessary parameters to a base class that performs my query
users = await super.getAll(
tenantId,
[
Role.schema(schemaName),
Profile.schema(schemaName),
Subscription,
Permission.schema(schemaName),
],
body.page,
body.size,
{},
);
findAll method of Super Class
public async getAll(
tenantId: string,
includeModel?: object,
page?: string,
size?: string,
where?: Record<string, unknown>,
) {
try {
// free_id
let records: unknown;
const free = tenantId.split('_');
const tenantName: string = getTenantName(tenantId);
if (free[0] === 'free') {
where.tenantId = free[1];
}
if (!includeModel) {
const { limit, offset } = paginate(page, size);
records = await this.model.schema(tenantName).findAndCountAll({
where,
limit,
offset,
});
return new HttpResponse.getSuccessful(records);
}
const { limit, offset } = paginate(page, size);
records = await this.model.schema(tenantName).findAndCountAll({
where,
limit,
offset,
include: includeModel,
});
return new HttpResponse.getSuccessful(records);
} catch (e) {
throw boom.badRequest(e);
}
}
The generated query And The error:
sql: `SELECT count("User"."user_id") AS "count" FROM "free_trial"."users" AS "User" LEFT OUTER JOIN "free_trial"."roles" AS "Role" ON "User"."role_id" = "Role"."role_id" LEFT OUTER JOIN "free_trial"."profiles" AS "Profile" ON "User"."profile_id" = "Profile"."profile_id" LEFT OUTER JOIN "subscriptions" AS "Subscription" ON "User"."subscription_id" = "Subscription"."subscription_id" LEFT OUTER JOIN ( "user_permissions" AS "Permissions->UserPermission" INNER JOIN "free_trial"."permissions" AS "Permissions" ON "Permissions"."permission_id" = "Permissions->UserPermission"."permission_id") ON "User"."user_id" = "Permissions->UserPermission"."user_id" WHERE "User"."tenant_id" = '72a5afbe-dc66-4b54-a428-d7bed9a2cbd9';`,
parameters: {},
isBoom: true,
isServer: false,
data: null,
output: {
statusCode: 400,
payload: {
statusCode: 400,
error: 'Bad Request',
message: 'no existe la relación «user_permissions»'
},
headers: {}
}
}
As can be seen in the query, all the tables are preceded by the name of the schema in which they are found, but in the join table it refers to the public schema and therefore it cannot be found; I have already tried thousands of ways but nothing seems to work, any ideas are appreciated.
I have tried to pass the name of the schema to the join table, referring to it in the include but it does not give results, I also tried within permissions to include the join table to be able to pass the name of the schema.

Nest.js TypeORM FindOneBy returns null even with data on the database

So I have this entity...
import { Column, Entity, PrimaryColumn } from "typeorm";
#Entity('Users')
export class User {
#PrimaryColumn({ type: 'nvarchar', length: 36, unique: true })
UserId: string;
#Column({ type: 'nvarchar', length: 100, unique: true })
Email: string;
#Column({ type: 'nvarchar', length: 36 })
CrmUserId: string;
#Column({ type: 'nvarchar', length: 36, nullable: true })
AzureB2CUserId: string;
#Column({ type: 'bit', default: false })
IsActive: boolean;
#Column({ type: 'nvarchar', length: 100 })
CreatedBy: string;
#Column({ type: 'datetime' })
CreatedDate: Date;
#Column({ type: 'nvarchar', length: 100, nullable: true })
UpdatedBy: string;
#Column({ type: 'datetime', nullable: true })
UpdatedDate: Date;
}
and using TypeORM I want to get one record by the email, not the UserId. So I had this on the repository.
public async getUserByEmail(email: string): Promise<User | null> {
let _res = await this._userRepository.findOne({ where: { Email: email, IsActive: true }})
return _res;
}
But it always returns a null, even if the record exists, I was thinking of doing it with a CreateQueryBuilder, like this...
public async getUserByEmail(email: string): Promise<User | null> {
let _res = await this._userRepository.createQueryBuilder()
.select()
.from(User, "Users")
.where('email = :emailParameter', { email })
.getOne();
return _res;
}
But the result is the same, I keep getting null, I have no idea what I am doing wrong, because it works if I use the primary key on the findOne and findOneBy. Any help out there with this?
If you are using MongoDB then use import { MongoRepository, Repository } from 'typeorm'; for the _userRepository type.
While using _userRepository findOneBy wrap the userId parameter in import { ObjectId } from 'mongodb' const query = {"_id":ObjectId(req.params.productId)}
OR have a look at this example
know this due to the fact that where in the "entity" you give the default value in the "delete_date" to the user, you must definitely give null.
I wrote in my entity like this
#DeleteDateColumn({
name: 'deleted_date',
type: 'timestamp',
default: null, // default: () => 'CURRENT_TIMESTAMP(6)',
})
public deletedAt: Date;
#CreateDateColumn({
name: 'created_date',
type: 'timestamp',
default: () => 'CURRENT_TIMESTAMP(6)',
})
public createdAt: Date;
#UpdateDateColumn({
name: 'updated_date',
type: 'timestamp',
default: null
onUpdate: 'CURRENT_TIMESTAMP(6)',
})
public updatedAt: Date;
you can't give values to a "delete_date" when you create a user

How to make a certain SQL instruction in sequelize?

I want to make the next SQL instruction, but I don't know how. I tried many ways, but I can't make it work in sequelize. This is the SQL instruction:
SELECT interes_revision.nombre FROM interes_revision
LEFT JOIN asociar ON interes_revision.id = asociar.id_interes_revision
WHERE asociar.id_par_evaluador = <req.params>
And tbe models asociated with that instruction:
InteresRevision.ts
#Table({
timestamps:false,
tableName: "interes_revision"
})
export class InteresRevision extends Model {
#Column({
type: DataType.INTEGER,
primaryKey:true,
allowNull:false,
autoIncrement:true
})
id!:number;
#Column({
type: DataType.STRING,
allowNull:true,
unique: true
})
nombre!:string;
#HasMany(() => Asociar)
asociar!: Asociar[];
ParEvaluador.ts
#Table({
timestamps:false,
tableName: "par_evaluador"
})
export class ParEvaluador extends Model {
#Column({
type: DataType.INTEGER,
primaryKey:true,
allowNull:false,
autoIncrement:true
})
id!:number;
#HasMany(() => Asociar)
asociar!: Asociar[];
}
Asociar.ts
#Table({
timestamps:false,
tableName: "asociar"
})
export class Asociar extends Model {
#ForeignKey(() => ParEvaluador)
#Column({
allowNull: false
})
id_par_evaluador!: number;
#ForeignKey(() => InteresRevision)
#Column({
allowNull: false
})
id_interes_revision!: number;
#BelongsTo(() => InteresRevision)
interesRevision!: InteresRevision;
#BelongsTo(() => ParEvaluador)
parEvaluador!: ParEvaluador;
}
The instruction in particular would show only a column from one table, and would return a list of "interes_revision" where the "id_par_evaluador" on "asociar" matches the req.params' id you want to match. Any help is welcome.

TypeORM import of Relation fails

I am using typeorm in a nodejs project with esm. I need to use the Relation wrapper to avoid circular dependencies.
I get this error when i try to use the relation wrapper:
import { Column, Entity, OneToOne, PrimaryGeneratedColumn, Relation } from 'typeorm';
^^^^^^^^
SyntaxError: The requested module 'typeorm' does not provide an export named 'Relation'
The code file:
import { Column, Entity, OneToOne, PrimaryGeneratedColumn, Relation } from 'typeorm';
import { Vehicle } from './vehicle';
#Entity({ name: 'vehicle_default_values' })
export class VehicleDefaultValues {
#PrimaryGeneratedColumn({ unsigned: true })
id: number;
#Column({ type: 'int', unsigned: true, unique: true })
model: number;
#Column({ type: 'float', default: 550 })
maxFuel: number;
#Column({ type: 'boolean', default: false })
isElectric: boolean;
#Column({ type: 'float', default: 0.008 })
fuelConsumption: number;
#Column({ type: 'mediumint', unsigned: true, default: 200 })
maxSpeed: number;
#Column({ type: 'mediumint', unsigned: true, default: 50 })
trunkVolume: number;
#Column({ type: 'text', nullable: true })
vehicleExtras: string;
#OneToOne(() => Vehicle, (vehicle) => vehicle.vehicleDefaultValues)
vehicle: Relation<Vehicle>;
}
Used version of typeorm: 0.3.7

Resources