From 932ddc0290285489e661ac8a8befe27f791e066f Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 21 Jul 2025 14:30:23 -0400 Subject: [PATCH] Adding the organization integration api service and test cases --- .../organization-integration-request.ts | 11 +++ .../organization-integration-response.ts | 15 ++++ .../models/organization-integration-type.ts | 7 ++ .../organization-integration-api.service.ts | 54 ++++++++++++ ...ganization-integration-api.service.spec.ts | 86 +++++++++++++++++++ libs/common/src/types/guid.ts | 1 + 6 files changed, 174 insertions(+) create mode 100644 bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-request.ts create mode 100644 bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-response.ts create mode 100644 bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-type.ts create mode 100644 bitwarden_license/bit-common/src/dirt/Integrations/organization-integration-api.service.ts create mode 100644 bitwarden_license/bit-common/src/dirt/Integrations/services/organization-integration-api.service.spec.ts diff --git a/bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-request.ts b/bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-request.ts new file mode 100644 index 00000000000..95f7d180dae --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-request.ts @@ -0,0 +1,11 @@ +import { OrganizationIntegrationType } from "./organization-integration-type"; + +export class OrganizationIntegrationRequest { + type: OrganizationIntegrationType; + configuration?: string; + + constructor(integrationType: OrganizationIntegrationType, configuration?: string) { + this.type = integrationType; + this.configuration = configuration; + } +} diff --git a/bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-response.ts b/bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-response.ts new file mode 100644 index 00000000000..7c951a94945 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-response.ts @@ -0,0 +1,15 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; +import { Guid } from "@bitwarden/common/types/guid"; + +import { OrganizationIntegrationType } from "./organization-integration-type"; + +export class OrganizationIntegrationResponse extends BaseResponse { + id: Guid; + organizationIntegrationType: OrganizationIntegrationType; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.organizationIntegrationType = this.getResponseProperty("Type"); + } +} diff --git a/bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-type.ts b/bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-type.ts new file mode 100644 index 00000000000..5974e3c46a6 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/Integrations/models/organization-integration-type.ts @@ -0,0 +1,7 @@ +export const OrganizationIntegrationType = { + CloudBillingSync: 1, + Scim: 2, + Slack: 3, + Webhook: 4, + Hec: 5, +} as const; diff --git a/bitwarden_license/bit-common/src/dirt/Integrations/organization-integration-api.service.ts b/bitwarden_license/bit-common/src/dirt/Integrations/organization-integration-api.service.ts new file mode 100644 index 00000000000..6a30e4d1c30 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/Integrations/organization-integration-api.service.ts @@ -0,0 +1,54 @@ +import { Injectable } from "@angular/core"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationId, OrganizationIntegrationId } from "@bitwarden/common/types/guid"; + +import { OrganizationIntegrationRequest } from "./models/organization-integration-request"; +import { OrganizationIntegrationResponse } from "./models/organization-integration-response"; + +@Injectable() +export class OrganizationIntegrationApiService { + constructor(private apiService: ApiService) {} + + async createOrganizationIntegration( + orgId: OrganizationId, + request: OrganizationIntegrationRequest, + ): Promise { + const response = await this.apiService.send( + "POST", + `organizations/${orgId}/integrations`, + request, + true, + true, + ); + return response; + } + + async updateOrganizationIntegration( + orgId: OrganizationId, + integrationId: OrganizationIntegrationId, + request: OrganizationIntegrationRequest, + ): Promise { + const response = await this.apiService.send( + "PUT", + `organizations/${orgId}/integrations/${integrationId}`, + request, + true, + true, + ); + return response; + } + + async deleteOrganizationIntegration( + orgId: OrganizationId, + integrationId: OrganizationIntegrationId, + ): Promise { + await this.apiService.send( + "DELETE", + `organizations/${orgId}/integrations/${integrationId}`, + null, + true, + false, + ); + } +} diff --git a/bitwarden_license/bit-common/src/dirt/Integrations/services/organization-integration-api.service.spec.ts b/bitwarden_license/bit-common/src/dirt/Integrations/services/organization-integration-api.service.spec.ts new file mode 100644 index 00000000000..61a9b49e31b --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/Integrations/services/organization-integration-api.service.spec.ts @@ -0,0 +1,86 @@ +import { mock } from "jest-mock-extended"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationId, OrganizationIntegrationId } from "@bitwarden/common/types/guid"; + +import { OrganizationIntegrationRequest } from "../models/organization-integration-request"; +import { OrganizationIntegrationType } from "../models/organization-integration-type"; +import { OrganizationIntegrationApiService } from "../organization-integration-api.service"; + +export const mockIntegrationResponse: any = { + id: "1", + organizationIntegratipnType: 5, +}; + +describe("OrganizationIntegrationApiService", () => { + let service: OrganizationIntegrationApiService; + const apiService = mock(); + + beforeEach(() => { + service = new OrganizationIntegrationApiService(apiService); + }); + + it("should be created", () => { + expect(service).toBeTruthy(); + }); + + it("should call apiService.send with correct parameters for createOrganizationIntegration", async () => { + const request = new OrganizationIntegrationRequest( + OrganizationIntegrationType.Hec, + "{ 'uri:' 'test.com', 'scheme:' 'bearer', 'token:' '123456789' }", + ); + const orgId = "org1" as OrganizationId; + + apiService.send.mockReturnValue(Promise.resolve(mockIntegrationResponse)); + + const result = await service.createOrganizationIntegration(orgId, request); + expect(result.organizationIntegrationType).toEqual( + mockIntegrationResponse.organizationIntegrationType, + ); + expect(apiService.send).toHaveBeenCalledWith( + "POST", + `organizations/${orgId.toString()}/integrations`, + request, + true, + true, + ); + }); + + it("should call apiService.send with the correct parameters for updateOrganizationIntegration", async () => { + const request = new OrganizationIntegrationRequest( + OrganizationIntegrationType.Hec, + "{ 'uri:' 'test.com', 'scheme:' 'bearer', 'token:' '123456789' }", + ); + const orgId = "org1" as OrganizationId; + const integrationId = "integration1" as OrganizationIntegrationId; + + apiService.send.mockReturnValue(Promise.resolve(mockIntegrationResponse)); + + const result = await service.updateOrganizationIntegration(orgId, integrationId, request); + expect(result.organizationIntegrationType).toEqual( + mockIntegrationResponse.organizationIntegrationType, + ); + expect(apiService.send).toHaveBeenCalledWith( + "PUT", + `organizations/${orgId}/integrations/${integrationId}`, + request, + true, + true, + ); + }); + + it("should call apiService.send with the correct parameters for deleteOrganizationIntegration", async () => { + const orgId = "org1" as OrganizationId; + const integrationId = "integration1" as OrganizationIntegrationId; + + await service.deleteOrganizationIntegration(orgId, integrationId); + + expect(apiService.send).toHaveBeenCalledWith( + "DELETE", + `organizations/${orgId}/integrations/${integrationId}`, + null, + true, + false, + ); + }); +}); diff --git a/libs/common/src/types/guid.ts b/libs/common/src/types/guid.ts index 5edd34e4fc5..0796d06c9ea 100644 --- a/libs/common/src/types/guid.ts +++ b/libs/common/src/types/guid.ts @@ -15,3 +15,4 @@ export type IndexedEntityId = Opaque; export type SecurityTaskId = Opaque; export type NotificationId = Opaque; export type EmergencyAccessId = Opaque; +export type OrganizationIntegrationId = Opaque;