I am using sequelize-typescript in this app and I have a model that currently is trying to hash a password before saving it
account.model.ts
#Table({
freezeTableName: true,
tableName: 'accounts',
})
export default class Account extends Model {
// ... other fields
#Length({ min: 0, max: 255 })
#Column({
comment:
'optional for social login users and compulsory for users signing up with an email password combination',
})
password: string;
// ...more fields
#BeforeUpdate
#BeforeCreate
static async hashPassword(instance: Account) {
instance.password = await buildHash(instance.password);
}
}
My buildHash function is asynchronous in nature and looks like this
import hasher from 'argon2';
const buildHash = (item) => hasher.hash(item);
I am getting an accountId not exists error when I try setting users on the account object
account.service.ts
static async create(createBody: {
email: string;
authenticationTypeId: string;
emailVerified?: boolean;
isPrimary?: boolean;
password?: string;
pictureUrl?: string;
socialAccountId?: string;
username?: string;
users?: User[];
}) {
console.log(createBody);
const createdAccount: Account = new Account(createBody);
console.log(createdAccount);
if (createBody.users) createdAccount.$set('users', createBody.users);
return createdAccount.save();
}
Very specifically it has an error at this line saying accountId does not exist
if (createBody.users) createdAccount.$set('users', createBody.users);
If I replace my hasher with a sync function from bcrypt, it works perfectly
import hasher from 'bcrypt';
const buildHash = (item) => hasher.hashSync(item, 12);
The model hook changed appropriately when using bcrypt
#BeforeUpdate
#BeforeCreate
static hashPassword(instance: Account) {
instance.password = buildHash(instance.password);
}
Unfortunately argon2 doesnt have a SYNC function and I dont want to change my hashing algorithm back to bcrypt
Can someone kindly tell me how I can wait for the new Account to be created before attempting to update the many to many relationship?
i am using nestjs/graphql, and i made a dto for a graphql mutation where i used class-validator options like #IsString() and #IsBoolean(). for this i installed class-validator and class-transformer. But when i do the mutation, it gives me an unheard error. i googled it, but nothing comes out.
the error is like this:
[Nest] 5872 - 2021. 11. 21. 오후 7:56:09 ERROR [ExceptionsHandler] classTransformer.plainToClass is not a function
TypeError: classTransformer.plainToClass is not a function
at ValidationPipe.transform (/home/inust33/ubereats-backend/node_modules/#nestjs/common/pipes/validation.pipe.js:51:39)
at /home/inust33/ubereats-backend/node_modules/#nestjs/core/pipes/pipes-consumer.js:16:33
at processTicksAndRejections (internal/process/task_queues.js:95:5)
in playground, it shows me like this:
graphql playground error
my dto looks like this:
#ArgsType()
export class createRestaurantDto {
#Field((type) => String)
#IsString()
#Length(5, 10)
name: string;
#Field((type) => Boolean)
#IsBoolean()
isVegan: boolean;
#Field((type) => String)
#IsString()
address: string;
#Field((type) => String)
#IsString()
ownersName: string;
#Field(() => String)
#IsString()
categoryName: string;
}
the mutation i used this dto is this:
#Mutation(() => Boolean)
async createRestaurant(
#Args() createRestaurantDto: createRestaurantDto,
): Promise<boolean> {
try {
await this.restaurantService.createRestaurant(createRestaurantDto);
return true;
} catch (e) {
console.log(e);
return false;
}
}
i did the validation pipe setting in main.ts like this:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
All I can get is without setting the useGlobalPipes option which is not what i want to do here, the mutation works out well.
could you please help me with this?
problem solved.
due to recent update, class-transformer#0.5.0 makes an error when used in validationPipe of nestJS.
you should downgrade to class-transformer#0.4.0
https://github.com/nestjs/nest/issues/8637
Had the same error with class-transformer#0.4.0
Updated to class-transformer#0.5.1 and it was solved.
I'm using Nest v8.0.0
I have an endpoint with no entry params:
async myendpoint(): Promise<any> {
const customer = await this.customerService.findOne(1);
if (customer) {
return await this.customerService.mapToDestination(customer);
}...
}
Then I have my method mapToDestination where I simply assign vars:
async mapToDestination(customer: Customer): Promise<DestinationDto> {
const destination: DestinationDto = {
lastname: customer.lastname,
firstname: customer.firstname,...
Finally, I have my DTO:
import {IsEmail, IsNotEmpty, IsOptional, IsNumber, IsBoolean, IsString, IsDate, MaxLength, Length, NotEquals} from 'class-validator';
import {ApiProperty} from '#nestjs/swagger';
export class DestinationDto {
#IsString()
#IsNotEmpty()
#MaxLength(32)
lastname: string;
#IsString()
#IsNotEmpty()
#MaxLength(20)
firstname: string; ...
I would like my DTO fields to be validated automatically following the decorators when I'm mapping it in my mapToDestination() method. I look through the web and the official documentation and I gave a try to Validators (ValidationPipe) but it does not seem to be my need as it validates the endpoint entry params.
Please, could you explain to me how to achieve this automatic validation? Thanks in advance.
I won't be "automatic" but you could instantiate your own instance of the validator from class validator and use it against the DTO in your service. Otherwise, it won't ever happen automatically because as you said, the ValidationPipe only works on the entry of the endpoint.
Example
Inside of mapToDestination so long as customer is an instance of DestinationDTO` you can have something like this:
#Injectable()
export class CustomerService {
async mapToDestination(customer: DestinationDTO) {
const errors = await validate(customer);
if (errors) {
throw new BadRequestException('Some Error Message');
}
...
}
...
}
I am seeing some really unusual behaviour in a type-graphql project.
I've followed the documentation here https://typegraphql.com/docs/resolvers.html to link some GQL into a JS function, but the object is simply empty when it enters the function. Really not sure why it is doing this.
Given this type class:
#ArgsType()
export class GetFoosArgs implements IGetFoosArgs {
#Field(() => Boolean, { nullable: true, defaultValue: false })
bar?: boolean;
#Field(() => Int, { nullable: true, defaultValue: 0 })
baz?: number;
#Field(() => Int, { nullable: true, defaultValue: 100 })
buzz?: number;
}
And this query function:
#Query(_returns => [Foo], { name: 'foo' })
async getFoos(#Args() {
bar,
baz,
buzz
}: GetFoosArgs): Promise<Foo[]> {
console.log({
foo,
baz,
buzz,
});
}
And this GQL query:
{
foos(
bar:true,
baz:1,
buzz:2,
) {
_key
}
}
The console dump just shows:
{
bar: undefined,
baz: undefined,
buzz: undefined
}
:-S
look good to me, I tested it and it worked just fine, maybe it's something related to your TypeGraphql typescipt setup, it might be helpful to check TypeGraphql ts config docs.
An issue I noticed and might be related is that when I use babel to compile TypeGraphql project, object destructuring in #Args and #Arg will not work properly
so maybe trying this instead might work in your case
#Query((returns) => Boolean, { name: 'foo' })
async getFoos(#Args() args: GetFoosArgs): Promise<boolean> {
console.log(args);
return true;
}
The strange thing is that graphql is strict and the idea that your resolver compiled successfully then you could run the query without errors, yet you get this output is a bit confusing so I'm not sure if SO will be helpful in your case, at least not without more info.
also note that your posted code contains some errors like query using foos when it should be foo so please read how to include MVCE in your question which will be more helpful for others to debug the issue.
P.S. I'm using TypeGraphql v0.17.6
Make sure you've installed the babel-plugin-transform-typescript-metadata package via npm (enter link description here.
You can read more about Babel + TypeScript compiler options here.
In my case, the args never showed up in my emitted schema in the first place (I checked via emitSchemaFile option for buildSchema). Installing the mentioned plugin and adding it to the plugins section of my .babelrc fixed it.
Ok, not completely sure of the nature of the problem, but I've changed things up a bit and the magic is working now.
The key thing to note is that before, I was using the Arg decorator from type-graphql, and now I'm using the Args decorator from #nestjs/graphql.
Old code:
import { InputType, Field, Int, Arg, ID } from 'type-graphql';
#Query(_returns => [Intent], { name: 'intents' })
async getIntents(
#Arg('getIntentsArgs', () => GetIntentsArgs, {
nullable: true,
defaultValue: {},
})
getIntentsArgs: GetIntentsArgs,
): Promise<Intent[]> {
}
#InputType()
export class GetIntentsArgs implements IGetIntentsArgs {
#Field(() => Boolean, { nullable: true, defaultValue: false })
displayTest?: boolean;
#Field(() => Int, { nullable: true, defaultValue: 0 })
skip?: number;
#Field(() => Int, { nullable: true, defaultValue: 100 })
take?: number;
#Field(() => String, { nullable: true, defaultValue: 'intent._key' })
sortColumn?: string;
#Field(() => String, { nullable: true, defaultValue: 'asc' })
sortDirection?: string;
#Field(() => [Filter], { nullable: true, defaultValue: [] })
filters?: IFilter[];
}
New code:
import {
Resolver,
Query,
Parent,
ResolveProperty,
Mutation,
Args,
Context,
} from '#nestjs/graphql';
#Query(_returns => [Intent], { name: 'intents' })
async getIntents(
#Args({ name: 'getIntentsArgs', type: () => GetIntentsArgs })
getIntentsArgs: GetIntentsArgs,
): Promise<Intent[]> {
return this.intentsService.getIntents(getIntentsArgs);
}
#InputType()
export class GetIntentsArgs implements IGetIntentsArgs {
#Field()
#IsBoolean()
#IsOptional()
displayTest?: boolean = false;
#Field()
#IsNumberString()
#IsOptional()
skip?: number = 0;
#Field()
#IsNumberString()
#IsOptional()
take?: number = 100;
#Field()
#IsString()
#IsOptional()
sortColumn?: string = 'intent._key';
#Field()
#IsString()
#IsOptional()
sortDirection?: string = 'asc';
#Field(_ => [Filter])
#Type(_ => Filter)
#IsOptional()
filters?: Filter[] = [];
Filter[] = [];
}
I'm trying to load a hierarchical treeview from an external source, but I'm having difficulties loading the first node childs and most likey the inner childs aswell (but this I don't know yet..).
The data is loaded through axios, so the RxJS Observable methods won't work. I tried may different things but when I'm expanding the first node the spinner just keeps on spinning until eternity.
Some of the points that I have tried
Tried to assign the parent node with the childeren
Tried an async modifier
Tried many different things with hasChilderen function
Hopefully someone can point me in the right direction.
My code:
tree.component.html
<kendo-treeview [nodes]="locations | async" [textField]="['shortName']" kendoTreeViewExpandable
[hasChildren]="hasChildren.bind(this)" [children]="fetchChildren.bind(this)">
</kendo-treeview>
tree.component.ts
export class TreeComponent implements OnInit {
public locations: Promise<Location[]>
constructor(private locationService: LocationService,
private cdRef: ChangeDetectorRef) { }
ngOnInit() {
this.locations = this.locationService.getBaseLocation();
}
public hasChildren = (item: Location): boolean => item.hasChildLocations;
public fetchChildren = (item: Location): Promise<Location[]> => this.locationService.getChildLocations(item.locationId);
}
location.service.ts
export class LocationService {
async getBaseLocation(): Promise<Location[]> {
return await axios.get<Location>('/Location/GetBaseLocation')
.then((response: AxiosResponse<Location>) => {
return [response.data];
});
}
async getChildLocations(locationId: string): Promise<Location[]> {
return await axios.get<Location[]>(`/Location/GetLocationTree?UpperLocationID=${locationId}`)
.then((response: AxiosResponse<Location[]>) => response.data);
}
}
location.model.ts
export interface Location {
locationId: string;
upperLocationId: string;
locationTypeId: number;
shortName: string;
plantCode: string;
hasChildLocations: boolean;
icon: string;
isSelected: boolean;
childeren: Location[];
}
e.g. Spinner keeps rolling
console prints out an error before my childs are loaded from the external service, so my 5 cents are on the fact that angular is trying to expand the nodes before they are loaded.
Easy solution:
Change
public fetchChildren = (item: any) this.locationService.getChildLocations(item.locationId);
to
public fetchChildren = (item: any) => from(this.locationService.getChildLocations(item.locationId));