chore: add user profile to org user endpoints apiv2 (#18039)
This commit is contained in:
+27
-7
@@ -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" }
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+13
-3
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user