Skip to content

Commit

Permalink
Implement API update user
Browse files Browse the repository at this point in the history
  • Loading branch information
lethemanh committed Jan 3, 2025
1 parent 57dfbaf commit c0ccdab
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 12 deletions.
52 changes: 50 additions & 2 deletions tdrive/backend/node/src/services/user/services/users/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { isNumber, isString } from "lodash";
import NodeCache from "node-cache";
import gr from "../../../global-resolver";
import { TYPE as DriveFileType, DriveFile } from "../../../documents/entities/drive-file";
import { UpdateUser } from "./types";

export class UserServiceImpl {
version: "1";
Expand Down Expand Up @@ -77,6 +78,24 @@ export class UserServiceImpl {
}
}

private async updateExtRepositoryInCaseChangeEmail(user: User, context?: ExecutionContext) {
if (user.identity_provider_id === "null") {
return;
}

const extUser = await this.extUserRepository.findOne({ user_id: user.id }, {}, context);
if (extUser) {
await this.extUserRepository.remove(extUser, context);
}

const newExtUser = getExternalUserInstance({
service_id: user.identity_provider || "null",
external_id: user.identity_provider_id || "null",
user_id: user.id,
});
await this.extUserRepository.save(newExtUser, context);
}

private assignDefaults(user: User) {
user.creation_date = !isNumber(user.creation_date) ? Date.now() : user.creation_date;
if (user.identity_provider_id && !user.identity_provider) user.identity_provider = "console";
Expand All @@ -92,8 +111,37 @@ export class UserServiceImpl {
return new CreateResult("user", user);
}

update(pk: Partial<User>, item: User, context?: ExecutionContext): Promise<UpdateResult<User>> {
throw new Error("Method not implemented.");
async update(
instance: User,
user: UpdateUser,
context?: ExecutionContext,
): Promise<UpdateResult<User>> {
let isChangeEmail = false;
if (instance.email_canonical !== user.email) {
isChangeEmail = true;
}

instance.email_canonical = user.email || instance.email_canonical;
instance.first_name = user.first_name || instance.first_name;
instance.last_name = user.last_name || instance.last_name;
instance.picture = user.picture || instance.picture;
instance.creation_date = !isNumber(instance.creation_date)
? Date.now()
: instance.creation_date;
if (isChangeEmail) {
instance.username_canonical = (
user.email.replace(/[^a-z0-9_-]/, "") || ""
).toLocaleLowerCase();
instance.identity_provider_id =
instance.identity_provider_id !== "null" ? user.email : "null";
}

await this.repository.save(instance, context);
if (isChangeEmail) {
await this.updateExtRepositoryInCaseChangeEmail(instance, context);
}

return new UpdateResult("user", instance);
}

async save(user: User, context?: ExecutionContext): Promise<SaveResult<User>> {
Expand Down
7 changes: 7 additions & 0 deletions tdrive/backend/node/src/services/user/services/users/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@ export type SearchUserOptions = {
workspaceId?: string;
channelId?: string;
};

export type UpdateUser = {
email: string;
picture: string;
first_name: string;
last_name: string;
};
30 changes: 30 additions & 0 deletions tdrive/backend/node/src/services/user/web/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { formatUser } from "../../../utils/users";
import gr from "../../global-resolver";
import config from "config";
import { getLogger } from "../../../core/platform/framework";
import { UpdateUser } from "../services/users/types";

export class UsersCrudController
implements
Expand Down Expand Up @@ -366,6 +367,35 @@ export class UsersCrudController
used: quota,
} as UserQuota;
}

async update(
request: FastifyRequest<{ Body: UpdateUser; Params: UserParameters }>,
reply: FastifyReply,
): Promise<ResourceCreateResponse<UserObject>> {
const context = getExecutionContext(request);

const id = request.params.id;
const user = await gr.services.users.get({ id });
if (!user) {
reply.notFound(`User ${id} not found`);
return;
}

const body = { ...request.body };
if (user.email_canonical !== body.email) {
const userByEmail = await gr.services.users.getByEmail(body.email, context);
if (userByEmail) {
reply.conflict(`Email ${body.email} is existed`);
return;
}
}

await gr.services.users.update(user, body, context);

return {
resource: await formatUser(user),
};
}
}

function getExecutionContext(request: FastifyRequest): ExecutionContext {
Expand Down
7 changes: 7 additions & 0 deletions tdrive/backend/node/src/services/user/web/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, options, next)
handler: usersController.recent.bind(usersController),
});

fastify.route({
method: "PUT",
url: `${usersUrl}/:id`,
preValidation: [fastify.authenticateOptional],
handler: usersController.update.bind(usersController),
});

next();
};

Expand Down
98 changes: 88 additions & 10 deletions tdrive/backend/node/test/e2e/users/users.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,11 @@ describe("The /users API", () => {
username: "adminuser",
firstName: "admin",
});
await testDbService.createUser([workspacePk]);

await testDbService.createUser([workspacePk], { password: "123456" });
});

afterAll(async () => {
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
afterAll(async () => {});

describe("The GET /users/:id route", () => {
it("should 401 when not authenticated", async () => {
Expand Down Expand Up @@ -136,7 +135,6 @@ describe("The /users API", () => {
}),
]),
);

});

it("should 200 and short response for another user", async () => {
Expand Down Expand Up @@ -222,7 +220,7 @@ describe("The /users API", () => {
},
query: {
search: "anon",
company_ids: oneUser.workspace.company_id
company_ids: oneUser.workspace.company_id,
},
});

Expand All @@ -232,9 +230,7 @@ describe("The /users API", () => {
const resources = json.resources;
console.log(resources);
expect(resources.length).toBe(0);

});

});

describe("The GET /users/:user_id/companies route", () => {
Expand Down Expand Up @@ -354,7 +350,6 @@ describe("The /users API", () => {
total_guests: expect.any(Number),
total_messages: expect.any(Number),
});

});
});

Expand Down Expand Up @@ -473,7 +468,6 @@ describe("The /users API", () => {

user = await testDbService.getUserFromDb({ id: firstId });
expect(user.devices).toMatchObject([]);

});
});
describe("List registered devices (GET)", () => {
Expand Down Expand Up @@ -545,8 +539,92 @@ describe("The /users API", () => {
expect(user.devices).toMatchObject([]);
const device = await testDbService.getDeviceFromDb(deviceToken);
expect(device).toBeFalsy();
});
});
});

describe("The PUT /users/:id route", () => {
it("should 200 and response for updated user", async () => {
const myId = testDbService.users[0].id;
const updatedUserId = testDbService.users[1].id;

const jwtToken = await platform.auth.getJWTToken({ sub: myId });
const response = await platform.app.inject({
method: "PUT",
url: `${url}/users/${updatedUserId}`,
headers: {
authorization: `Bearer ${jwtToken}`,
},
body: {
email: "[email protected]",
first_name: "New",
last_name: "Value",
picture: "null",
},
});

expect(response.statusCode).toBe(200);

const resource = response.json()["resource"];

console.log("resource: ", resource);

expect(resource).toMatchObject({
id: updatedUserId,
provider: expect.any(String),
provider_id: expect.any(String),
email: expect.any(String),
username: expect.any(String),
is_verified: expect.any(Boolean),
picture: expect.any(String),
first_name: expect.any(String),
last_name: expect.any(String),
full_name: expect.any(String),
created_at: expect.any(Number),
deleted: expect.any(Boolean),
status: null,
last_activity: null,
cache: { companies: expect.any(Array) },
});

expect(resource).not.toMatchObject({
locale: expect.anything(),
timezone: expect.anything(),
companies: expect.anything(),
});
});

it("should login with new email", async () => {
const myId = testDbService.users[0].id;
const updatedUserId = testDbService.users[1].id;

const jwtToken = await platform.auth.getJWTToken({ sub: myId });
await platform.app.inject({
method: "PUT",
url: `${url}/users/${updatedUserId}`,
headers: {
authorization: `Bearer ${jwtToken}`,
},
body: {
email: "[email protected]",
first_name: "New",
last_name: "Value",
picture: "null",
},
});

const response = await platform.app.inject({
method: "POST",
url: "/internal/services/console/v1/login",
body: {
email: "[email protected]",
password: "123456",
remember_me: true,
device: {},
},
});

expect(response.statusCode).toBe(200);
});
});
});

0 comments on commit c0ccdab

Please sign in to comment.