Skip to content

Commit 6ae468b

Browse files
fix: add & update user logic (#150)
1 parent ef8d868 commit 6ae468b

File tree

8 files changed

+372
-49
lines changed

8 files changed

+372
-49
lines changed
Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,40 @@
1-
import { Field, InputType, PartialType } from '@nestjs/graphql';
1+
import { InputType } from '@nestjs/graphql';
2+
import {
3+
EmailField,
4+
EmailFieldOptional,
5+
PasswordField,
6+
StringField,
7+
StringFieldOptional,
8+
URLFieldOptional,
9+
} from '@repo/graphql';
10+
import { lowerCaseTransformer } from '@repo/nest-common';
11+
import { Transform } from 'class-transformer';
212

3-
@InputType()
13+
@InputType({ description: 'User register request' })
414
export class CreateUserInput {
5-
@Field(() => String)
15+
@EmailField()
616
email: string;
717

8-
@Field(() => String)
18+
@StringField()
19+
@Transform(lowerCaseTransformer)
920
username: string;
1021

11-
@Field(() => String)
22+
@PasswordField()
1223
password: string;
1324
}
1425

15-
@InputType()
16-
export class UpdateUserInput extends PartialType(CreateUserInput) {
17-
@Field(() => Number)
18-
id: number;
26+
@InputType({ description: 'User update request' })
27+
export class UpdateUserInput {
28+
@EmailFieldOptional()
29+
email?: string;
30+
31+
@StringFieldOptional()
32+
@Transform(lowerCaseTransformer)
33+
username?: string;
34+
35+
@StringFieldOptional()
36+
bio?: string;
37+
38+
@URLFieldOptional()
39+
image?: string;
1940
}
Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { Field, ObjectType } from '@nestjs/graphql';
22
@ObjectType()
33
export class User {
4-
@Field(() => String)
5-
email: string;
4+
@Field(() => Number, { nullable: true })
5+
id?: number;
66

7-
@Field(() => String)
8-
token: string;
7+
@Field(() => String, { nullable: true })
8+
email?: string;
99

10-
@Field(() => String)
11-
username: string;
10+
@Field(() => String, { nullable: true })
11+
token?: string;
1212

13-
@Field(() => String)
14-
bio: string;
13+
@Field(() => String, { nullable: true })
14+
username?: string;
1515

16-
@Field(() => String)
17-
image: string;
16+
@Field(() => String, { nullable: true })
17+
bio?: string;
18+
19+
@Field(() => String, { nullable: true })
20+
image?: string;
1821
}
Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1-
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
1+
import {
2+
Args,
3+
Mutation,
4+
Parent,
5+
Query,
6+
ResolveField,
7+
Resolver,
8+
} from '@nestjs/graphql';
29
import { CurrentUser } from '@repo/graphql';
10+
import { Public } from '@repo/nest-common';
11+
import { AuthService } from '../auth/auth.service';
312
import { CreateUserInput, UpdateUserInput } from './dto/user.dto';
413
import { User } from './model/user.model';
514
import { UserService } from './user.service';
615

7-
@Resolver()
16+
@Resolver(() => User)
817
export class UserResolver {
9-
constructor(private readonly userService: UserService) {}
18+
constructor(
19+
private readonly userService: UserService,
20+
private readonly authService: AuthService,
21+
) {}
1022

1123
@Query(() => User)
1224
async currentUser(
@@ -15,13 +27,22 @@ export class UserResolver {
1527
return this.userService.get(user);
1628
}
1729

30+
@Public()
1831
@Mutation(() => User)
1932
async createUser(@Args('user') userInput: CreateUserInput): Promise<User> {
2033
return await this.userService.create(userInput);
2134
}
2235

2336
@Mutation(() => User)
24-
async updateUser(@Args('user') userInput: UpdateUserInput): Promise<User> {
25-
return await this.userService.update(userInput);
37+
async updateUser(
38+
@CurrentUser('id') userId: number,
39+
@Args('user') userInput: UpdateUserInput,
40+
): Promise<User> {
41+
return await this.userService.update(userId, userInput);
42+
}
43+
44+
@ResolveField()
45+
async token(@Parent() user: User) {
46+
return await this.authService.createToken({ id: user.id });
2647
}
2748
}

apps/realworld-graphql/src/modules/user/user.service.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,24 @@ export class UserService {
3939
const newUser = this.userRepository.create({ username, email, password });
4040
const savedUser = await this.userRepository.save(newUser);
4141

42-
return {
43-
...savedUser,
44-
token: await this.authService.createToken({ id: savedUser.id }),
45-
};
42+
return savedUser;
4643
}
4744

48-
async update(dto: UpdateUserInput) {
49-
const user = await this.userRepository.findOneBy({ id: dto.id });
45+
async update(userId: number, dto: UpdateUserInput) {
46+
const user = await this.userRepository.findOneBy({ id: userId });
5047

5148
if (!user) {
5249
throw new ValidationException(ErrorCode.E002);
5350
}
5451

5552
const savedUser = await this.userRepository.save({
56-
id: user.id,
53+
id: userId,
5754
...dto,
5855
});
5956

6057
return {
58+
...user,
6159
...savedUser,
62-
token: await this.authService.createToken({ id: savedUser.id }),
6360
};
6461
}
6562
}

apps/realworld-graphql/src/schema.gql

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
33
# ------------------------------------------------------
44

5+
"""User register request"""
56
input CreateUserInput {
67
email: String!
78
password: String!
@@ -30,17 +31,19 @@ type Query {
3031
getHello: String!
3132
}
3233

34+
"""User update request"""
3335
input UpdateUserInput {
36+
bio: String
3437
email: String
35-
id: Float!
36-
password: String
38+
image: String
3739
username: String
3840
}
3941

4042
type User {
41-
bio: String!
42-
email: String!
43-
image: String!
44-
token: String!
45-
username: String!
43+
bio: String
44+
email: String
45+
id: Float
46+
image: String
47+
token: String
48+
username: String
4649
}

apps/realworldx-api/src/filters/global-exception.filter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ export class GlobalExceptionFilter implements ExceptionFilter {
4545
} else if (exception instanceof HttpException) {
4646
error = handleHttpException(exception);
4747
} else if (exception instanceof QueryFailedError) {
48-
this.logger.error(error);
48+
this.logger.error(exception);
4949
error = this.handleQueryFailedError(exception);
5050
} else if (exception instanceof EntityNotFoundError) {
5151
this.logger.debug(exception);
5252
error = this.handleEntityNotFoundError(exception);
5353
} else {
54-
this.logger.error(error);
54+
this.logger.error(exception);
5555
error = handleError(exception);
5656
}
5757

packages/graphql/src/decorators/field.decorators.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ export function NumberField(
7777
export function NumberFieldOptional(
7878
options: Omit<FieldOptions, 'nullable'> & INumberFieldOptions = {},
7979
): PropertyDecorator {
80-
return NumberField({ nullable: true, ...options });
80+
return applyDecorators(
81+
IsOptional({ each: options.each }),
82+
NumberField({ nullable: true, ...options }),
83+
);
8184
}
8285

8386
export function StringField(
@@ -108,7 +111,10 @@ export function StringField(
108111
export function StringFieldOptional(
109112
options: Omit<FieldOptions, 'nullable'> & IStringFieldOptions = {},
110113
): PropertyDecorator {
111-
return StringField({ nullable: true, ...options });
114+
return applyDecorators(
115+
IsOptional({ each: options.each }),
116+
StringField({ nullable: true, ...options }),
117+
);
112118
}
113119

114120
export function TokenField(
@@ -136,7 +142,10 @@ export function PasswordField(
136142
export function PasswordFieldOptional(
137143
options: Omit<FieldOptions, 'nullable'> & IStringFieldOptions = {},
138144
): PropertyDecorator {
139-
return PasswordField({ nullable: true, ...options });
145+
return applyDecorators(
146+
IsOptional({ each: options.each }),
147+
PasswordField({ nullable: true, ...options }),
148+
);
140149
}
141150

142151
export function BooleanField(
@@ -157,7 +166,10 @@ export function BooleanField(
157166
export function BooleanFieldOptional(
158167
options: Omit<FieldOptions, 'nullable'> & IBooleanFieldOptions = {},
159168
): PropertyDecorator {
160-
return BooleanField({ nullable: true, ...options });
169+
return applyDecorators(
170+
IsOptional({ each: options.each }),
171+
BooleanField({ nullable: true, ...options }),
172+
);
161173
}
162174

163175
export function EmailField(
@@ -173,7 +185,10 @@ export function EmailField(
173185
export function EmailFieldOptional(
174186
options: Omit<FieldOptions, 'nullable'> & IStringFieldOptions = {},
175187
): PropertyDecorator {
176-
return EmailField({ nullable: true, ...options });
188+
return applyDecorators(
189+
IsOptional({ each: options.each }),
190+
EmailField({ nullable: true, ...options }),
191+
);
177192
}
178193

179194
export function UUIDField(
@@ -189,7 +204,10 @@ export function UUIDField(
189204
export function UUIDFieldOptional(
190205
options: Omit<FieldOptions, 'nullable'> & IFieldOptions = {},
191206
): PropertyDecorator {
192-
return UUIDField({ nullable: true, ...options });
207+
return applyDecorators(
208+
IsOptional({ each: options.each }),
209+
UUIDField({ nullable: true, ...options }),
210+
);
193211
}
194212

195213
export function URLField(
@@ -207,7 +225,10 @@ export function URLField(
207225
export function URLFieldOptional(
208226
options: Omit<FieldOptions, 'nullable'> & IStringFieldOptions = {},
209227
): PropertyDecorator {
210-
return URLField({ nullable: true, ...options });
228+
return applyDecorators(
229+
IsOptional({ each: options.each }),
230+
URLField({ nullable: true, ...options }),
231+
);
211232
}
212233

213234
export function DateField(
@@ -223,7 +244,10 @@ export function DateField(
223244
export function DateFieldOptional(
224245
options: Omit<FieldOptions, 'nullable'> & IFieldOptions = {},
225246
): PropertyDecorator {
226-
return DateField({ ...options, nullable: true });
247+
return applyDecorators(
248+
IsOptional({ each: options.each }),
249+
DateField({ ...options, nullable: true }),
250+
);
227251
}
228252

229253
export function EnumField<TEnum extends object>(

0 commit comments

Comments
 (0)