feat: attributes v2 api (#16461)
* WIP * services * controllers + outputs * unit tests for repositories * use hard coded enum type * use prisma import * remove redunant input file * fix packagejson * rename to spec files * fix typo in option service file name * move membership call to org membership service * move logic to service > repo * fix membership query * attribute controller e2e spec * add options e2e test * add jest watch for e2e --------- Co-authored-by: Morgan Vernay <morgan@cal.com>
This commit is contained in:
co-authored by
Morgan Vernay
parent
36e79bf69c
commit
2de33fe1bf
@@ -12,6 +12,7 @@
|
||||
"dev:build:watch": "yarn workspace @calcom/platform-constants build:watch & yarn workspace @calcom/platform-utils build:watch & yarn workspace @calcom/platform-types build:watch",
|
||||
"dev:build": "yarn workspace @calcom/platform-constants build && yarn workspace @calcom/platform-enums build && yarn workspace @calcom/platform-utils build && yarn workspace @calcom/platform-types build ",
|
||||
"dev": "yarn dev:build && docker-compose up -d && yarn copy-swagger-module && yarn start --watch",
|
||||
"dev:no-docker": "yarn dev:build && yarn copy-swagger-module && yarn start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node ./dist/apps/api/v2/src/main.js",
|
||||
"test": "yarn dev:build && jest",
|
||||
@@ -19,6 +20,7 @@
|
||||
"test:cov": "yarn dev:build && jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "yarn dev:build && jest --runInBand --detectOpenHandles --forceExit --config ./jest-e2e.json",
|
||||
"test:e2e:watch": "yarn dev:build && jest --runInBand --detectOpenHandles --forceExit --config ./jest-e2e.json --watch",
|
||||
"prisma": "yarn workspace @calcom/prisma prisma",
|
||||
"generate-schemas": "yarn prisma generate && yarn prisma format",
|
||||
"copy-swagger-module": "ts-node -r tsconfig-paths/register swagger/copy-swagger-module.ts"
|
||||
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
import { bootstrap } from "@/app";
|
||||
import { AppModule } from "@/app.module";
|
||||
import { AssignOrganizationAttributeOptionToUserInput } from "@/modules/organizations/inputs/attributes/assign/organizations-attributes-options-assign.input";
|
||||
import { CreateOrganizationAttributeOptionInput } from "@/modules/organizations/inputs/attributes/options/create-organization-attribute-option.input";
|
||||
import { UpdateOrganizationAttributeOptionInput } from "@/modules/organizations/inputs/attributes/options/update-organizaiton-attribute-option.input.ts";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { TokensModule } from "@/modules/tokens/tokens.module";
|
||||
import { UsersModule } from "@/modules/users/users.module";
|
||||
import { INestApplication } from "@nestjs/common";
|
||||
import { NestExpressApplication } from "@nestjs/platform-express";
|
||||
import { Test } from "@nestjs/testing";
|
||||
import * as request from "supertest";
|
||||
import { AttributeRepositoryFixture } from "test/fixtures/repository/attributes.repository.fixture";
|
||||
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
|
||||
import { OrganizationRepositoryFixture } from "test/fixtures/repository/organization.repository.fixture";
|
||||
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
|
||||
import { withApiAuth } from "test/utils/withApiAuth";
|
||||
|
||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import { User, Team, Membership } from "@calcom/prisma/client";
|
||||
|
||||
describe("Organizations Attributes Options Endpoints", () => {
|
||||
describe("User lacks required role", () => {
|
||||
let app: INestApplication;
|
||||
let userRepositoryFixture: UserRepositoryFixture;
|
||||
let organizationsRepositoryFixture: OrganizationRepositoryFixture;
|
||||
let membershipFixtures: MembershipRepositoryFixture;
|
||||
|
||||
const userEmail = "member@attributes-options-api.com";
|
||||
let user: User;
|
||||
let org: Team;
|
||||
let membership: Membership;
|
||||
let attributeId: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await withApiAuth(
|
||||
userEmail,
|
||||
Test.createTestingModule({
|
||||
imports: [AppModule, PrismaModule, UsersModule, TokensModule],
|
||||
})
|
||||
).compile();
|
||||
|
||||
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
|
||||
organizationsRepositoryFixture = new OrganizationRepositoryFixture(moduleRef);
|
||||
membershipFixtures = new MembershipRepositoryFixture(moduleRef);
|
||||
|
||||
org = await organizationsRepositoryFixture.create({
|
||||
name: "AttributesOptionsCorp",
|
||||
isOrganization: true,
|
||||
});
|
||||
|
||||
user = await userRepositoryFixture.create({
|
||||
email: userEmail,
|
||||
username: userEmail,
|
||||
organization: { connect: { id: org.id } },
|
||||
});
|
||||
|
||||
membership = await membershipFixtures.addUserToOrg(user, org, "MEMBER", true);
|
||||
|
||||
// Create an attribute for testing
|
||||
attributeId = "test-attribute-id";
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
bootstrap(app as NestExpressApplication);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it("should not be able to create attribute option", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/organizations/${org.id}/attributes/${attributeId}/options`)
|
||||
.send({
|
||||
name: "Option 1",
|
||||
value: "option1",
|
||||
})
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it("should not be able to delete attribute option", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.delete(`/v2/organizations/${org.id}/attributes/${attributeId}/options/1`)
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await membershipFixtures.delete(membership.id);
|
||||
await userRepositoryFixture.deleteByEmail(user.email);
|
||||
await organizationsRepositoryFixture.delete(org.id);
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe("User has required role", () => {
|
||||
let app: INestApplication;
|
||||
let userRepositoryFixture: UserRepositoryFixture;
|
||||
let organizationsRepositoryFixture: OrganizationRepositoryFixture;
|
||||
let membershipFixtures: MembershipRepositoryFixture;
|
||||
let attributeRepositoryFixture: AttributeRepositoryFixture;
|
||||
|
||||
const userEmail = "admin@attributes-options-api.com";
|
||||
let user: User;
|
||||
let org: Team;
|
||||
let membership: Membership;
|
||||
let attributeId: string;
|
||||
let createdOption: any;
|
||||
|
||||
const createOptionInput: CreateOrganizationAttributeOptionInput = {
|
||||
value: "option1",
|
||||
slug: "option1",
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await withApiAuth(
|
||||
userEmail,
|
||||
Test.createTestingModule({
|
||||
imports: [AppModule, PrismaModule, UsersModule, TokensModule],
|
||||
})
|
||||
).compile();
|
||||
|
||||
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
|
||||
organizationsRepositoryFixture = new OrganizationRepositoryFixture(moduleRef);
|
||||
membershipFixtures = new MembershipRepositoryFixture(moduleRef);
|
||||
attributeRepositoryFixture = new AttributeRepositoryFixture(moduleRef);
|
||||
|
||||
org = await organizationsRepositoryFixture.create({
|
||||
name: "AttributesOptionsCorp",
|
||||
isOrganization: true,
|
||||
});
|
||||
|
||||
user = await userRepositoryFixture.create({
|
||||
email: userEmail,
|
||||
username: userEmail,
|
||||
organization: { connect: { id: org.id } },
|
||||
});
|
||||
|
||||
membership = await membershipFixtures.addUserToOrg(user, org, "ADMIN", true);
|
||||
|
||||
// Create an attribute for testing
|
||||
const attribute = await attributeRepositoryFixture.create({
|
||||
name: "Test Attribute",
|
||||
team: { connect: { id: org.id } },
|
||||
type: "TEXT",
|
||||
slug: "test-attribute",
|
||||
});
|
||||
attributeId = attribute.id;
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
bootstrap(app as NestExpressApplication);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it("should create attribute option", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/organizations/${org.id}/attributes/${attributeId}/options`)
|
||||
.send(createOptionInput)
|
||||
.expect(201)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
createdOption = response.body.data;
|
||||
expect(createdOption.value).toEqual(createOptionInput.value);
|
||||
expect(createdOption.slug).toEqual(createOptionInput.slug);
|
||||
});
|
||||
});
|
||||
|
||||
it("should get attribute options", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get(`/v2/organizations/${org.id}/attributes/${attributeId}/options`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
const options = response.body.data;
|
||||
expect(options.length).toEqual(1);
|
||||
expect(options[0].value).toEqual(createOptionInput.value);
|
||||
expect(options[0].slug).toEqual(createOptionInput.slug);
|
||||
});
|
||||
});
|
||||
|
||||
it("should update attribute option", async () => {
|
||||
const updateOptionInput: UpdateOrganizationAttributeOptionInput = {
|
||||
value: "updated-option-value",
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.patch(`/v2/organizations/${org.id}/attributes/${attributeId}/options/${createdOption.id}`)
|
||||
.send(updateOptionInput)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
const updatedOption = response.body.data;
|
||||
expect(updatedOption.value).toEqual(updateOptionInput.value);
|
||||
});
|
||||
});
|
||||
|
||||
it("should assign attribute option to user", async () => {
|
||||
const assignInput: AssignOrganizationAttributeOptionToUserInput = {
|
||||
attributeId: attributeId,
|
||||
attributeOptionId: createdOption.id,
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/organizations/${org.id}/attributes/options/${user.id}`)
|
||||
.send(assignInput)
|
||||
.expect(201)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
expect(response.body.data).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it("should get attribute options for user", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get(`/v2/organizations/${org.id}/attributes/options/${user.id}`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
const userOptions = response.body.data;
|
||||
expect(userOptions.length).toEqual(1);
|
||||
expect(userOptions[0].id).toEqual(createdOption.id);
|
||||
});
|
||||
});
|
||||
|
||||
it("should unassign attribute option from user", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.delete(`/v2/organizations/${org.id}/attributes/options/${user.id}/${createdOption.id}`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
expect(response.body.data).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it("should delete attribute option", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.delete(`/v2/organizations/${org.id}/attributes/${attributeId}/options/${createdOption.id}`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
expect(response.body.data).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await membershipFixtures.delete(membership.id);
|
||||
await userRepositoryFixture.deleteByEmail(user.email);
|
||||
await organizationsRepositoryFixture.delete(org.id);
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
import { bootstrap } from "@/app";
|
||||
import { AppModule } from "@/app.module";
|
||||
import { CreateOrganizationAttributeInput } from "@/modules/organizations/inputs/attributes/create-organization-attribute.input";
|
||||
import { UpdateOrganizationAttributeInput } from "@/modules/organizations/inputs/attributes/update-organization-attribute.input";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { TokensModule } from "@/modules/tokens/tokens.module";
|
||||
import { UsersModule } from "@/modules/users/users.module";
|
||||
import { INestApplication } from "@nestjs/common";
|
||||
import { NestExpressApplication } from "@nestjs/platform-express";
|
||||
import { Test } from "@nestjs/testing";
|
||||
import * as request from "supertest";
|
||||
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
|
||||
import { OrganizationRepositoryFixture } from "test/fixtures/repository/organization.repository.fixture";
|
||||
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
|
||||
import { withApiAuth } from "test/utils/withApiAuth";
|
||||
|
||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import { User, Team, Membership } from "@calcom/prisma/client";
|
||||
|
||||
describe("Organizations Attributes Endpoints", () => {
|
||||
describe("User lacks required role", () => {
|
||||
let app: INestApplication;
|
||||
let userRepositoryFixture: UserRepositoryFixture;
|
||||
let organizationsRepositoryFixture: OrganizationRepositoryFixture;
|
||||
let membershipFixtures: MembershipRepositoryFixture;
|
||||
|
||||
const userEmail = "member@attributes-api.com";
|
||||
let user: User;
|
||||
let org: Team;
|
||||
let membership: Membership;
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await withApiAuth(
|
||||
userEmail,
|
||||
Test.createTestingModule({
|
||||
imports: [AppModule, PrismaModule, UsersModule, TokensModule],
|
||||
})
|
||||
).compile();
|
||||
|
||||
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
|
||||
organizationsRepositoryFixture = new OrganizationRepositoryFixture(moduleRef);
|
||||
membershipFixtures = new MembershipRepositoryFixture(moduleRef);
|
||||
|
||||
org = await organizationsRepositoryFixture.create({
|
||||
name: "AttributesCorp",
|
||||
isOrganization: true,
|
||||
});
|
||||
|
||||
user = await userRepositoryFixture.create({
|
||||
email: userEmail,
|
||||
username: userEmail,
|
||||
organization: { connect: { id: org.id } },
|
||||
});
|
||||
|
||||
membership = await membershipFixtures.addUserToOrg(user, org, "MEMBER", true);
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
bootstrap(app as NestExpressApplication);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it("should not be able to create attribute for org", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/organizations/${org.id}/attributes`)
|
||||
.send({
|
||||
key: "department",
|
||||
value: "engineering",
|
||||
})
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it("should not be able to delete attribute for org", async () => {
|
||||
return request(app.getHttpServer()).delete(`/v2/organizations/${org.id}/attributes/1`).expect(403);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await membershipFixtures.delete(membership.id);
|
||||
await userRepositoryFixture.deleteByEmail(user.email);
|
||||
await organizationsRepositoryFixture.delete(org.id);
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe("User has required role", () => {
|
||||
let app: INestApplication;
|
||||
let userRepositoryFixture: UserRepositoryFixture;
|
||||
let organizationsRepositoryFixture: OrganizationRepositoryFixture;
|
||||
let membershipFixtures: MembershipRepositoryFixture;
|
||||
|
||||
const userEmail = "admin@attributes-api.com";
|
||||
let user: User;
|
||||
let org: Team;
|
||||
let membership: Membership;
|
||||
|
||||
let createdAttribute: any;
|
||||
|
||||
const createAttributeInput: CreateOrganizationAttributeInput = {
|
||||
name: "department",
|
||||
slug: "department",
|
||||
type: "TEXT",
|
||||
options: [],
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await withApiAuth(
|
||||
userEmail,
|
||||
Test.createTestingModule({
|
||||
imports: [AppModule, PrismaModule, UsersModule, TokensModule],
|
||||
})
|
||||
).compile();
|
||||
|
||||
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
|
||||
organizationsRepositoryFixture = new OrganizationRepositoryFixture(moduleRef);
|
||||
membershipFixtures = new MembershipRepositoryFixture(moduleRef);
|
||||
|
||||
org = await organizationsRepositoryFixture.create({
|
||||
name: "AttributesCorp",
|
||||
isOrganization: true,
|
||||
});
|
||||
|
||||
user = await userRepositoryFixture.create({
|
||||
email: userEmail,
|
||||
username: userEmail,
|
||||
organization: { connect: { id: org.id } },
|
||||
});
|
||||
|
||||
membership = await membershipFixtures.addUserToOrg(user, org, "ADMIN", true);
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
bootstrap(app as NestExpressApplication);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it("should create attribute for org", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/organizations/${org.id}/attributes`)
|
||||
.send(createAttributeInput)
|
||||
.expect(201)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
createdAttribute = response.body.data;
|
||||
expect(createdAttribute.type).toEqual(createAttributeInput.type);
|
||||
expect(createdAttribute.slug).toEqual(createAttributeInput.slug);
|
||||
expect(createdAttribute.enabled).toEqual(createAttributeInput.enabled);
|
||||
});
|
||||
});
|
||||
|
||||
it("should get org attributes", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get(`/v2/organizations/${org.id}/attributes`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
const attributes = response.body.data;
|
||||
expect(attributes.length).toEqual(1);
|
||||
expect(attributes[0].name).toEqual(createAttributeInput.name);
|
||||
expect(attributes[0].slug).toEqual(createAttributeInput.slug);
|
||||
expect(attributes[0].type).toEqual(createAttributeInput.type);
|
||||
expect(attributes[0].enabled).toEqual(createAttributeInput.enabled);
|
||||
});
|
||||
});
|
||||
|
||||
it("should get single org attribute", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get(`/v2/organizations/${org.id}/attributes/${createdAttribute.id}`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
const attribute = response.body.data;
|
||||
expect(attribute.name).toEqual(createAttributeInput.name);
|
||||
expect(attribute.slug).toEqual(createAttributeInput.slug);
|
||||
expect(attribute.type).toEqual(createAttributeInput.type);
|
||||
expect(attribute.enabled).toEqual(createAttributeInput.enabled);
|
||||
});
|
||||
});
|
||||
|
||||
it("should update org attribute", async () => {
|
||||
const updateAttributeInput: UpdateOrganizationAttributeInput = {
|
||||
name: "marketing",
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.patch(`/v2/organizations/${org.id}/attributes/${createdAttribute.id}`)
|
||||
.send(updateAttributeInput)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
const updatedAttribute = response.body.data;
|
||||
expect(updatedAttribute.name).toEqual(updateAttributeInput.name);
|
||||
});
|
||||
});
|
||||
|
||||
it("should delete org attribute", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.delete(`/v2/organizations/${org.id}/attributes/${createdAttribute.id}`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body.status).toEqual(SUCCESS_STATUS);
|
||||
expect(response.body.data).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await membershipFixtures.delete(membership.id);
|
||||
await userRepositoryFixture.deleteByEmail(user.email);
|
||||
await organizationsRepositoryFixture.delete(org.id);
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
||||
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
|
||||
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
|
||||
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
||||
import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.guard";
|
||||
import { IsAdminAPIEnabledGuard } from "@/modules/auth/guards/organizations/is-admin-api-enabled.guard";
|
||||
import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard";
|
||||
import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard";
|
||||
import { AssignOrganizationAttributeOptionToUserInput } from "@/modules/organizations/inputs/attributes/assign/organizations-attributes-options-assign.input";
|
||||
import { CreateOrganizationAttributeOptionInput } from "@/modules/organizations/inputs/attributes/options/create-organization-attribute-option.input";
|
||||
import { UpdateOrganizationAttributeOptionInput } from "@/modules/organizations/inputs/attributes/options/update-organizaiton-attribute-option.input.ts";
|
||||
import {
|
||||
AssignOptionUserOutput,
|
||||
UnassignOptionUserOutput,
|
||||
} from "@/modules/organizations/outputs/attributes/options/assign-option-user.output";
|
||||
import { CreateAttributeOptionOutput } from "@/modules/organizations/outputs/attributes/options/create-option.output";
|
||||
import { DeleteAttributeOptionOutput } from "@/modules/organizations/outputs/attributes/options/delete-option.output";
|
||||
import { GetOptionUserOutput } from "@/modules/organizations/outputs/attributes/options/get-option-user.output";
|
||||
import { GetAllAttributeOptionOutput } from "@/modules/organizations/outputs/attributes/options/get-option.output";
|
||||
import { UpdateAttributeOptionOutput } from "@/modules/organizations/outputs/attributes/options/update-option.output";
|
||||
import { OrganizationAttributeOptionService } from "@/modules/organizations/services/attributes/organization-attributes-option.service";
|
||||
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
|
||||
|
||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
|
||||
@Controller({
|
||||
path: "/v2/organizations/:orgId",
|
||||
version: API_VERSIONS_VALUES,
|
||||
})
|
||||
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
|
||||
export class OrganizationsOptionsAttributesController {
|
||||
constructor(private readonly organizationsAttributesOptionsService: OrganizationAttributeOptionService) {}
|
||||
|
||||
// Creates an attribute option for an attribute
|
||||
@Roles("ORG_ADMIN")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Post("/attributes/:attributeId/options")
|
||||
async createOrganizationAttributeOption(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("attributeId") attributeId: string,
|
||||
@Body() bodyAttribute: CreateOrganizationAttributeOptionInput
|
||||
): Promise<CreateAttributeOptionOutput> {
|
||||
const attributeOption =
|
||||
await this.organizationsAttributesOptionsService.createOrganizationAttributeOption(
|
||||
orgId,
|
||||
attributeId,
|
||||
bodyAttribute
|
||||
);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attributeOption,
|
||||
};
|
||||
}
|
||||
|
||||
// Deletes an attribute option for an attribute
|
||||
@Roles("ORG_ADMIN")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Delete("/attributes/:attributeId/options/:optionId")
|
||||
async deleteOrganizationAttributeOption(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("attributeId") attributeId: string,
|
||||
@Param("optionId") optionId: string
|
||||
): Promise<DeleteAttributeOptionOutput> {
|
||||
const attributeOption =
|
||||
await this.organizationsAttributesOptionsService.deleteOrganizationAttributeOption(
|
||||
orgId,
|
||||
attributeId,
|
||||
optionId
|
||||
);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attributeOption,
|
||||
};
|
||||
}
|
||||
|
||||
// Updates an attribute option for an attribute
|
||||
@Roles("ORG_ADMIN")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Patch("/attributes/:attributeId/options/:optionId")
|
||||
async updateOrganizationAttributeOption(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("attributeId") attributeId: string,
|
||||
@Param("optionId") optionId: string,
|
||||
@Body() bodyAttribute: UpdateOrganizationAttributeOptionInput
|
||||
): Promise<UpdateAttributeOptionOutput> {
|
||||
const attributeOption =
|
||||
await this.organizationsAttributesOptionsService.updateOrganizationAttributeOption(
|
||||
orgId,
|
||||
attributeId,
|
||||
optionId,
|
||||
bodyAttribute
|
||||
);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attributeOption,
|
||||
};
|
||||
}
|
||||
|
||||
// Gets all attribute options for an attribute
|
||||
@Roles("ORG_MEMBER")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Get("/attributes/:attributeId/options")
|
||||
async getOrganizationAttributeOptions(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("attributeId") attributeId: string
|
||||
): Promise<GetAllAttributeOptionOutput> {
|
||||
const attributeOptions = await this.organizationsAttributesOptionsService.getOrganizationAttributeOptions(
|
||||
orgId,
|
||||
attributeId
|
||||
);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attributeOptions,
|
||||
};
|
||||
}
|
||||
|
||||
// Assign attribute option to user
|
||||
@Roles("ORG_ADMIN")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Post("/attributes/options/:userId")
|
||||
async assignOrganizationAttributeOptionToUser(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("userId", ParseIntPipe) userId: number,
|
||||
@Body() bodyAttribute: AssignOrganizationAttributeOptionToUserInput
|
||||
): Promise<AssignOptionUserOutput> {
|
||||
const attributeOption =
|
||||
await this.organizationsAttributesOptionsService.assignOrganizationAttributeOptionToUser(
|
||||
orgId,
|
||||
userId,
|
||||
bodyAttribute
|
||||
);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attributeOption,
|
||||
};
|
||||
}
|
||||
|
||||
// Unassign attribute option from user
|
||||
@Roles("ORG_MEMBER")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Delete("/attributes/options/:userId/:attributeOptionId")
|
||||
async unassignOrganizationAttributeOptionFromUser(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("userId", ParseIntPipe) userId: number,
|
||||
@Param("attributeOptionId") attributeOptionId: string
|
||||
): Promise<UnassignOptionUserOutput> {
|
||||
const attributeOption =
|
||||
await this.organizationsAttributesOptionsService.unassignOrganizationAttributeOptionFromUser(
|
||||
orgId,
|
||||
userId,
|
||||
attributeOptionId
|
||||
);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attributeOption,
|
||||
};
|
||||
}
|
||||
|
||||
@Roles("ORG_MEMBER")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Get("/attributes/options/:userId")
|
||||
async getOrganizationAttributeOptionsForUser(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("userId", ParseIntPipe) userId: number
|
||||
): Promise<GetOptionUserOutput> {
|
||||
const attributeOptions =
|
||||
await this.organizationsAttributesOptionsService.getOrganizationAttributeOptionsForUser(orgId, userId);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attributeOptions,
|
||||
};
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
||||
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
|
||||
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
|
||||
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
||||
import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.guard";
|
||||
import { IsAdminAPIEnabledGuard } from "@/modules/auth/guards/organizations/is-admin-api-enabled.guard";
|
||||
import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard";
|
||||
import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard";
|
||||
import { CreateOrganizationAttributeInput } from "@/modules/organizations/inputs/attributes/create-organization-attribute.input";
|
||||
import { UpdateOrganizationAttributeInput } from "@/modules/organizations/inputs/attributes/update-organization-attribute.input";
|
||||
import { CreateOrganizationAttributesOutput } from "@/modules/organizations/outputs/attributes/create-organization-attributes.output";
|
||||
import { DeleteOrganizationAttributesOutput } from "@/modules/organizations/outputs/attributes/delete-organization-attributes.output";
|
||||
import {
|
||||
GetOrganizationAttributesOutput,
|
||||
GetSingleAttributeOutput,
|
||||
} from "@/modules/organizations/outputs/attributes/get-organization-attributes.output";
|
||||
import { UpdateOrganizationAttributesOutput } from "@/modules/organizations/outputs/attributes/update-organization-attributes.output";
|
||||
import { OrganizationAttributesService } from "@/modules/organizations/services/attributes/organization-attributes.service";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
|
||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import { SkipTakePagination } from "@calcom/platform-types";
|
||||
|
||||
@Controller({
|
||||
path: "/v2/organizations/:orgId",
|
||||
version: API_VERSIONS_VALUES,
|
||||
})
|
||||
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
|
||||
export class OrganizationsAttributesController {
|
||||
constructor(private readonly organizationsAttributesService: OrganizationAttributesService) {}
|
||||
// Gets all attributes for an organization
|
||||
@Roles("ORG_MEMBER")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Get("/attributes")
|
||||
async getOrganizationAttributes(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Query() queryParams: SkipTakePagination
|
||||
): Promise<GetOrganizationAttributesOutput> {
|
||||
const { skip, take } = queryParams;
|
||||
const attributes = await this.organizationsAttributesService.getOrganizationAttributes(orgId, skip, take);
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attributes,
|
||||
};
|
||||
}
|
||||
|
||||
// Gets a single attribute for an organization
|
||||
@Roles("ORG_MEMBER")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Get("/attributes/:attributeId")
|
||||
async getOrganizationAttribute(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("attributeId") attributeId: string
|
||||
): Promise<GetSingleAttributeOutput> {
|
||||
const attribute = await this.organizationsAttributesService.getOrganizationAttribute(orgId, attributeId);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attribute,
|
||||
};
|
||||
}
|
||||
|
||||
// Creates an attribute for an organization
|
||||
@Roles("ORG_ADMIN")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Post("/attributes")
|
||||
async createOrganizationAttribute(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Body() bodyAttribute: CreateOrganizationAttributeInput
|
||||
): Promise<CreateOrganizationAttributesOutput> {
|
||||
const attribute = await this.organizationsAttributesService.createOrganizationAttribute(
|
||||
orgId,
|
||||
bodyAttribute
|
||||
);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attribute,
|
||||
};
|
||||
}
|
||||
|
||||
// Updates an attribute for an organization
|
||||
@Roles("ORG_ADMIN")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Patch("/attributes/:attributeId")
|
||||
async updateOrganizationAttribute(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("attributeId") attributeId: string,
|
||||
@Body() bodyAttribute: UpdateOrganizationAttributeInput
|
||||
): Promise<UpdateOrganizationAttributesOutput> {
|
||||
const attribute = await this.organizationsAttributesService.updateOrganizationAttribute(
|
||||
orgId,
|
||||
attributeId,
|
||||
bodyAttribute
|
||||
);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attribute,
|
||||
};
|
||||
}
|
||||
|
||||
// Deletes an attribute for an organization
|
||||
@Roles("ORG_ADMIN")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@Delete("/attributes/:attributeId")
|
||||
async deleteOrganizationAttribute(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Param("attributeId") attributeId: string
|
||||
): Promise<DeleteOrganizationAttributesOutput> {
|
||||
const attribute = await this.organizationsAttributesService.deleteOrganizationAttribute(
|
||||
orgId,
|
||||
attributeId
|
||||
);
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: attribute,
|
||||
};
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { IsNotEmpty, IsOptional, IsString } from "class-validator";
|
||||
|
||||
export class AssignOrganizationAttributeOptionToUserInput {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
readonly value?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
readonly attributeOptionId?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
readonly attributeId!: string;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { CreateOrganizationAttributeOptionInput } from "@/modules/organizations/inputs/attributes/options/create-organization-attribute-option.input";
|
||||
import { AttributeType } from "@prisma/client";
|
||||
import { Type } from "class-transformer";
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator";
|
||||
|
||||
export class CreateOrganizationAttributeInput {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
readonly name!: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
readonly slug!: string;
|
||||
|
||||
@IsEnum(AttributeType)
|
||||
@IsNotEmpty()
|
||||
readonly type!: AttributeType;
|
||||
|
||||
@IsArray()
|
||||
@ValidateNested()
|
||||
@Type(() => CreateOrganizationAttributeOptionInput)
|
||||
readonly options!: CreateOrganizationAttributeOptionInput[];
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
readonly enabled?: boolean;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
|
||||
export class CreateOrganizationAttributeOptionInput {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
readonly value!: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
readonly slug!: string;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { IsNotEmpty, IsOptional, IsString } from "class-validator";
|
||||
|
||||
export class UpdateOrganizationAttributeOptionInput {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
readonly value?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
readonly slug?: string;
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { AttributeType } from "@prisma/client";
|
||||
import { IsBoolean, IsEnum, IsOptional, IsString } from "class-validator";
|
||||
|
||||
export class UpdateOrganizationAttributeInput {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
readonly name?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
readonly slug?: string;
|
||||
|
||||
@IsEnum(AttributeType)
|
||||
@IsOptional()
|
||||
readonly type?: AttributeType;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
readonly enabled?: boolean;
|
||||
}
|
||||
@@ -3,6 +3,8 @@ import { SchedulesModule_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/
|
||||
import { EmailModule } from "@/modules/email/email.module";
|
||||
import { EmailService } from "@/modules/email/email.service";
|
||||
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
|
||||
import { OrganizationsOptionsAttributesController } from "@/modules/organizations/controllers/attributes/organizations-attributes-options.controller";
|
||||
import { OrganizationsAttributesController } from "@/modules/organizations/controllers/attributes/organizations-attributes.controller";
|
||||
import { OrganizationsEventTypesController } from "@/modules/organizations/controllers/event-types/organizations-event-types.controller";
|
||||
import { OrganizationsMembershipsController } from "@/modules/organizations/controllers/memberships/organizations-membership.controller";
|
||||
import { OrganizationsSchedulesController } from "@/modules/organizations/controllers/schedules/organizations-schedules.controller";
|
||||
@@ -11,6 +13,8 @@ import { OrganizationsTeamsController } from "@/modules/organizations/controller
|
||||
import { OrganizationsUsersController } from "@/modules/organizations/controllers/users/organizations-users.controller";
|
||||
import { OrganizationsWebhooksController } from "@/modules/organizations/controllers/webhooks/organizations-webhooks.controller";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
import { OrganizationAttributeOptionRepository } from "@/modules/organizations/repositories/attributes/organization-attribute-option.repository";
|
||||
import { OrganizationAttributesRepository } from "@/modules/organizations/repositories/attributes/organization-attribute.repository";
|
||||
import { OrganizationsEventTypesRepository } from "@/modules/organizations/repositories/organizations-event-types.repository";
|
||||
import { OrganizationsMembershipRepository } from "@/modules/organizations/repositories/organizations-membership.repository";
|
||||
import { OrganizationSchedulesRepository } from "@/modules/organizations/repositories/organizations-schedules.repository";
|
||||
@@ -18,6 +22,8 @@ import { OrganizationsTeamsMembershipsRepository } from "@/modules/organizations
|
||||
import { OrganizationsTeamsRepository } from "@/modules/organizations/repositories/organizations-teams.repository";
|
||||
import { OrganizationsUsersRepository } from "@/modules/organizations/repositories/organizations-users.repository";
|
||||
import { OrganizationsWebhooksRepository } from "@/modules/organizations/repositories/organizations-webhooks.repository";
|
||||
import { OrganizationAttributeOptionService } from "@/modules/organizations/services/attributes/organization-attributes-option.service";
|
||||
import { OrganizationAttributesService } from "@/modules/organizations/services/attributes/organization-attributes.service";
|
||||
import { InputOrganizationsEventTypesService } from "@/modules/organizations/services/event-types/input.service";
|
||||
import { OrganizationsEventTypesService } from "@/modules/organizations/services/event-types/organizations-event-types.service";
|
||||
import { OutputOrganizationsEventTypesService } from "@/modules/organizations/services/event-types/output.service";
|
||||
@@ -65,6 +71,10 @@ import { Module } from "@nestjs/common";
|
||||
OrganizationsEventTypesRepository,
|
||||
OrganizationsTeamsMembershipsRepository,
|
||||
OrganizationsTeamsMembershipsService,
|
||||
OrganizationAttributesService,
|
||||
OrganizationAttributeOptionService,
|
||||
OrganizationAttributeOptionRepository,
|
||||
OrganizationAttributesRepository,
|
||||
OrganizationsWebhooksRepository,
|
||||
OrganizationsWebhooksService,
|
||||
WebhooksRepository,
|
||||
@@ -80,6 +90,10 @@ import { Module } from "@nestjs/common";
|
||||
OrganizationsMembershipService,
|
||||
OrganizationsTeamsMembershipsRepository,
|
||||
OrganizationsTeamsMembershipsService,
|
||||
OrganizationAttributesService,
|
||||
OrganizationAttributeOptionService,
|
||||
OrganizationAttributeOptionRepository,
|
||||
OrganizationAttributesRepository,
|
||||
OrganizationsWebhooksRepository,
|
||||
OrganizationsWebhooksService,
|
||||
WebhooksRepository,
|
||||
@@ -92,6 +106,8 @@ import { Module } from "@nestjs/common";
|
||||
OrganizationsMembershipsController,
|
||||
OrganizationsEventTypesController,
|
||||
OrganizationsTeamsMembershipsController,
|
||||
OrganizationsAttributesController,
|
||||
OrganizationsOptionsAttributesController,
|
||||
OrganizationsWebhooksController,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsString, IsInt, IsEnum, IsBoolean } from "class-validator";
|
||||
|
||||
export const AttributeType = {
|
||||
TEXT: "TEXT",
|
||||
NUMBER: "NUMBER",
|
||||
SINGLE_SELECT: "SINGLE_SELECT",
|
||||
MULTI_SELECT: "MULTI_SELECT",
|
||||
} as const;
|
||||
|
||||
export type AttributeType = (typeof AttributeType)[keyof typeof AttributeType];
|
||||
|
||||
export class Attribute {
|
||||
@IsString()
|
||||
@ApiProperty({ type: String, required: true, description: "The ID of the attribute", example: "attr_123" })
|
||||
id!: string;
|
||||
|
||||
@IsInt()
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
required: true,
|
||||
description: "The team ID associated with the attribute",
|
||||
example: 1,
|
||||
})
|
||||
teamId!: number;
|
||||
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
required: true,
|
||||
description: "The type of the attribute",
|
||||
enum: AttributeType,
|
||||
})
|
||||
@IsEnum(AttributeType)
|
||||
type!: AttributeType;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
required: true,
|
||||
description: "The name of the attribute",
|
||||
example: "Attribute Name",
|
||||
})
|
||||
name!: string;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
required: true,
|
||||
description: "The slug of the attribute",
|
||||
example: "attribute-name",
|
||||
})
|
||||
slug!: string;
|
||||
|
||||
@IsBoolean()
|
||||
@ApiProperty({
|
||||
type: Boolean,
|
||||
required: true,
|
||||
description: "Whether the attribute is enabled and displayed on their profile",
|
||||
example: true,
|
||||
})
|
||||
enabled!: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@ApiProperty({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
description: "Whether users can edit the relation",
|
||||
example: true,
|
||||
})
|
||||
usersCanEditRelation!: boolean;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsEnum } from "class-validator";
|
||||
|
||||
import { ERROR_STATUS } from "@calcom/platform-constants";
|
||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
|
||||
export class BaseOutputDTO {
|
||||
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
|
||||
@IsEnum([SUCCESS_STATUS, ERROR_STATUS])
|
||||
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Attribute } from "@/modules/organizations/outputs/attributes/attribute.output";
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { ValidateNested } from "class-validator";
|
||||
|
||||
export class CreateOrganizationAttributesOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => Attribute)
|
||||
data!: Attribute;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Attribute } from "@/modules/organizations/outputs/attributes/attribute.output";
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { ValidateNested } from "class-validator";
|
||||
|
||||
export class DeleteOrganizationAttributesOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => Attribute)
|
||||
data!: Attribute;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { Attribute } from "@/modules/organizations/outputs/attributes/attribute.output";
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { IsBoolean, IsInt, IsString, IsEnum, ValidateNested, IsOptional } from "class-validator";
|
||||
|
||||
import { AttributeType } from "@calcom/prisma/client";
|
||||
|
||||
export class GetSingleAttributeOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@IsOptional()
|
||||
@Type(() => Attribute)
|
||||
data!: Attribute | null;
|
||||
}
|
||||
|
||||
export class GetOrganizationAttributesOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => Attribute)
|
||||
data!: Attribute[];
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { OptionOutput } from "@/modules/organizations/outputs/attributes/options/option.output";
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { IsString, ValidateNested } from "class-validator";
|
||||
|
||||
class AssignOptionUserOutputData {
|
||||
@IsString()
|
||||
@ApiProperty({ type: String, required: true, description: "The ID of the option assigned to the user" })
|
||||
id!: string;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({ type: Number, required: true, description: "The ID form the org membership for the user" })
|
||||
memberId!: number;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({ type: String, required: true, description: "The value of the option" })
|
||||
attributeOptionId!: string;
|
||||
}
|
||||
|
||||
export class AssignOptionUserOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => AssignOptionUserOutputData)
|
||||
data!: AssignOptionUserOutputData;
|
||||
}
|
||||
|
||||
export class UnassignOptionUserOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => AssignOptionUserOutputData)
|
||||
data!: AssignOptionUserOutputData;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { OptionOutput } from "@/modules/organizations/outputs/attributes/options/option.output";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { ValidateNested } from "class-validator";
|
||||
|
||||
export class CreateAttributeOptionOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => OptionOutput)
|
||||
data!: OptionOutput;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { OptionOutput } from "@/modules/organizations/outputs/attributes/options/option.output";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { ValidateNested } from "class-validator";
|
||||
|
||||
export class DeleteAttributeOptionOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => OptionOutput)
|
||||
data!: OptionOutput;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { IsString, ValidateNested } from "class-validator";
|
||||
|
||||
class GetOptionUserOutputData {
|
||||
@IsString()
|
||||
@ApiProperty({ type: String, required: true, description: "The ID of the option assigned to the user" })
|
||||
id!: string;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({ type: String, required: true, description: "The ID of the attribute" })
|
||||
attributeId!: string;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({ type: String, required: true, description: "The value of the option" })
|
||||
value!: string;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({ type: String, required: true, description: "The slug of the option" })
|
||||
slug!: string;
|
||||
}
|
||||
|
||||
export class GetOptionUserOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => GetOptionUserOutputData)
|
||||
data!: GetOptionUserOutputData[];
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { OptionOutput } from "@/modules/organizations/outputs/attributes/options/option.output";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { ValidateNested } from "class-validator";
|
||||
|
||||
export class GetAllAttributeOptionOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => OptionOutput)
|
||||
data!: OptionOutput[];
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsString, IsInt, IsEnum, IsBoolean } from "class-validator";
|
||||
|
||||
export class OptionOutput {
|
||||
@IsString()
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
required: true,
|
||||
description: "The ID of the option",
|
||||
example: "attr_option_id",
|
||||
})
|
||||
id!: string;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({ type: String, required: true, description: "The ID of the attribute", example: "attr_id" })
|
||||
attributeId!: string;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
required: true,
|
||||
description: "The value of the option",
|
||||
example: "option_value",
|
||||
})
|
||||
value!: string;
|
||||
|
||||
@IsString()
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
required: true,
|
||||
description: "The slug of the option",
|
||||
example: "option-slug",
|
||||
})
|
||||
slug!: string;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { OptionOutput } from "@/modules/organizations/outputs/attributes/options/option.output";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { ValidateNested } from "class-validator";
|
||||
|
||||
export class UpdateAttributeOptionOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => OptionOutput)
|
||||
data!: OptionOutput;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Attribute } from "@/modules/organizations/outputs/attributes/attribute.output";
|
||||
import { BaseOutputDTO } from "@/modules/organizations/outputs/attributes/base.output";
|
||||
import { Expose, Type } from "class-transformer";
|
||||
import { ValidateNested } from "class-validator";
|
||||
|
||||
export class UpdateOrganizationAttributesOutput extends BaseOutputDTO {
|
||||
@Expose()
|
||||
@ValidateNested()
|
||||
@Type(() => Attribute)
|
||||
data!: Attribute;
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
import { CreateOrganizationAttributeOptionInput } from "@/modules/organizations/inputs/attributes/options/create-organization-attribute-option.input";
|
||||
import { UpdateOrganizationAttributeOptionInput } from "@/modules/organizations/inputs/attributes/options/update-organizaiton-attribute-option.input.ts";
|
||||
import { OrganizationsMembershipService } from "@/modules/organizations/services/organizations-membership.service";
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable, Logger, NotFoundException } from "@nestjs/common";
|
||||
|
||||
import { slugify } from "@calcom/platform-libraries";
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationAttributeOptionRepository {
|
||||
private readonly logger = new Logger("OrganizationAttributeOptionRepository");
|
||||
constructor(
|
||||
private readonly dbRead: PrismaReadService,
|
||||
private readonly dbWrite: PrismaWriteService,
|
||||
private readonly organizationsMembershipsService: OrganizationsMembershipService
|
||||
) {}
|
||||
|
||||
async createOrganizationAttributeOption(
|
||||
organizationId: number,
|
||||
attributeId: string,
|
||||
data: CreateOrganizationAttributeOptionInput
|
||||
) {
|
||||
return this.dbWrite.prisma.attributeOption.create({
|
||||
data: {
|
||||
...data,
|
||||
attributeId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async deleteOrganizationAttributeOption(organizationId: number, attributeId: string, optionId: string) {
|
||||
try {
|
||||
const deletedAttributeOption = await this.dbWrite.prisma.attributeOption.delete({
|
||||
where: {
|
||||
id: optionId,
|
||||
attributeId,
|
||||
},
|
||||
});
|
||||
return deletedAttributeOption;
|
||||
} catch (error: any) {
|
||||
if (error.code === "P2025") {
|
||||
// P2025 is the Prisma error code for "Record to delete does not exist."
|
||||
this.logger.warn(`Attribute option not found: ${optionId}`);
|
||||
throw new NotFoundException("Attribute option not found");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async updateOrganizationAttributeOption(
|
||||
organizationId: number,
|
||||
attributeId: string,
|
||||
optionId: string,
|
||||
data: UpdateOrganizationAttributeOptionInput
|
||||
) {
|
||||
return this.dbWrite.prisma.attributeOption.update({
|
||||
where: {
|
||||
id: optionId,
|
||||
attributeId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
async getOrganizationAttributeOptions(organizationId: number, attributeId: string) {
|
||||
return this.dbRead.prisma.attributeOption.findMany({
|
||||
where: {
|
||||
attributeId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getOrganizationAttributeOptionsForUser(organizationId: number, userId: number) {
|
||||
const options = this.dbRead.prisma.attributeOption.findMany({
|
||||
where: {
|
||||
attribute: {
|
||||
teamId: organizationId,
|
||||
},
|
||||
assignedUsers: {
|
||||
some: {
|
||||
member: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
async assignOrganizationAttributeOptionToUser({
|
||||
organizationId,
|
||||
membershipId,
|
||||
attributeId,
|
||||
value,
|
||||
attributeOptionId,
|
||||
}: {
|
||||
organizationId: number;
|
||||
membershipId: number;
|
||||
attributeId: string;
|
||||
value?: string;
|
||||
attributeOptionId?: string;
|
||||
}) {
|
||||
let _attributeOptionId = attributeOptionId;
|
||||
|
||||
if (value && !attributeOptionId) {
|
||||
_attributeOptionId = await this.createDynamicAttributeOption(organizationId, attributeId, value);
|
||||
}
|
||||
|
||||
if (!_attributeOptionId) throw new Error("Attribute option not found");
|
||||
|
||||
return this.dbWrite.prisma.attributeToUser.create({
|
||||
data: {
|
||||
attributeOptionId: _attributeOptionId,
|
||||
memberId: membershipId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async createDynamicAttributeOption(
|
||||
organizationId: number,
|
||||
attributeId: string,
|
||||
value: string
|
||||
): Promise<string> {
|
||||
const attributeOption = await this.createOrganizationAttributeOption(organizationId, attributeId, {
|
||||
value: value,
|
||||
slug: slugify(value),
|
||||
});
|
||||
return attributeOption.id;
|
||||
}
|
||||
|
||||
async unassignOrganizationAttributeOptionFromUser(
|
||||
organizationId: number,
|
||||
userId: number,
|
||||
attributeOptionId: string
|
||||
) {
|
||||
const membership = await this.organizationsMembershipsService.getOrgMembershipByUserId(
|
||||
organizationId,
|
||||
userId
|
||||
);
|
||||
|
||||
if (!membership) throw new Error("Membership not found");
|
||||
|
||||
try {
|
||||
const deletedAttributeToUser = await this.dbWrite.prisma.attributeToUser.delete({
|
||||
where: { memberId_attributeOptionId: { memberId: membership.id, attributeOptionId } },
|
||||
});
|
||||
return deletedAttributeToUser;
|
||||
} catch (error: any) {
|
||||
if (error.code === "P2025") {
|
||||
// P2025 is the Prisma error code for "Record to delete does not exist."
|
||||
this.logger.warn(`Attribute option not found: ${attributeOptionId} for user ${userId}`);
|
||||
throw new NotFoundException("Attribute does not belong to this user");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import { CreateOrganizationAttributeInput } from "@/modules/organizations/inputs/attributes/create-organization-attribute.input";
|
||||
import { UpdateOrganizationAttributeInput } from "@/modules/organizations/inputs/attributes/update-organization-attribute.input";
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationAttributesRepository {
|
||||
constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
|
||||
|
||||
async createOrganizationAttribute(organizationId: number, data: CreateOrganizationAttributeInput) {
|
||||
const { options, ...attributeData } = data;
|
||||
|
||||
const attribute = await this.dbWrite.prisma.attribute.create({
|
||||
data: {
|
||||
...attributeData,
|
||||
teamId: organizationId,
|
||||
},
|
||||
});
|
||||
|
||||
if (attribute.type === "SINGLE_SELECT" || attribute.type === "MULTI_SELECT") {
|
||||
// TODO: move this to attribute option service
|
||||
await this.dbWrite.prisma.attributeOption.createMany({
|
||||
data: options.map((option) => ({
|
||||
...option,
|
||||
attributeId: attribute.id,
|
||||
})),
|
||||
});
|
||||
}
|
||||
return attribute;
|
||||
}
|
||||
|
||||
async getOrganizationAttribute(organizationId: number, attributeId: string) {
|
||||
const attribute = await this.dbRead.prisma.attribute.findUnique({
|
||||
where: {
|
||||
id: attributeId,
|
||||
teamId: organizationId,
|
||||
},
|
||||
});
|
||||
return attribute;
|
||||
}
|
||||
|
||||
async getOrganizationAttributes(organizationId: number, skip?: number, take?: number) {
|
||||
const attributes = await this.dbRead.prisma.attribute.findMany({
|
||||
where: {
|
||||
teamId: organizationId,
|
||||
},
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
return attributes;
|
||||
}
|
||||
async updateOrganizationAttribute(
|
||||
organizationId: number,
|
||||
attributeId: string,
|
||||
data: UpdateOrganizationAttributeInput
|
||||
) {
|
||||
const attribute = await this.dbWrite.prisma.attribute.update({
|
||||
where: {
|
||||
id: attributeId,
|
||||
teamId: organizationId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
return attribute;
|
||||
}
|
||||
|
||||
async deleteOrganizationAttribute(organizationId: number, attributeId: string) {
|
||||
const attribute = await this.dbWrite.prisma.attribute.delete({
|
||||
where: {
|
||||
id: attributeId,
|
||||
teamId: organizationId,
|
||||
},
|
||||
});
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
+9
@@ -18,6 +18,15 @@ export class OrganizationsMembershipRepository {
|
||||
});
|
||||
}
|
||||
|
||||
async findOrgMembershipByUserId(organizationId: number, userId: number) {
|
||||
return this.dbRead.prisma.membership.findFirst({
|
||||
where: {
|
||||
teamId: organizationId,
|
||||
userId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async deleteOrgMembership(organizationId: number, membershipId: number) {
|
||||
return this.dbWrite.prisma.membership.delete({
|
||||
where: {
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
import { AssignOrganizationAttributeOptionToUserInput } from "@/modules/organizations/inputs/attributes/assign/organizations-attributes-options-assign.input";
|
||||
import { CreateOrganizationAttributeOptionInput } from "@/modules/organizations/inputs/attributes/options/create-organization-attribute-option.input";
|
||||
import { UpdateOrganizationAttributeOptionInput } from "@/modules/organizations/inputs/attributes/options/update-organizaiton-attribute-option.input.ts";
|
||||
import { OrganizationAttributeOptionRepository } from "@/modules/organizations/repositories/attributes/organization-attribute-option.repository";
|
||||
import { OrganizationAttributesService } from "@/modules/organizations/services/attributes/organization-attributes.service";
|
||||
import { OrganizationsMembershipService } from "@/modules/organizations/services/organizations-membership.service";
|
||||
import { BadRequestException, Injectable, Logger, NotFoundException } from "@nestjs/common";
|
||||
|
||||
const TYPE_SUPPORTS_VALUE = new Set(["TEXT", "NUMBER"]);
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationAttributeOptionService {
|
||||
private readonly logger = new Logger("OrganizationAttributeOptionService");
|
||||
constructor(
|
||||
private readonly organizationAttributeOptionRepository: OrganizationAttributeOptionRepository,
|
||||
private readonly organizationAttributesService: OrganizationAttributesService,
|
||||
private readonly organizationsMembershipsService: OrganizationsMembershipService
|
||||
) {}
|
||||
|
||||
async createOrganizationAttributeOption(
|
||||
organizationId: number,
|
||||
attributeId: string,
|
||||
data: CreateOrganizationAttributeOptionInput
|
||||
) {
|
||||
return this.organizationAttributeOptionRepository.createOrganizationAttributeOption(
|
||||
organizationId,
|
||||
attributeId,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
async deleteOrganizationAttributeOption(organizationId: number, attributeId: string, optionId: string) {
|
||||
return this.organizationAttributeOptionRepository.deleteOrganizationAttributeOption(
|
||||
organizationId,
|
||||
attributeId,
|
||||
optionId
|
||||
);
|
||||
}
|
||||
|
||||
async updateOrganizationAttributeOption(
|
||||
organizationId: number,
|
||||
attributeId: string,
|
||||
optionId: string,
|
||||
data: UpdateOrganizationAttributeOptionInput
|
||||
) {
|
||||
return this.organizationAttributeOptionRepository.updateOrganizationAttributeOption(
|
||||
organizationId,
|
||||
attributeId,
|
||||
optionId,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
async getOrganizationAttributeOptions(organizationId: number, attributeId: string) {
|
||||
return this.organizationAttributeOptionRepository.getOrganizationAttributeOptions(
|
||||
organizationId,
|
||||
attributeId
|
||||
);
|
||||
}
|
||||
|
||||
async assignOrganizationAttributeOptionToUser(
|
||||
organizationId: number,
|
||||
userId: number,
|
||||
data: AssignOrganizationAttributeOptionToUserInput
|
||||
) {
|
||||
const attribute = await this.organizationAttributesService.getOrganizationAttribute(
|
||||
organizationId,
|
||||
data.attributeId
|
||||
);
|
||||
|
||||
if (!attribute) {
|
||||
throw new NotFoundException("Attribute not found");
|
||||
}
|
||||
|
||||
const membership = await this.organizationsMembershipsService.getOrgMembershipByUserId(
|
||||
organizationId,
|
||||
userId
|
||||
);
|
||||
|
||||
if (!membership || !membership.accepted)
|
||||
throw new NotFoundException("User is not a member of the organization");
|
||||
|
||||
if (!TYPE_SUPPORTS_VALUE.has(attribute.type) && data.value) {
|
||||
throw new BadRequestException("Attribute type does not support value");
|
||||
}
|
||||
|
||||
return this.organizationAttributeOptionRepository.assignOrganizationAttributeOptionToUser({
|
||||
organizationId,
|
||||
membershipId: membership.id,
|
||||
value: data.value,
|
||||
attributeId: data.attributeId,
|
||||
attributeOptionId: data.attributeOptionId,
|
||||
});
|
||||
}
|
||||
|
||||
async unassignOrganizationAttributeOptionFromUser(
|
||||
organizationId: number,
|
||||
userId: number,
|
||||
attributeOptionId: string
|
||||
) {
|
||||
return this.organizationAttributeOptionRepository.unassignOrganizationAttributeOptionFromUser(
|
||||
organizationId,
|
||||
userId,
|
||||
attributeOptionId
|
||||
);
|
||||
}
|
||||
|
||||
async getOrganizationAttributeOptionsForUser(organizationId: number, userId: number) {
|
||||
return this.organizationAttributeOptionRepository.getOrganizationAttributeOptionsForUser(
|
||||
organizationId,
|
||||
userId
|
||||
);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { CreateOrganizationAttributeInput } from "@/modules/organizations/inputs/attributes/create-organization-attribute.input";
|
||||
import { UpdateOrganizationAttributeInput } from "@/modules/organizations/inputs/attributes/update-organization-attribute.input";
|
||||
import { OrganizationAttributesRepository } from "@/modules/organizations/repositories/attributes/organization-attribute.repository";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationAttributesService {
|
||||
constructor(private readonly organizationAttributesRepository: OrganizationAttributesRepository) {}
|
||||
|
||||
async createOrganizationAttribute(organizationId: number, data: CreateOrganizationAttributeInput) {
|
||||
const attribute = await this.organizationAttributesRepository.createOrganizationAttribute(organizationId, data);
|
||||
return attribute;
|
||||
}
|
||||
|
||||
async getOrganizationAttribute(organizationId: number, attributeId: string) {
|
||||
const attribute = await this.organizationAttributesRepository.getOrganizationAttribute(organizationId, attributeId);
|
||||
return attribute;
|
||||
}
|
||||
|
||||
async getOrganizationAttributes(organizationId: number, skip?: number, take?: number) {
|
||||
const attributes = await this.organizationAttributesRepository.getOrganizationAttributes(organizationId, skip, take);
|
||||
return attributes;
|
||||
}
|
||||
|
||||
async updateOrganizationAttribute(organizationId: number, attributeId: string, data: UpdateOrganizationAttributeInput) {
|
||||
const attribute = await this.organizationAttributesRepository.updateOrganizationAttribute(organizationId, attributeId, data);
|
||||
return attribute;
|
||||
}
|
||||
|
||||
async deleteOrganizationAttribute(organizationId: number, attributeId: string) {
|
||||
const attribute = await this.organizationAttributesRepository.deleteOrganizationAttribute(organizationId, attributeId);
|
||||
return attribute;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,14 @@ export class OrganizationsMembershipService {
|
||||
return membership;
|
||||
}
|
||||
|
||||
async getOrgMembershipByUserId(organizationId: number, userId: number) {
|
||||
const membership = await this.organizationsMembershipRepository.findOrgMembershipByUserId(
|
||||
organizationId,
|
||||
userId
|
||||
);
|
||||
return membership;
|
||||
}
|
||||
|
||||
async getPaginatedOrgMemberships(organizationId: number, skip = 0, take = 250) {
|
||||
const memberships = await this.organizationsMembershipRepository.findOrgMembershipsPaginated(
|
||||
organizationId,
|
||||
|
||||
@@ -2285,6 +2285,492 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/organizations/{orgId}/attributes": {
|
||||
"get": {
|
||||
"operationId": "OrganizationsAttributesController_getOrganizationAttributes",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "take",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "The number of items to return",
|
||||
"example": 10,
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "The number of items to skip",
|
||||
"example": 0,
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GetOrganizationAttributesOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"operationId": "OrganizationsAttributesController_createOrganizationAttribute",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateOrganizationAttributeInput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateOrganizationAttributesOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/organizations/{orgId}/attributes/{attributeId}": {
|
||||
"get": {
|
||||
"operationId": "OrganizationsAttributesController_getOrganizationAttribute",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "attributeId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GetSingleAttributeOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"operationId": "OrganizationsAttributesController_updateOrganizationAttribute",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "attributeId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateOrganizationAttributeInput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateOrganizationAttributesOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"operationId": "OrganizationsAttributesController_deleteOrganizationAttribute",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "attributeId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/DeleteOrganizationAttributesOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/organizations/{orgId}/attributes/{attributeId}/options": {
|
||||
"post": {
|
||||
"operationId": "OrganizationsOptionsAttributesController_createOrganizationAttributeOption",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "attributeId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateOrganizationAttributeOptionInput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateAttributeOptionOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"get": {
|
||||
"operationId": "OrganizationsOptionsAttributesController_getOrganizationAttributeOptions",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "attributeId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GetAllAttributeOptionOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/organizations/{orgId}/attributes/{attributeId}/options/{optionId}": {
|
||||
"delete": {
|
||||
"operationId": "OrganizationsOptionsAttributesController_deleteOrganizationAttributeOption",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "attributeId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "optionId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/DeleteAttributeOptionOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"operationId": "OrganizationsOptionsAttributesController_updateOrganizationAttributeOption",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "attributeId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "optionId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateOrganizationAttributeOptionInput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateAttributeOptionOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/organizations/{orgId}/attributes/options/{userId}": {
|
||||
"post": {
|
||||
"operationId": "OrganizationsOptionsAttributesController_assignOrganizationAttributeOptionToUser",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "userId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AssignOrganizationAttributeOptionToUserInput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AssignOptionUserOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"get": {
|
||||
"operationId": "OrganizationsOptionsAttributesController_getOrganizationAttributeOptionsForUser",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "userId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GetOptionUserOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/organizations/{orgId}/attributes/options/{userId}/{attributeOptionId}": {
|
||||
"delete": {
|
||||
"operationId": "OrganizationsOptionsAttributesController_unassignOrganizationAttributeOptionFromUser",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "orgId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "userId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "attributeOptionId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UnassignOptionUserOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/organizations/{orgId}/webhooks": {
|
||||
"get": {
|
||||
"operationId": "OrganizationsWebhooksController_getAllOrganizationWebhooks",
|
||||
@@ -7578,6 +8064,482 @@
|
||||
"userId"
|
||||
]
|
||||
},
|
||||
"Attribute": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the attribute",
|
||||
"example": "attr_123"
|
||||
},
|
||||
"teamId": {
|
||||
"type": "number",
|
||||
"description": "The team ID associated with the attribute",
|
||||
"example": 1
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "The type of the attribute",
|
||||
"enum": [
|
||||
"TEXT",
|
||||
"NUMBER",
|
||||
"SINGLE_SELECT",
|
||||
"MULTI_SELECT"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the attribute",
|
||||
"example": "Attribute Name"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"description": "The slug of the attribute",
|
||||
"example": "attribute-name"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the attribute is enabled and displayed on their profile",
|
||||
"example": true
|
||||
},
|
||||
"usersCanEditRelation": {
|
||||
"type": "boolean",
|
||||
"description": "Whether users can edit the relation",
|
||||
"example": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"teamId",
|
||||
"type",
|
||||
"name",
|
||||
"slug",
|
||||
"enabled"
|
||||
]
|
||||
},
|
||||
"GetOrganizationAttributesOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Attribute"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"GetSingleAttributeOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"nullable": true,
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Attribute"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"CreateOrganizationAttributeOptionInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "string"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"value",
|
||||
"slug"
|
||||
]
|
||||
},
|
||||
"CreateOrganizationAttributeInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "object"
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/CreateOrganizationAttributeOptionInput"
|
||||
}
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"slug",
|
||||
"type",
|
||||
"options"
|
||||
]
|
||||
},
|
||||
"CreateOrganizationAttributesOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/Attribute"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"UpdateOrganizationAttributeInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "object"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UpdateOrganizationAttributesOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/Attribute"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"DeleteOrganizationAttributesOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/Attribute"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"OptionOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the option",
|
||||
"example": "attr_option_id"
|
||||
},
|
||||
"attributeId": {
|
||||
"type": "string",
|
||||
"description": "The ID of the attribute",
|
||||
"example": "attr_id"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "The value of the option",
|
||||
"example": "option_value"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"description": "The slug of the option",
|
||||
"example": "option-slug"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"attributeId",
|
||||
"value",
|
||||
"slug"
|
||||
]
|
||||
},
|
||||
"CreateAttributeOptionOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/OptionOutput"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"DeleteAttributeOptionOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/OptionOutput"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"UpdateOrganizationAttributeOptionInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "string"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UpdateAttributeOptionOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/OptionOutput"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"GetAllAttributeOptionOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/OptionOutput"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"AssignOrganizationAttributeOptionToUserInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "string"
|
||||
},
|
||||
"attributeOptionId": {
|
||||
"type": "string"
|
||||
},
|
||||
"attributeId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"attributeId"
|
||||
]
|
||||
},
|
||||
"AssignOptionUserOutputData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the option assigned to the user"
|
||||
},
|
||||
"memberId": {
|
||||
"type": "number",
|
||||
"description": "The ID form the org membership for the user"
|
||||
},
|
||||
"attributeOptionId": {
|
||||
"type": "string",
|
||||
"description": "The value of the option"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"memberId",
|
||||
"attributeOptionId"
|
||||
]
|
||||
},
|
||||
"AssignOptionUserOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/AssignOptionUserOutputData"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"UnassignOptionUserOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/AssignOptionUserOutputData"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"GetOptionUserOutputData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the option assigned to the user"
|
||||
},
|
||||
"attributeId": {
|
||||
"type": "string",
|
||||
"description": "The ID of the attribute"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "The value of the option"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"description": "The slug of the option"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"attributeId",
|
||||
"value",
|
||||
"slug"
|
||||
]
|
||||
},
|
||||
"GetOptionUserOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "success",
|
||||
"enum": [
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/GetOptionUserOutputData"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"TeamWebhookOutputDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { TestingModule } from "@nestjs/testing";
|
||||
import { Prisma, User } from "@prisma/client";
|
||||
|
||||
export class AttributeRepositoryFixture {
|
||||
private prismaReadClient: PrismaReadService["prisma"];
|
||||
private prismaWriteClient: PrismaWriteService["prisma"];
|
||||
|
||||
constructor(private readonly module: TestingModule) {
|
||||
this.prismaReadClient = module.get(PrismaReadService).prisma;
|
||||
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
|
||||
}
|
||||
|
||||
async create(data: Prisma.AttributeCreateInput) {
|
||||
return this.prismaWriteClient.attribute.create({ data });
|
||||
}
|
||||
|
||||
async createOption(data: Prisma.AttributeOptionCreateInput) {
|
||||
return this.prismaWriteClient.attributeOption.create({ data });
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
return this.prismaWriteClient.attribute.delete({ where: { id } });
|
||||
}
|
||||
|
||||
async deleteOption(id: string) {
|
||||
return this.prismaWriteClient.attributeOption.delete({ where: { id } });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user