I am building a project using NestJs. I have a problem that "#IsNotEmpty" in Dto does not check empty. The checking is only performed because {require: true} in the schema. Please help me with this case. Thank you!
Here is my code:
import { IsNotEmpty, IsString } from 'class-validator';
export class AddCommentDto {
#IsNotEmpty()
#IsString()
comment: string
#IsNotEmpty()
#IsString()
walletId: string
#IsString()
replyCommentId: string
}
//Schema
#Schema({
timestamps: true,
})
export class UserComment extends Document {
#Prop({
required: true
})
walletId: string;
#Prop({
})
replyCommentId: string;
#Prop({
require: true
})
comment: string;
}
An explanation for this case.
Related
I want to add some linking dependencies between my props in my VueJS component.
For example in my component on props declaration, i would like to stipulate that if a prop is present, then another one should be required, but not required at all if the previous props is not there.
props: {
url: {
type: String,
required: true,
},
isShared: {
type: Boolean,
default: false,
},
isSharedByOtherMember: {
type: Boolean,
default: false,
},
archivedId: {
type: String,
required: isSharedByOtherMember ? true : false, // This is not working, bit is there a way to do so ?
},
After reading vuejs docs :
Note that props are validated before a component instance is created, so instance properties (e.g. data, computed, etc) will not be available inside default or validator functions.
Is there a way to still do this in props declaration for better readability/understandability after ?
Thanks in advance
You could use the validator property for prop.
Vue docs has this example: (https://v2.vuejs.org/v2/guide/components-props.html#Prop-Validation)
// Custom validator function
propF: {
validator: function (value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
You could define a validator method in Vue methods section.
Something like this:
export default {
props: {
isSharedByOtherMember: {
type: Boolean,
default: false
},
archivedId: {
type: String,
default: null,
required: false,
validator: this.validateArchiveId(),
errorMessage: 'Archived ID required when isSharedByOtherMember has value of true.'
}
},
methods: {
validateArchiveId () {
return this.isSharedByOtherMember
}
}
}
Assume a user is logged in and there is a Post document, I want to save the user who created and updated the post.
export interface Post extends Document {
readonly title: string;
readonly content: string;
readonly createdAt?: Date;
readonly updatedAt?: Date;
readonly createdBy?: User;
readonly updatedBy?: User;
}
export const PostSchema = new Schema({
title: SchemaTypes.String,
content: SchemaTypes.String,
createdAt: { type: SchemaTypes.Date, required: false },
updatedAt: { type: SchemaTypes.Date, required: false },
createdBy: { type: SchemaTypes.ObjectId, ref: 'User', required: false },
updatedBy: { type: SchemaTypes.ObjectId, ref: 'User', required: false },
});
But I have no idea how to read the user from the request in a service component or Mongoose document schema.
It really depends on how you authenticate your users, but you should be able to intercept whichever access token, cookies (...) that the frontend uses to authentifies itself.
You can intercept it in the controller and get the corresponding user (or userId) which you can then pass to your service.
In case you're using nestjs passport, your example could look like this:
import { Injectable, Request, Body } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
import { Controller, Bind, Request, Post, UseGuards } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { JwtAuthGuard } from './auth/jwt-auth.guard';
#Controller()
export class PostController {
#UseGuards(JwtAuthGuard)
#Put('post')
addPost(#Request() request, #Body() body) {
const user = request.user._id;
// We only need the id to add it to mongo but you could also pass the whole user
return this.createPost(body, user._id)
}
}
#Injectable()
class PostService
//...
async createPost(body, userId) {
this.postModel.create({
...body,
createdBy: userId
})
}
//...
If you want more information about how to use nestjs passport and implement auth guards, see here nestjs docs
I'm trying to get into nestjs by creating a simple REST Api with TypeORM and the crud library. So far I have created a working role based authentication, but I'm running into a strange problem. I used the crud library to create a simple controller for the User entity. The GET-requests are working without any problems. But I can't POST to create a new user, neither can I use PATCH to update one. I think it might just be a very stupid mistake by me, but as I did not write much code, I can't find any differences to the examples in the doc.
When I try to patch a property, it just responds me with the original user object, no changes made (It's like I send an empty request).
When I try to post a new user, the response is the following error message:
{
"statusCode": 400,
"error": "Bad Request",
"message": "Empty data. Nothing to save."
}
It might have something to do with validation..
This is my user controller:
import { Controller, UseGuards } from '#nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';
import { AuthGuard } from '#nestjs/passport';
import { ApiTags, ApiSecurity } from '#nestjs/swagger';
import { RolesGuard } from 'src/auth/role.guard';
import { Roles } from './roles.decorator';
import { Crud, CrudController } from '#nestjsx/crud';
#UseGuards(AuthGuard('jwt'), RolesGuard)
#Crud({
model: {
type: User
},
routes: {
exclude: ['createManyBase', 'replaceOneBase'],
},
//validation: false,
})
#Roles('admin')
#ApiSecurity('bearer')
#ApiTags('user')
#Controller('user')
export class UserController implements CrudController<User> {
constructor(public service: UserService) {}
}
This is my user service:
import { Injectable, Body, NotFoundException } from '#nestjs/common';
import { CreateUserDTO } from './dto/create-user.dto';
import { User } from './user.entity';
import { GetUsersFilterDto } from './dto/get-users-filter.dto';
import { InjectRepository } from '#nestjs/typeorm';
import { UserRepository } from './user.repository';
import { Role } from './role.entity';
import { TypeOrmCrudService } from '#nestjsx/crud-typeorm';
#Injectable()
export class UserService extends TypeOrmCrudService<User> {
constructor(
#InjectRepository(User) user,
private userRepository: UserRepository
) {
super(user);
}
async getUserByName(username: string): Promise<User>{
const found = await this.userRepository.findOne({
where: {
username: username,
},
relations: ["roles"]
});
if (!found){
throw new NotFoundException('User "${username}" not found!');
}
return found;
}
async getUserById(id: number): Promise<User>{
const found = await this.userRepository.findOne(id, {relations: ["roles"] });
if (!found){
throw new NotFoundException('User with "${id}" not found');
}
return found;
}
async matchRoles(roles: string[], userroles: Role[]){
let match = false;
console.log(userroles)
userroles.forEach(r => {
if (roles.indexOf('r.name')){
match = true;
}
})
return match;
}
}
This is the entity:
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany, JoinTable, BeforeInsert, Unique } from 'typeorm';
import { Role } from './role.entity';
import * as bcrypt from 'bcryptjs';
import { Exclude } from 'class-transformer';
import { ApiProperty } from '#nestjs/swagger';
#Entity('auth_user')
#Unique(['username'])
export class User {
#PrimaryGeneratedColumn()
id: number;
#ApiProperty()
#Column({ length: 30 })
username: string;
#ApiProperty()
#Column()
firstName: string;
#ApiProperty()
#Column()
lastName: string;
#ApiProperty()
#Column()
email: string;
#BeforeInsert()
async hashPassword() {
this.password = await bcrypt.hash(this.password, 10);
}
#ApiProperty()
#Column()//({select: false})
#Exclude()
password: string;
#ApiProperty()
#Column({ default: true })
isActive: boolean;
#ManyToMany(
type => Role,
role => role.users,
{ cascade: true },
)
#JoinTable()
roles?: Role[];
}
Any hints are appreciated
As it turned out, it was the validation. Crud already has validation activated and I had this in the main.ts:
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true}));
So it was validated twice, what somehow led to an empty body in the request. I removed this and now I'm able to post/patch/put.
I came accross this issue about a simple password change form, in which I want to make sure that the new password is different from the old
I've tried implementing it directly following the documentation :
import { ref, withParams } from 'vuelidate/lib/validators'
export const differsFrom = equalTo => withParams(
{type: 'differsFrom', eq: equalTo},
function (value, parentVm) {
return value !== ref(equalTo, this, parentVm)
}
)
Now, importing the proper items from vuelidate is not as easy as the documentation states it.
Use not:
import { sameAs, not } from 'vuelidate/lib/validators'
export default {
data () {
return {
password: '',
oldPassword: ''
}
},
validations: {
password: {
not(sameAs('oldPassword'))
}
}
}
Hints:
withParams is not a function there, have to import it like this:
import { withParams } from 'vuelidate/lib/params'
I could not find how to import ref properly... It kept saying that it was not a function.
Now, an implementation of a not validator works just as well:
import { withParams } from 'vuelidate/lib/params'
export const not = validator => {
return withParams({type: 'not'}, (...args) => !validator(...args))
}
I wanted to hide routes that were the user roles and I found THIS question on SO that is similar. I tried to implement it in my typescript project but its returning nothing and I am not sure why.
This is my implementation as it stands.
import { autoinject, bindable, bindingMode } from "aurelia-framework";
import { Router } from 'aurelia-router'
#autoinject
export class Navmenu {
public userName: string = 'anonymous';
private userRole = localStorage.getItem("user_role");
constructor(public authService: AuthService, public router: Router) {
this.userName = authService.getUserName();
console.log("userRole: ", this.userRole);
}
get routes() {
return this.router.navigation.filter(r => r.settings.roles === this.userRole );
}
}
My console.log shows "Admin" in the console but my filter doesnt filter it.
Here is how I have structured a route:
{
route: ["", "scheduler"],
name: "scheduler",
settings: {
icon: "scheduler",
auth: true,
roles: ["Employee", "Admin"], //These are my roles for this route.
pos: "left"
},
moduleId: PLATFORM.moduleName("../components/scheduler/scheduler"),
nav: true,
title: "scheduler"
},
Roles is an array.
How do I structure my filter so that it matches any userRole and thus returns a subset of filtered routes?
Look at this line in your router config:
roles: ["Employee", "Admin"]
Then at this in your getter:
r.settings.roles === this.userRole
roles is an array whereas this.userRole is a string, so the === operator will always return with false. Use indexOf or some instead:
return this.router.navigation.filter(r => r.settings.roles.indexOf(this.userRole) > -1);
or
return this.router.navigation.filter(r => r.settings.roles.some(t => t === this.userRole));