Skip to content

Commit

Permalink
feat: user update
Browse files Browse the repository at this point in the history
  • Loading branch information
n9mi committed Sep 13, 2024
1 parent d73c7c4 commit e95068d
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 23 deletions.
83 changes: 67 additions & 16 deletions __tests__/user.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ describe("GET /user/info", () => {
let token: string = "";

beforeAll(async () => {
token = await UserTestUtil.getToken();
await UserTestUtil.create(UserTestUtil.user);
token = await UserTestUtil.getToken(UserTestUtil.user.username, UserTestUtil.user.password);
});

afterAll(async () => {
await UserTestUtil.delete();
});

it("should return 200 - get user info", async () => {
it("should return 200 - success get user info", async () => {
const res = await supertest(web)
.get(`${basePath}/user/info`)
.set('Authorization', `Bearer ${token}`);
Expand All @@ -25,6 +26,62 @@ describe("GET /user/info", () => {
expect(res.body.data.username).toBe(UserTestUtil.user.username);
expect(res.body.data.name).toBe(UserTestUtil.user.name);
});

it("should return 401 - unauthorized", async () => {
const res = await supertest(web)
.get(`${basePath}/user/info`);

logger.debug(res.body);
expect(res.status).toBe(401);
expect(res.body.errors).toBeDefined();
});
});

describe("PUT /user/update", () => {
const newUserData = {
name: "user update",
username: "user_update",
password: "userupdate"
};

afterAll(() => {
UserTestUtil.delete();
});

it ("should return 200 - success updating user info", async () => {
await UserTestUtil.create(UserTestUtil.user);
const token = await UserTestUtil.getToken(UserTestUtil.user.username, UserTestUtil.user.password);

const resUpdate = await supertest(web)
.put(`${basePath}/user/update`)
.send(newUserData)
.set('Authorization', `Bearer ${token}`);

logger.debug("update response : ", resUpdate.body);
expect(resUpdate.status).toBe(200);
expect(resUpdate.body.status).toBe("success");

const resLoginWithNewCred = await supertest(web)
.post(`${basePath}/auth/login`)
.send({
username: newUserData.username,
password: newUserData.password
});

logger.debug("login response : ", resLoginWithNewCred.body);
expect(resLoginWithNewCred.status).toBe(200);
expect(resLoginWithNewCred.body.data.token).toBeDefined();
});

it("should return 401 - unauthorized", async () => {
const resUpdate = await supertest(web)
.put(`${basePath}/user/update`)
.send(newUserData);

logger.debug("update response : ", resUpdate.body);
expect(resUpdate.status).toBe(401);
expect(resUpdate.body.errors).toBeDefined();
});
});

class UserTestUtil {
Expand All @@ -34,33 +91,27 @@ class UserTestUtil {
password: "password"
};

static async create() {
static async create(user : { name: string, username: string, password: string }) {
await prisma.user.create({
data: {
name: UserTestUtil.user.name,
username: UserTestUtil.user.username,
password: await bcrypt.hash(UserTestUtil.user.password, 10),
name: user.name,
username: user.username,
password: await bcrypt.hash(user.password, 10),
token: ""
}
});
}

static async delete() {
await prisma.user.deleteMany({
where: {
username: UserTestUtil.user.username
}
});
await prisma.user.deleteMany({});
}

static async getToken() {
await UserTestUtil.create();

static async getToken(username: string, password: string) {
const loginRes = await supertest(web)
.post(`${basePath}/auth/login`)
.send({
username: UserTestUtil.user.username,
password: UserTestUtil.user.password,
username: username,
password: password,
});

return loginRes.body.data.token;
Expand Down
12 changes: 12 additions & 0 deletions src/controller/user.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Request, Response, NextFunction } from "express";
import UserService from "../service/user";
import { UserUpdateRequest } from "../model/user";

export class UserController {

Expand All @@ -15,4 +16,15 @@ export class UserController {
next(e);
}
}

static async update(req: Request, res: Response, next: NextFunction) {
try {
const updateRes = await UserService.updateUser(res.locals.user.username, req.body as UserUpdateRequest);

res.status(200)
.json(updateRes);
} catch (e) {
next(e);
}
}
}
6 changes: 6 additions & 0 deletions src/model/user.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
export type UserInfoResponse = {
username: string,
name: string
}

export type UserUpdateRequest = {
username?: string,
name?: string,
password?: string
}
1 change: 1 addition & 0 deletions src/router/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const getUserRouter = (basePath: string) => {
const userRouter = express.Router();
userRouter.use(accessValidation);
userRouter.get(`${basePath}/user/info`, UserController.info);
userRouter.put(`${basePath}/user/update`, UserController.update);

return userRouter;
}
46 changes: 39 additions & 7 deletions src/service/user.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import prisma from "../application/database";
import { ResponseError } from "../error/response";
import { UserInfoResponse } from "../model/user";
import { StatusResponse } from "../model/common";
import { UserInfoResponse, UserUpdateRequest } from "../model/user";
import bcrypt from "bcrypt";
import { Validation } from "../validation/validation";
import { UserValidation } from "../validation/user";

export default class UserService {
static async getUserInfo(username: string) : Promise<UserInfoResponse> {
static async getUserInfo(username: string): Promise<UserInfoResponse> {
const user = await prisma.user.findFirstOrThrow({
where: {
username: {
Expand All @@ -12,15 +16,43 @@ export default class UserService {
}
}
}).catch((e) => { throw new ResponseError(404, "user not found") });

console.info({
username: user.username,
name: user.name
});

return {
username: user.username,
name: user.name
}
}

static async updateUser(username: string, req: UserUpdateRequest): Promise<StatusResponse> {
const updateReq = Validation.validate(UserValidation.INFO, req);

const isExists = await prisma.user.count({
where: {
username: {
equals: username,
mode: 'insensitive'
}
}
}) > 0;
if (!isExists) {
throw new ResponseError(404, "user not found");
}

if (req.password !== undefined) {
updateReq.password = await bcrypt.hash(req.password, 10);
}

console.info(updateReq);

await prisma.user.update({
where: {
username: username,
},
data: updateReq
});

return {
status: "success"
}
}
}
27 changes: 27 additions & 0 deletions src/validation/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { z, ZodType } from "zod";

export class UserValidation {
static readonly INFO: ZodType = z.object({
username: z.string({
invalid_type_error: "username should be string",
}).min(4, {
message: "username should be more than 4 characters"
}).max(100, {
message: "username should less than 100 characters"
}).optional(),
name: z.string({
invalid_type_error: "name should be string",
}).min(1, {
message: "name should be more than 1 characters"
}).max(100, {
message: "name should less than 100 characters"
}).optional(),
password: z.string({
invalid_type_error: "password should be string",
}).min(6, {
message: "password should be more than 6 characters"
}).max(100, {
message: "password should less than 100 characters"
}).optional(),
})
}

0 comments on commit e95068d

Please sign in to comment.