mirror of
https://github.com/bitwarden/browser
synced 2025-12-22 03:03:43 +00:00
PM-26015 Datadog integration card (#16559)
* PM-26015 adding Datadog integration card * PM-26015 removing 2 changes * PM-26015 Removing 1 change * PM-26015 adding datadog integration card * PM-26015 fixing code to accept new toast owner changes * PM-26015 fixing linting error * PM-26015 fixing pr comment
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
import { OrganizationIntegrationServiceType } from "../organization-integration-service-type";
|
||||
|
||||
export class DatadogConfiguration {
|
||||
uri: string;
|
||||
apiKey: string;
|
||||
service: OrganizationIntegrationServiceType;
|
||||
|
||||
constructor(uri: string, apiKey: string, service: string) {
|
||||
this.uri = uri;
|
||||
this.apiKey = apiKey;
|
||||
this.service = service as OrganizationIntegrationServiceType;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { OrganizationIntegrationServiceType } from "../../organization-integration-service-type";
|
||||
|
||||
export class DatadogTemplate {
|
||||
source_type_name = "Bitwarden";
|
||||
title: string = "#Title#";
|
||||
text: string =
|
||||
"ActingUser: #ActingUserId#\nUser: #UserId#\nEvent: #Type#\nOrganization: #OrganizationId#\nPolicyId: #PolicyId#\nIpAddress: #IpAddress#\nDomainName: #DomainName#\nCipherId: #CipherId#\n";
|
||||
service: OrganizationIntegrationServiceType;
|
||||
|
||||
constructor(service: string) {
|
||||
this.service = service as OrganizationIntegrationServiceType;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IntegrationType } from "@bitwarden/common/enums/integration-type.enum";
|
||||
|
||||
import { OrganizationIntegration } from "./organization-integration";
|
||||
import { OrganizationIntegrationType } from "./organization-integration-type";
|
||||
|
||||
/** Integration or SDK */
|
||||
export type Integration = {
|
||||
@@ -23,6 +24,7 @@ export type Integration = {
|
||||
canSetupConnection?: boolean;
|
||||
configuration?: string;
|
||||
template?: string;
|
||||
integrationType?: OrganizationIntegrationType | null;
|
||||
|
||||
// OrganizationIntegration
|
||||
organizationIntegration?: OrganizationIntegration | null;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
OrganizationIntegrationId,
|
||||
} from "@bitwarden/common/types/guid";
|
||||
|
||||
import { DatadogTemplate } from "./integration-configuration-config/configuration-template/datadog-template";
|
||||
import { HecTemplate } from "./integration-configuration-config/configuration-template/hec-template";
|
||||
import { WebhookTemplate } from "./integration-configuration-config/configuration-template/webhook-template";
|
||||
import { WebhookIntegrationConfigurationConfig } from "./integration-configuration-config/webhook-integration-configuration-config";
|
||||
@@ -14,7 +15,7 @@ export class OrganizationIntegrationConfiguration {
|
||||
eventType?: EventType | null;
|
||||
configuration?: WebhookIntegrationConfigurationConfig | null;
|
||||
filters?: string;
|
||||
template?: HecTemplate | WebhookTemplate | null;
|
||||
template?: HecTemplate | WebhookTemplate | DatadogTemplate | null;
|
||||
|
||||
constructor(
|
||||
id: OrganizationIntegrationConfigurationId,
|
||||
@@ -22,7 +23,7 @@ export class OrganizationIntegrationConfiguration {
|
||||
eventType?: EventType | null,
|
||||
configuration?: WebhookIntegrationConfigurationConfig | null,
|
||||
filters?: string,
|
||||
template?: HecTemplate | WebhookTemplate | null,
|
||||
template?: HecTemplate | WebhookTemplate | DatadogTemplate | null,
|
||||
) {
|
||||
this.id = id;
|
||||
this.integrationId = integrationId;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const OrganizationIntegrationServiceType = Object.freeze({
|
||||
CrowdStrike: "CrowdStrike",
|
||||
Datadog: "Datadog",
|
||||
} as const);
|
||||
|
||||
export type OrganizationIntegrationServiceType =
|
||||
|
||||
@@ -4,6 +4,7 @@ export const OrganizationIntegrationType = Object.freeze({
|
||||
Slack: 3,
|
||||
Webhook: 4,
|
||||
Hec: 5,
|
||||
Datadog: 6,
|
||||
} as const);
|
||||
|
||||
export type OrganizationIntegrationType =
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { OrganizationIntegrationId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { DatadogConfiguration } from "./configuration/datadog-configuration";
|
||||
import { HecConfiguration } from "./configuration/hec-configuration";
|
||||
import { WebhookConfiguration } from "./configuration/webhook-configuration";
|
||||
import { OrganizationIntegrationConfiguration } from "./organization-integration-configuration";
|
||||
@@ -10,14 +11,14 @@ export class OrganizationIntegration {
|
||||
id: OrganizationIntegrationId;
|
||||
type: OrganizationIntegrationType;
|
||||
serviceType: OrganizationIntegrationServiceType;
|
||||
configuration: HecConfiguration | WebhookConfiguration | null;
|
||||
configuration: HecConfiguration | WebhookConfiguration | DatadogConfiguration | null;
|
||||
integrationConfiguration: OrganizationIntegrationConfiguration[] = [];
|
||||
|
||||
constructor(
|
||||
id: OrganizationIntegrationId,
|
||||
type: OrganizationIntegrationType,
|
||||
serviceType: OrganizationIntegrationServiceType,
|
||||
configuration: HecConfiguration | WebhookConfiguration | null,
|
||||
configuration: HecConfiguration | WebhookConfiguration | DatadogConfiguration | null,
|
||||
integrationConfiguration: OrganizationIntegrationConfiguration[] = [],
|
||||
) {
|
||||
this.id = id;
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import {
|
||||
OrganizationId,
|
||||
OrganizationIntegrationConfigurationId,
|
||||
OrganizationIntegrationId,
|
||||
} from "@bitwarden/common/types/guid";
|
||||
|
||||
import { DatadogConfiguration } from "../models/configuration/datadog-configuration";
|
||||
import { DatadogTemplate } from "../models/integration-configuration-config/configuration-template/datadog-template";
|
||||
import { OrganizationIntegration } from "../models/organization-integration";
|
||||
import { OrganizationIntegrationConfiguration } from "../models/organization-integration-configuration";
|
||||
import { OrganizationIntegrationConfigurationResponse } from "../models/organization-integration-configuration-response";
|
||||
import { OrganizationIntegrationResponse } from "../models/organization-integration-response";
|
||||
import { OrganizationIntegrationServiceType } from "../models/organization-integration-service-type";
|
||||
import { OrganizationIntegrationType } from "../models/organization-integration-type";
|
||||
|
||||
import { DatadogOrganizationIntegrationService } from "./datadog-organization-integration-service";
|
||||
import { OrganizationIntegrationApiService } from "./organization-integration-api.service";
|
||||
import { OrganizationIntegrationConfigurationApiService } from "./organization-integration-configuration-api.service";
|
||||
|
||||
describe("DatadogOrganizationIntegrationService", () => {
|
||||
let service: DatadogOrganizationIntegrationService;
|
||||
const mockIntegrationApiService = mock<OrganizationIntegrationApiService>();
|
||||
const mockIntegrationConfigurationApiService =
|
||||
mock<OrganizationIntegrationConfigurationApiService>();
|
||||
const organizationId = "org-1" as OrganizationId;
|
||||
const integrationId = "int-1" as OrganizationIntegrationId;
|
||||
const configId = "conf-1" as OrganizationIntegrationConfigurationId;
|
||||
const serviceType = OrganizationIntegrationServiceType.CrowdStrike;
|
||||
const url = "https://example.com";
|
||||
const apiKey = "token";
|
||||
|
||||
beforeEach(() => {
|
||||
service = new DatadogOrganizationIntegrationService(
|
||||
mockIntegrationApiService,
|
||||
mockIntegrationConfigurationApiService,
|
||||
);
|
||||
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should set organization integrations", (done) => {
|
||||
mockIntegrationApiService.getOrganizationIntegrations.mockResolvedValue([]);
|
||||
service.setOrganizationIntegrations(organizationId);
|
||||
const subscription = service.integrations$.subscribe((integrations) => {
|
||||
expect(integrations).toEqual([]);
|
||||
subscription.unsubscribe();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should save a new Datadog integration", async () => {
|
||||
service.setOrganizationIntegrations(organizationId);
|
||||
|
||||
const integrationResponse = {
|
||||
id: integrationId,
|
||||
type: OrganizationIntegrationType.Datadog,
|
||||
configuration: JSON.stringify({ url, apiKey, service: serviceType }),
|
||||
} as OrganizationIntegrationResponse;
|
||||
|
||||
const configResponse = {
|
||||
id: configId,
|
||||
template: JSON.stringify({ service: serviceType }),
|
||||
} as OrganizationIntegrationConfigurationResponse;
|
||||
|
||||
mockIntegrationApiService.createOrganizationIntegration.mockResolvedValue(integrationResponse);
|
||||
mockIntegrationConfigurationApiService.createOrganizationIntegrationConfiguration.mockResolvedValue(
|
||||
configResponse,
|
||||
);
|
||||
|
||||
await service.saveDatadog(organizationId, serviceType, url, apiKey);
|
||||
|
||||
const integrations = await firstValueFrom(service.integrations$);
|
||||
expect(integrations.length).toBe(1);
|
||||
expect(integrations[0].id).toBe(integrationId);
|
||||
expect(integrations[0].serviceType).toBe(serviceType);
|
||||
});
|
||||
|
||||
it("should throw error on organization ID mismatch in saveDatadog", async () => {
|
||||
service.setOrganizationIntegrations("other-org" as OrganizationId);
|
||||
await expect(service.saveDatadog(organizationId, serviceType, url, apiKey)).rejects.toThrow(
|
||||
Error("Organization ID mismatch"),
|
||||
);
|
||||
});
|
||||
|
||||
it("should update an existing Datadog integration", async () => {
|
||||
service.setOrganizationIntegrations(organizationId);
|
||||
|
||||
const integrationResponse = {
|
||||
id: integrationId,
|
||||
type: OrganizationIntegrationType.Datadog,
|
||||
configuration: JSON.stringify({ url, apiKey, service: serviceType }),
|
||||
} as OrganizationIntegrationResponse;
|
||||
|
||||
const configResponse = {
|
||||
id: configId,
|
||||
template: JSON.stringify({ service: serviceType }),
|
||||
} as OrganizationIntegrationConfigurationResponse;
|
||||
|
||||
mockIntegrationApiService.updateOrganizationIntegration.mockResolvedValue(integrationResponse);
|
||||
mockIntegrationConfigurationApiService.updateOrganizationIntegrationConfiguration.mockResolvedValue(
|
||||
configResponse,
|
||||
);
|
||||
|
||||
await service.updateDatadog(organizationId, integrationId, configId, serviceType, url, apiKey);
|
||||
|
||||
const integrations = await firstValueFrom(service.integrations$);
|
||||
expect(integrations.length).toBe(1);
|
||||
expect(integrations[0].id).toBe(integrationId);
|
||||
});
|
||||
|
||||
it("should throw error on organization ID mismatch in updateDatadog", async () => {
|
||||
service.setOrganizationIntegrations("other-org" as OrganizationId);
|
||||
await expect(
|
||||
service.updateDatadog(organizationId, integrationId, configId, serviceType, url, apiKey),
|
||||
).rejects.toThrow(Error("Organization ID mismatch"));
|
||||
});
|
||||
|
||||
it("should get integration by id", async () => {
|
||||
service["_integrations$"].next([
|
||||
new OrganizationIntegration(
|
||||
integrationId,
|
||||
OrganizationIntegrationType.Datadog,
|
||||
serviceType,
|
||||
{} as DatadogConfiguration,
|
||||
[],
|
||||
),
|
||||
]);
|
||||
const integration = await service.getIntegrationById(integrationId);
|
||||
expect(integration).not.toBeNull();
|
||||
expect(integration!.id).toBe(integrationId);
|
||||
});
|
||||
|
||||
it("should get integration by service type", async () => {
|
||||
service["_integrations$"].next([
|
||||
new OrganizationIntegration(
|
||||
integrationId,
|
||||
OrganizationIntegrationType.Datadog,
|
||||
serviceType,
|
||||
{} as DatadogConfiguration,
|
||||
[],
|
||||
),
|
||||
]);
|
||||
const integration = await service.getIntegrationByServiceType(serviceType);
|
||||
expect(integration).not.toBeNull();
|
||||
expect(integration!.serviceType).toBe(serviceType);
|
||||
});
|
||||
|
||||
it("should get integration configurations", async () => {
|
||||
const config = new OrganizationIntegrationConfiguration(
|
||||
configId,
|
||||
integrationId,
|
||||
null,
|
||||
null,
|
||||
"",
|
||||
{} as DatadogTemplate,
|
||||
);
|
||||
|
||||
service["_integrations$"].next([
|
||||
new OrganizationIntegration(
|
||||
integrationId,
|
||||
OrganizationIntegrationType.Datadog,
|
||||
serviceType,
|
||||
{} as DatadogConfiguration,
|
||||
[config],
|
||||
),
|
||||
]);
|
||||
const configs = await service.getIntegrationConfigurations(integrationId);
|
||||
expect(configs).not.toBeNull();
|
||||
expect(configs![0].id).toBe(configId);
|
||||
});
|
||||
|
||||
it("convertToJson should parse valid JSON", () => {
|
||||
const obj = service.convertToJson<{ a: number }>('{"a":1}');
|
||||
expect(obj).toEqual({ a: 1 });
|
||||
});
|
||||
|
||||
it("convertToJson should return null for invalid JSON", () => {
|
||||
const obj = service.convertToJson<{ a: number }>("invalid");
|
||||
expect(obj).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,350 @@
|
||||
import { BehaviorSubject, firstValueFrom, map, Subject, switchMap, takeUntil, zip } from "rxjs";
|
||||
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import {
|
||||
OrganizationId,
|
||||
OrganizationIntegrationId,
|
||||
OrganizationIntegrationConfigurationId,
|
||||
} from "@bitwarden/common/types/guid";
|
||||
|
||||
import { DatadogConfiguration } from "../models/configuration/datadog-configuration";
|
||||
import { DatadogTemplate } from "../models/integration-configuration-config/configuration-template/datadog-template";
|
||||
import { OrganizationIntegration } from "../models/organization-integration";
|
||||
import { OrganizationIntegrationConfiguration } from "../models/organization-integration-configuration";
|
||||
import { OrganizationIntegrationConfigurationRequest } from "../models/organization-integration-configuration-request";
|
||||
import { OrganizationIntegrationConfigurationResponse } from "../models/organization-integration-configuration-response";
|
||||
import { OrganizationIntegrationRequest } from "../models/organization-integration-request";
|
||||
import { OrganizationIntegrationResponse } from "../models/organization-integration-response";
|
||||
import { OrganizationIntegrationServiceType } from "../models/organization-integration-service-type";
|
||||
import { OrganizationIntegrationType } from "../models/organization-integration-type";
|
||||
|
||||
import { OrganizationIntegrationApiService } from "./organization-integration-api.service";
|
||||
import { OrganizationIntegrationConfigurationApiService } from "./organization-integration-configuration-api.service";
|
||||
|
||||
export type DatadogModificationFailureReason = {
|
||||
mustBeOwner: boolean;
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
export class DatadogOrganizationIntegrationService {
|
||||
private organizationId$ = new BehaviorSubject<OrganizationId | null>(null);
|
||||
private _integrations$ = new BehaviorSubject<OrganizationIntegration[]>([]);
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
integrations$ = this._integrations$.asObservable();
|
||||
|
||||
private fetch$ = this.organizationId$
|
||||
.pipe(
|
||||
switchMap(async (orgId) => {
|
||||
if (orgId) {
|
||||
const data$ = await this.setIntegrations(orgId);
|
||||
return await firstValueFrom(data$);
|
||||
} else {
|
||||
return this._integrations$.getValue();
|
||||
}
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe({
|
||||
next: (integrations) => {
|
||||
this._integrations$.next(integrations);
|
||||
},
|
||||
});
|
||||
|
||||
constructor(
|
||||
private integrationApiService: OrganizationIntegrationApiService,
|
||||
private integrationConfigurationApiService: OrganizationIntegrationConfigurationApiService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Sets the organization Id and will trigger the retrieval of the
|
||||
* integrations for a given org.
|
||||
* @param orgId
|
||||
*/
|
||||
setOrganizationIntegrations(orgId: OrganizationId) {
|
||||
this.organizationId$.next(orgId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a new organization integration and updates the integrations$ observable
|
||||
* @param organizationId id of the organization
|
||||
* @param service service type of the integration
|
||||
* @param url url of the service
|
||||
* @param apiKey api token
|
||||
*/
|
||||
async saveDatadog(
|
||||
organizationId: OrganizationId,
|
||||
service: OrganizationIntegrationServiceType,
|
||||
url: string,
|
||||
apiKey: string,
|
||||
): Promise<DatadogModificationFailureReason> {
|
||||
if (organizationId != this.organizationId$.getValue()) {
|
||||
throw new Error("Organization ID mismatch");
|
||||
}
|
||||
|
||||
try {
|
||||
const datadogConfig = new DatadogConfiguration(url, apiKey, service);
|
||||
const newIntegrationResponse = await this.integrationApiService.createOrganizationIntegration(
|
||||
organizationId,
|
||||
new OrganizationIntegrationRequest(
|
||||
OrganizationIntegrationType.Datadog,
|
||||
datadogConfig.toString(),
|
||||
),
|
||||
);
|
||||
|
||||
const newTemplate = new DatadogTemplate(service);
|
||||
const newIntegrationConfigResponse =
|
||||
await this.integrationConfigurationApiService.createOrganizationIntegrationConfiguration(
|
||||
organizationId,
|
||||
newIntegrationResponse.id,
|
||||
new OrganizationIntegrationConfigurationRequest(null, null, null, newTemplate.toString()),
|
||||
);
|
||||
|
||||
const newIntegration = this.mapResponsesToOrganizationIntegration(
|
||||
newIntegrationResponse,
|
||||
newIntegrationConfigResponse,
|
||||
);
|
||||
if (newIntegration !== null) {
|
||||
this._integrations$.next([...this._integrations$.getValue(), newIntegration]);
|
||||
}
|
||||
return { mustBeOwner: false, success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof ErrorResponse && error.statusCode === 404) {
|
||||
return { mustBeOwner: true, success: false };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing organization integration and updates the integrations$ observable
|
||||
* @param organizationId id of the organization
|
||||
* @param OrganizationIntegrationId id of the organization integration
|
||||
* @param OrganizationIntegrationConfigurationId id of the organization integration configuration
|
||||
* @param service service type of the integration
|
||||
* @param url url of the service
|
||||
* @param apiKey api token
|
||||
*/
|
||||
async updateDatadog(
|
||||
organizationId: OrganizationId,
|
||||
OrganizationIntegrationId: OrganizationIntegrationId,
|
||||
OrganizationIntegrationConfigurationId: OrganizationIntegrationConfigurationId,
|
||||
service: OrganizationIntegrationServiceType,
|
||||
url: string,
|
||||
apiKey: string,
|
||||
): Promise<DatadogModificationFailureReason> {
|
||||
if (organizationId != this.organizationId$.getValue()) {
|
||||
throw new Error("Organization ID mismatch");
|
||||
}
|
||||
|
||||
try {
|
||||
const datadogConfig = new DatadogConfiguration(url, apiKey, service);
|
||||
const updatedIntegrationResponse =
|
||||
await this.integrationApiService.updateOrganizationIntegration(
|
||||
organizationId,
|
||||
OrganizationIntegrationId,
|
||||
new OrganizationIntegrationRequest(
|
||||
OrganizationIntegrationType.Datadog,
|
||||
datadogConfig.toString(),
|
||||
),
|
||||
);
|
||||
|
||||
const updatedTemplate = new DatadogTemplate(service);
|
||||
const updatedIntegrationConfigResponse =
|
||||
await this.integrationConfigurationApiService.updateOrganizationIntegrationConfiguration(
|
||||
organizationId,
|
||||
OrganizationIntegrationId,
|
||||
OrganizationIntegrationConfigurationId,
|
||||
new OrganizationIntegrationConfigurationRequest(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
updatedTemplate.toString(),
|
||||
),
|
||||
);
|
||||
|
||||
const updatedIntegration = this.mapResponsesToOrganizationIntegration(
|
||||
updatedIntegrationResponse,
|
||||
updatedIntegrationConfigResponse,
|
||||
);
|
||||
|
||||
if (updatedIntegration !== null) {
|
||||
this._integrations$.next([...this._integrations$.getValue(), updatedIntegration]);
|
||||
}
|
||||
return { mustBeOwner: false, success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof ErrorResponse && error.statusCode === 404) {
|
||||
return { mustBeOwner: true, success: false };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteDatadog(
|
||||
organizationId: OrganizationId,
|
||||
OrganizationIntegrationId: OrganizationIntegrationId,
|
||||
OrganizationIntegrationConfigurationId: OrganizationIntegrationConfigurationId,
|
||||
): Promise<DatadogModificationFailureReason> {
|
||||
if (organizationId != this.organizationId$.getValue()) {
|
||||
throw new Error("Organization ID mismatch");
|
||||
}
|
||||
|
||||
try {
|
||||
// delete the configuration first due to foreign key constraint
|
||||
await this.integrationConfigurationApiService.deleteOrganizationIntegrationConfiguration(
|
||||
organizationId,
|
||||
OrganizationIntegrationId,
|
||||
OrganizationIntegrationConfigurationId,
|
||||
);
|
||||
|
||||
// delete the integration
|
||||
await this.integrationApiService.deleteOrganizationIntegration(
|
||||
organizationId,
|
||||
OrganizationIntegrationId,
|
||||
);
|
||||
|
||||
// update the local observable
|
||||
const updatedIntegrations = this._integrations$
|
||||
.getValue()
|
||||
.filter((i) => i.id !== OrganizationIntegrationId);
|
||||
this._integrations$.next(updatedIntegrations);
|
||||
|
||||
return { mustBeOwner: false, success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof ErrorResponse && error.statusCode === 404) {
|
||||
return { mustBeOwner: true, success: false };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a OrganizationIntegration for an OrganizationIntegrationId
|
||||
* @param integrationId id of the integration
|
||||
* @returns OrganizationIntegration or null
|
||||
*/
|
||||
// TODO: Move to base class when another service integration type is implemented
|
||||
async getIntegrationById(
|
||||
integrationId: OrganizationIntegrationId,
|
||||
): Promise<OrganizationIntegration | null> {
|
||||
return await firstValueFrom(
|
||||
this.integrations$.pipe(
|
||||
map((integrations) => integrations.find((i) => i.id === integrationId) || null),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a OrganizationIntegration for a service type
|
||||
* @param serviceType type of the service
|
||||
* @returns OrganizationIntegration or null
|
||||
*/
|
||||
// TODO: Move to base class when another service integration type is implemented
|
||||
async getIntegrationByServiceType(
|
||||
serviceType: OrganizationIntegrationServiceType,
|
||||
): Promise<OrganizationIntegration | null> {
|
||||
return await firstValueFrom(
|
||||
this.integrations$.pipe(
|
||||
map((integrations) => integrations.find((i) => i.serviceType === serviceType) || null),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a OrganizationIntegrationConfigurations for an integration ID
|
||||
* @param integrationId id of the integration
|
||||
* @returns OrganizationIntegration array or null
|
||||
*/
|
||||
// TODO: Move to base class when another service integration type is implemented
|
||||
async getIntegrationConfigurations(
|
||||
integrationId: OrganizationIntegrationId,
|
||||
): Promise<OrganizationIntegrationConfiguration[] | null> {
|
||||
return await firstValueFrom(
|
||||
this.integrations$.pipe(
|
||||
map((integrations) => {
|
||||
const integration = integrations.find((i) => i.id === integrationId);
|
||||
return integration ? integration.integrationConfiguration : null;
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Move to data models to be more explicit for future services
|
||||
private mapResponsesToOrganizationIntegration(
|
||||
integrationResponse: OrganizationIntegrationResponse,
|
||||
configurationResponse: OrganizationIntegrationConfigurationResponse,
|
||||
): OrganizationIntegration | null {
|
||||
const datadogConfig = this.convertToJson<DatadogConfiguration>(
|
||||
integrationResponse.configuration,
|
||||
);
|
||||
const template = this.convertToJson<DatadogTemplate>(configurationResponse.template);
|
||||
|
||||
if (!datadogConfig || !template) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const integrationConfig = new OrganizationIntegrationConfiguration(
|
||||
configurationResponse.id,
|
||||
integrationResponse.id,
|
||||
null,
|
||||
null,
|
||||
"",
|
||||
template,
|
||||
);
|
||||
|
||||
return new OrganizationIntegration(
|
||||
integrationResponse.id,
|
||||
integrationResponse.type,
|
||||
datadogConfig.service,
|
||||
datadogConfig,
|
||||
[integrationConfig],
|
||||
);
|
||||
}
|
||||
|
||||
// Could possibly be moved to a base service. All services would then assume that the
|
||||
// integration configuration would always be an array and this datadog specific service
|
||||
// would just assume a single entry.
|
||||
private setIntegrations(orgId: OrganizationId) {
|
||||
const results$ = zip(this.integrationApiService.getOrganizationIntegrations(orgId)).pipe(
|
||||
switchMap(([responses]) => {
|
||||
const integrations: OrganizationIntegration[] = [];
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
responses.forEach((integration) => {
|
||||
if (integration.type === OrganizationIntegrationType.Datadog) {
|
||||
const promise = this.integrationConfigurationApiService
|
||||
.getOrganizationIntegrationConfigurations(orgId, integration.id)
|
||||
.then((response) => {
|
||||
// datadog events will only have one OrganizationIntegrationConfiguration
|
||||
const config = response[0];
|
||||
|
||||
const orgIntegration = this.mapResponsesToOrganizationIntegration(
|
||||
integration,
|
||||
config,
|
||||
);
|
||||
|
||||
if (orgIntegration !== null) {
|
||||
integrations.push(orgIntegration);
|
||||
}
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
});
|
||||
return Promise.all(promises).then(() => {
|
||||
return integrations;
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
return results$;
|
||||
}
|
||||
|
||||
// TODO: Move to base service when necessary
|
||||
convertToJson<T>(jsonString?: string): T | null {
|
||||
try {
|
||||
return JSON.parse(jsonString || "") as T;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,22 +311,24 @@ export class HecOrganizationIntegrationService {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
responses.forEach((integration) => {
|
||||
const promise = this.integrationConfigurationApiService
|
||||
.getOrganizationIntegrationConfigurations(orgId, integration.id)
|
||||
.then((response) => {
|
||||
// Hec events will only have one OrganizationIntegrationConfiguration
|
||||
const config = response[0];
|
||||
if (integration.type === OrganizationIntegrationType.Hec) {
|
||||
const promise = this.integrationConfigurationApiService
|
||||
.getOrganizationIntegrationConfigurations(orgId, integration.id)
|
||||
.then((response) => {
|
||||
// Hec events will only have one OrganizationIntegrationConfiguration
|
||||
const config = response[0];
|
||||
|
||||
const orgIntegration = this.mapResponsesToOrganizationIntegration(
|
||||
integration,
|
||||
config,
|
||||
);
|
||||
const orgIntegration = this.mapResponsesToOrganizationIntegration(
|
||||
integration,
|
||||
config,
|
||||
);
|
||||
|
||||
if (orgIntegration !== null) {
|
||||
integrations.push(orgIntegration);
|
||||
}
|
||||
});
|
||||
promises.push(promise);
|
||||
if (orgIntegration !== null) {
|
||||
integrations.push(orgIntegration);
|
||||
}
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
});
|
||||
return Promise.all(promises).then(() => {
|
||||
return integrations;
|
||||
|
||||
Reference in New Issue
Block a user