chore: add user profile to org user endpoints apiv2 (#18039)

This commit is contained in:
Morgan
2024-12-09 10:21:28 +02:00
committed by GitHub
parent be3e580107
commit 3b1123a29a
6 changed files with 210 additions and 62 deletions
@@ -12,10 +12,12 @@ import { IsUserInOrg } from "@/modules/auth/guards/users/is-user-in-org.guard";
import { CreateOrganizationUserInput } from "@/modules/organizations/inputs/create-organization-user.input";
import { GetOrganizationsUsersInput } from "@/modules/organizations/inputs/get-organization-users.input";
import { UpdateOrganizationUserInput } from "@/modules/organizations/inputs/update-organization-user.input";
import { GetOrganizationUsersOutput } from "@/modules/organizations/outputs/get-organization-users.output";
import {
GetOrganizationUsersResponseDTO,
GetOrgUsersWithProfileOutput,
} from "@/modules/organizations/outputs/get-organization-users.output";
import { GetOrganizationUserOutput } from "@/modules/organizations/outputs/get-organization-users.output";
import { OrganizationsUsersService } from "@/modules/organizations/services/organizations-users-service";
import { GetUserOutput } from "@/modules/users/outputs/get-users.output";
import { UserWithProfile } from "@/modules/users/users.repository";
import {
Controller,
@@ -55,7 +57,7 @@ export class OrganizationsUsersController {
async getOrganizationsUsers(
@Param("orgId", ParseIntPipe) orgId: number,
@Query() query: GetOrganizationsUsersInput
): Promise<GetOrganizationUsersOutput> {
): Promise<GetOrganizationUsersResponseDTO> {
const users = await this.organizationsUsersService.getUsers(
orgId,
query.emails,
@@ -65,7 +67,13 @@ export class OrganizationsUsersController {
return {
status: SUCCESS_STATUS,
data: users.map((user) => plainToInstance(GetUserOutput, user, { strategy: "excludeAll" })),
data: users.map((user) =>
plainToInstance(
GetOrgUsersWithProfileOutput,
{ ...user, profile: user?.profiles?.[0] ?? {} },
{ strategy: "excludeAll" }
)
),
};
}
@@ -86,7 +94,11 @@ export class OrganizationsUsersController {
);
return {
status: SUCCESS_STATUS,
data: plainToInstance(GetUserOutput, user, { strategy: "excludeAll" }),
data: plainToInstance(
GetOrgUsersWithProfileOutput,
{ ...user, profile: user?.profiles?.[0] ?? {} },
{ strategy: "excludeAll" }
),
};
}
@@ -104,7 +116,11 @@ export class OrganizationsUsersController {
const user = await this.organizationsUsersService.updateUser(orgId, userId, input);
return {
status: SUCCESS_STATUS,
data: plainToInstance(GetUserOutput, user, { strategy: "excludeAll" }),
data: plainToInstance(
GetOrgUsersWithProfileOutput,
{ ...user, profile: user?.profiles?.[0] ?? {} },
{ strategy: "excludeAll" }
),
};
}
@@ -120,7 +136,11 @@ export class OrganizationsUsersController {
const user = await this.organizationsUsersService.deleteUser(orgId, userId);
return {
status: SUCCESS_STATUS,
data: plainToInstance(GetUserOutput, user, { strategy: "excludeAll" }),
data: plainToInstance(
GetOrgUsersWithProfileOutput,
{ ...user, profile: user?.profiles?.[0] ?? {} },
{ strategy: "excludeAll" }
),
};
}
}
@@ -1,6 +1,7 @@
import { bootstrap } from "@/app";
import { AppModule } from "@/app.module";
import { EmailService } from "@/modules/email/email.service";
import { GetOrgUsersWithProfileOutput } from "@/modules/organizations/outputs/get-organization-users.output";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { TokensModule } from "@/modules/tokens/tokens.module";
import { UsersModule } from "@/modules/users/users.module";
@@ -233,10 +234,18 @@ describe("Organizations Users Endpoints", () => {
it("should get all org users", async () => {
const { body } = await request(app.getHttpServer()).get(`/v2/organizations/${org.id}/users`);
const userData = body.data;
const userData = body.data as GetOrgUsersWithProfileOutput[];
expect(body.status).toBe(SUCCESS_STATUS);
expect(userData.length).toBe(4);
console.log(
"profiles",
{ userData },
userData.map((u) => u.profile)
);
expect(userData.find((u) => u.profile.username === "member1@org.com")).toBeDefined();
expect(userData.find((u) => u.profile.username === "member2@org.com")).toBeDefined();
expect(userData.find((u) => u.profile.username === "member3@org.com")).toBeDefined();
expect(userData.filter((user: { email: string }) => user.email === nonMemberEmail).length).toBe(0);
});
@@ -250,12 +259,13 @@ describe("Organizations Users Endpoints", () => {
.set("Content-Type", "application/json")
.set("Accept", "application/json");
const userData = body.data;
const userData = body.data as GetOrgUsersWithProfileOutput[];
expect(body.status).toBe(SUCCESS_STATUS);
expect(userData.length).toBe(1);
expect(userData.filter((user: { email: string }) => user.email === userEmail).length).toBe(1);
expect(userData.filter((user) => user.email === userEmail).length).toBe(1);
expect(userData.find((u) => u.profile.username === user.username)).toBeDefined();
});
it("should get users within the specified emails array", async () => {
@@ -1,20 +1,64 @@
import { GetUserOutput } from "@/modules/users/outputs/get-users.output";
import { ApiProperty } from "@nestjs/swagger";
import { IsEnum } from "class-validator";
import { Expose, Type } from "class-transformer";
import { IsEnum, IsInt, IsString, ValidateNested, IsArray } from "class-validator";
import { ERROR_STATUS } from "@calcom/platform-constants";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
export class GetOrganizationUsersOutput {
export class ProfileOutput {
@IsInt()
@Expose()
@ApiProperty({ type: Number, required: true, description: "The ID of the profile of user", example: 1 })
id!: number;
@IsInt()
@Expose()
@ApiProperty({
type: Number,
required: true,
description: "The ID of the organization of user",
example: 1,
})
organizationId!: number;
@IsInt()
@Expose()
@ApiProperty({ type: Number, required: true, description: "The IDof the user", example: 1 })
userId!: number;
@IsString()
@Expose()
@ApiProperty({
type: String,
nullable: true,
required: false,
description: "The username of the user within the organization context",
example: "john_doe",
})
username!: string;
}
export class GetOrgUsersWithProfileOutput extends GetUserOutput {
@ApiProperty({
description: "organization user profile, contains user data within the organizaton context",
})
@Expose()
@ValidateNested()
@IsArray()
@Type(() => ProfileOutput)
profile!: ProfileOutput;
}
export class GetOrganizationUsersResponseDTO {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS])
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
data!: GetUserOutput[];
data!: GetOrgUsersWithProfileOutput[];
}
export class GetOrganizationUserOutput {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS])
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
data!: GetUserOutput;
data!: GetOrgUsersWithProfileOutput;
}
@@ -24,6 +24,13 @@ export class OrganizationsUsersRepository {
...this.filterOnOrgMembership(orgId),
...(emailArray && emailArray.length ? { email: { in: emailArray } } : {}),
},
include: {
profiles: {
where: {
organizationId: orgId,
},
},
},
skip,
take,
});
@@ -35,6 +42,13 @@ export class OrganizationsUsersRepository {
username,
...this.filterOnOrgMembership(orgId),
},
include: {
profiles: {
where: {
organizationId: orgId,
},
},
},
});
}
@@ -44,6 +58,13 @@ export class OrganizationsUsersRepository {
email,
...this.filterOnOrgMembership(orgId),
},
include: {
profiles: {
where: {
organizationId: orgId,
},
},
},
});
}
@@ -62,6 +83,13 @@ export class OrganizationsUsersRepository {
organizationId: orgId,
},
data: updateUserBody,
include: {
profiles: {
where: {
organizationId: orgId,
},
},
},
});
}
@@ -71,6 +99,13 @@ export class OrganizationsUsersRepository {
id: userId,
organizationId: orgId,
},
include: {
profiles: {
where: {
organizationId: orgId,
},
},
},
});
}
}
+46 -24
View File
@@ -2548,7 +2548,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetOrganizationUsersOutput"
"$ref": "#/components/schemas/GetOrganizationUsersResponseDTO"
}
}
}
@@ -4176,15 +4176,6 @@
"type": "string"
}
},
{
"name": "eventTypeId",
"required": false,
"in": "query",
"description": "Id of the event-type, if this is provided no other parameter is required",
"schema": {
"type": "number"
}
},
{
"name": "usernames",
"required": false,
@@ -4194,15 +4185,6 @@
"type": "string"
}
},
{
"name": "userId",
"required": false,
"in": "query",
"description": "ID of the user you want the get the event-types of, username is not needed when using this parameter",
"schema": {
"type": "number"
}
},
{
"name": "orgSlug",
"required": false,
@@ -9784,7 +9766,38 @@
"status"
]
},
"GetUserOutput": {
"ProfileOutput": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The ID of the profile of user",
"example": 1
},
"organizationId": {
"type": "number",
"description": "The ID of the organization of user",
"example": 1
},
"userId": {
"type": "number",
"description": "The IDof the user",
"example": 1
},
"username": {
"type": "string",
"nullable": true,
"description": "The username of the user within the organization context",
"example": "john_doe"
}
},
"required": [
"id",
"organizationId",
"userId"
]
},
"GetOrgUsersWithProfileOutput": {
"type": "object",
"properties": {
"id": {
@@ -9908,6 +9921,14 @@
"nullable": true,
"description": "The ID of the user who invited this user",
"example": 1
},
"profile": {
"description": "organization user profile, contains user data within the organizaton context",
"allOf": [
{
"$ref": "#/components/schemas/ProfileOutput"
}
]
}
},
"required": [
@@ -9916,10 +9937,11 @@
"timeZone",
"weekStart",
"hideBranding",
"createdDate"
"createdDate",
"profile"
]
},
"GetOrganizationUsersOutput": {
"GetOrganizationUsersResponseDTO": {
"type": "object",
"properties": {
"status": {
@@ -9933,7 +9955,7 @@
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GetUserOutput"
"$ref": "#/components/schemas/GetOrgUsersWithProfileOutput"
}
}
},
@@ -10042,7 +10064,7 @@
]
},
"data": {
"$ref": "#/components/schemas/GetUserOutput"
"$ref": "#/components/schemas/GetOrgUsersWithProfileOutput"
}
},
"required": [
+41 -24
View File
@@ -2421,7 +2421,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetOrganizationUsersOutput"
"$ref": "#/components/schemas/GetOrganizationUsersResponseDTO"
}
}
}
@@ -3960,15 +3960,6 @@
"type": "string"
}
},
{
"name": "eventTypeId",
"required": false,
"in": "query",
"description": "Id of the event-type, if this is provided no other parameter is required",
"schema": {
"type": "number"
}
},
{
"name": "usernames",
"required": false,
@@ -3978,15 +3969,6 @@
"type": "string"
}
},
{
"name": "userId",
"required": false,
"in": "query",
"description": "ID of the user you want the get the event-types of, username is not needed when using this parameter",
"schema": {
"type": "number"
}
},
{
"name": "orgSlug",
"required": false,
@@ -8942,7 +8924,34 @@
},
"required": ["status"]
},
"GetUserOutput": {
"ProfileOutput": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "The ID of the profile of user",
"example": 1
},
"organizationId": {
"type": "number",
"description": "The ID of the organization of user",
"example": 1
},
"userId": {
"type": "number",
"description": "The IDof the user",
"example": 1
},
"username": {
"type": "string",
"nullable": true,
"description": "The username of the user within the organization context",
"example": "john_doe"
}
},
"required": ["id", "organizationId", "userId"]
},
"GetOrgUsersWithProfileOutput": {
"type": "object",
"properties": {
"id": {
@@ -9066,11 +9075,19 @@
"nullable": true,
"description": "The ID of the user who invited this user",
"example": 1
},
"profile": {
"description": "organization user profile, contains user data within the organizaton context",
"allOf": [
{
"$ref": "#/components/schemas/ProfileOutput"
}
]
}
},
"required": ["id", "email", "timeZone", "weekStart", "hideBranding", "createdDate"]
"required": ["id", "email", "timeZone", "weekStart", "hideBranding", "createdDate", "profile"]
},
"GetOrganizationUsersOutput": {
"GetOrganizationUsersResponseDTO": {
"type": "object",
"properties": {
"status": {
@@ -9081,7 +9098,7 @@
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GetUserOutput"
"$ref": "#/components/schemas/GetOrgUsersWithProfileOutput"
}
}
},
@@ -9180,7 +9197,7 @@
"enum": ["success", "error"]
},
"data": {
"$ref": "#/components/schemas/GetUserOutput"
"$ref": "#/components/schemas/GetOrgUsersWithProfileOutput"
}
},
"required": ["status", "data"]