diff --git a/packages/api-client/src/controllers/integration.ts b/packages/api-client/src/controllers/integration.ts new file mode 100644 index 00000000..d0c128c9 --- /dev/null +++ b/packages/api-client/src/controllers/integration.ts @@ -0,0 +1,84 @@ +import { + CreateIntegrationRequest, + CreateIntegrationResponse, + DeleteIntegrationRequest, + DeleteIntegrationResponse, + GetAllIntegrationRequest, + GetAllIntegrationResponse, + GetIntegrationRequest, + GetIntegrationResponse, + UpdateIntegrationRequest, + UpdateIntegrationResponse +} from '../types/integration.types' +import { APIClient } from '../core/client' +import { ClientResponse } from '../types/index.types' +import { parseResponse } from '../core/response-parser' + +export default class IntegrationController { + private apiClient: APIClient + + constructor(private readonly backendUrl: string) { + this.apiClient = new APIClient(this.backendUrl) + } + + async createIntegration( + request: CreateIntegrationRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.post( + `/api/integration/${request.workspaceId}`, + request, + headers + ) + return await parseResponse(response) + } + + async updateIntegration( + request: UpdateIntegrationRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.put( + `/api/integration/${request.integrationId}`, + request, + headers + ) + return await parseResponse(response) + } + + async getIntegration( + request: GetIntegrationRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.get( + `/api/integration/${request.integrationId}`, + headers + ) + return await parseResponse(response) + } + + async getAllIntegrations( + request: GetAllIntegrationRequest, + headers?: Record + ): Promise> { + let url = `/api/integration/all/${request.workspaceId}` + request.page && (url += `page=${request.page}&`) + request.limit && (url += `limit=${request.limit}&`) + request.sort && (url += `sort=${request.sort}&`) + request.order && (url += `order=${request.order}&`) + request.search && (url += `search=${request.search}&`) + + const response = await this.apiClient.get(url, headers) + return await parseResponse(response) + } + + async deleteIntegration( + request: DeleteIntegrationRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.delete( + `/api/integration/${request.integrationId}`, + headers + ) + return await parseResponse(response) + } +} diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts index 67d78354..e87c54ba 100644 --- a/packages/api-client/src/index.ts +++ b/packages/api-client/src/index.ts @@ -1,5 +1,11 @@ import EnvironmentController from './controllers/environment' import SecretController from './controllers/secret' import EventController from './controllers/event' +import IntegrationController from './controllers/integration' -export { EnvironmentController, SecretController, EventController } +export { + EnvironmentController, + SecretController, + EventController, + IntegrationController +} diff --git a/packages/api-client/src/types/integration.types.d.ts b/packages/api-client/src/types/integration.types.d.ts new file mode 100644 index 00000000..3c225ccf --- /dev/null +++ b/packages/api-client/src/types/integration.types.d.ts @@ -0,0 +1,129 @@ +import { Page } from '../../../../apps/cli/src/types/index.types' +export enum IntegrationType { + DISCORD, + SLACK, + GITHUB, + GITLAB +} + +export enum EventType { + INVITED_TO_WORKSPACE, + REMOVED_FROM_WORKSPACE, + ACCEPTED_INVITATION, + DECLINED_INVITATION, + CANCELLED_INVITATION, + LEFT_WORKSPACE, + WORKSPACE_MEMBERSHIP_UPDATED, + WORKSPACE_UPDATED, + WORKSPACE_CREATED, + WORKSPACE_ROLE_CREATED, + WORKSPACE_ROLE_UPDATED, + WORKSPACE_ROLE_DELETED, + PROJECT_CREATED, + PROJECT_UPDATED, + PROJECT_DELETED, + SECRET_UPDATED, + SECRET_DELETED, + SECRET_ADDED, + VARIABLE_UPDATED, + VARIABLE_DELETED, + VARIABLE_ADDED, + ENVIRONMENT_UPDATED, + ENVIRONMENT_DELETED, + ENVIRONMENT_ADDED, + INTEGRATION_ADDED, + INTEGRATION_UPDATED, + INTEGRATION_DELETED +} +export interface CreateIntegrationRequest { + workspaceId?: string + projectId?: string + name: string + type: string + notifyOn: [string] + metadata: Record + environmentId: string +} + +export interface CreateIntegrationResponse { + id: string + name: string + metadata: Record + createdAt: string + updatedAt: string + type: IntegrationType + notifyOn: EventType[] + workspaceId: string + projectId: string + environmentId: string +} + +export interface UpdateIntegrationRequest { + integrationId: string + workspaceId?: string + projectId?: string + name?: string + type?: IntegrationType + notifyOn?: EventType[] + metadata?: Record + environmentId?: string +} + +export interface UpdateIntegrationResponse { + id: string + name: string + metadata: Record + createdAt: string + updatedAt: string + type: IntegrationType + notifyOn: EventType[] + workspaceId: string + projectId: string + environmentId: string +} + +export interface DeleteIntegrationResponse {} + +export interface DeleteIntegrationRequest { + integrationId: string +} + +export interface GetIntegrationRequest { + integrationId: string +} + +export interface GetIntegrationResponse { + id: string + name: string + metadata: Record + createdAt: string + updatedAt: string + type: IntegrationType + notifyOn: EventType[] + workspaceId: string + projectId: string + environmentId: string +} + +export interface GetAllIntegrationRequest { + page?: number + limit?: number + sort?: string + order?: string + search?: string + workspaceId: string +} + +export interface GetAllIntegrationResponse + extends Page<{ + id: string + name: string + metadata: Record + createdAt: string + updatedAt: string + type: IntegrationType + notifyOn: EventType[] + workspaceId: string + projectId: string + environmentId: string + }> {} diff --git a/packages/api-client/tests/integration.spec.ts b/packages/api-client/tests/integration.spec.ts new file mode 100644 index 00000000..9ab16b36 --- /dev/null +++ b/packages/api-client/tests/integration.spec.ts @@ -0,0 +1,175 @@ +import { APIClient } from '../src/core/client' +import IntegrationController from '../src/controllers/integration' + +describe('Get Environments Tests', () => { + const backendUrl = process.env.BACKEND_URL as string + + const client = new APIClient(backendUrl) + const integrationController = new IntegrationController(backendUrl) + const email = 'johndoe@example.com' + let projectId: string | undefined + let workspaceId: string + let environment: any + let integrationId: string + + beforeAll(async () => { + // Create the user's workspace + const workspaceResponse = (await ( + await client.post( + '/api/workspace', + { + name: 'Integration Workspace' + }, + { + 'x-e2e-user-email': email + } + ) + ).json()) as any + + workspaceId = workspaceResponse.id + + // Create a project + const projectResponse = (await ( + await client.post( + `/api/project/${workspaceId}`, + { + name: 'Project', + storePrivateKey: true + }, + { + 'x-e2e-user-email': email + } + ) + ).json()) as any + + projectId = projectResponse.id + + const createEnvironmentResponse = (await ( + await client.post( + `/api/environment/${projectId}`, + { + name: 'Dev' + }, + { + 'x-e2e-user-email': email + } + ) + ).json()) as any + + environment = createEnvironmentResponse + }) + + afterAll(async () => { + // Delete the workspace + await client.delete(`/api/workspace/${workspaceId}`, { + 'x-e2e-user-email': email + }) + }) + + beforeEach(async () => { + // Create a dummy integration before each test + const integration = await integrationController.createIntegration( + { + workspaceId, + projectId, + name: 'Dummy Integration', + type: 'DISCORD', + notifyOn: ['PROJECT_CREATED'], + metadata: { + webhookUrl: '{{vault:WEBHOOK_URL}}' + }, + environmentId: environment.id + }, + { + 'x-e2e-user-email': email + } + ) + integrationId = integration.data?.id as string + }) + + afterEach(async () => { + // Delete the dummy integration after each test + await integrationController.deleteIntegration( + { integrationId }, + { 'x-e2e-user-email': email } + ) + }) + + it('should create an integration', async () => { + const integration = await integrationController.createIntegration( + { + workspaceId, + projectId, + name: 'Discord second', + type: 'DISCORD', + notifyOn: ['PROJECT_CREATED'], + metadata: { + webhookUrl: '{{vault:WEBHOOK_URL}}' + }, + environmentId: environment.id + }, + { + 'x-e2e-user-email': email + } + ) + expect(integration.data.name).toBe('Discord second') + expect(integration.data.projectId).toBe(projectId) + expect(integration.data.environmentId).toBe(environment.id) + expect(integration.data.workspaceId).toBe(workspaceId) + expect(integration.data.type).toBe('DISCORD') + }) + + it('should update the integration', async () => { + const updatedIntegration: any = + await integrationController.updateIntegration( + { integrationId, name: 'Github second' }, + { 'x-e2e-user-email': email } + ) + expect(updatedIntegration.data.name).toBe('Github second') + }) + + it('should get an integration', async () => { + const integration: any = await integrationController.getIntegration( + { integrationId }, + { 'x-e2e-user-email': email } + ) + expect(integration).toBeDefined() + }) + + it('should get all integrations in workspace', async () => { + // Adding another integration + await integrationController.createIntegration( + { + workspaceId, + projectId, + name: 'Discord third', + type: 'DISCORD', + notifyOn: ['PROJECT_CREATED'], + metadata: { + webhookUrl: '{{vault:WEBHOOK_URL}}' + }, + environmentId: environment.id + }, + { + 'x-e2e-user-email': email + } + ) + const integrations: any = await integrationController.getAllIntegrations( + { workspaceId }, + { 'x-e2e-user-email': email } + ) + expect(integrations.data?.items.length).toBe(3) + }) + + it('should delete an integration', async () => { + await integrationController.deleteIntegration( + { integrationId }, + { 'x-e2e-user-email': email } + ) + const integrations: any = await integrationController.getAllIntegrations( + { workspaceId }, + { 'x-e2e-user-email': email } + ) + expect(integrations.data.items.length).toBe(2) + }) +})