mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
feat:metrics for outgoing integrations (#7921)
This commit is contained in:
parent
58f2b5ab1c
commit
e714a7fe2b
@ -4,6 +4,7 @@ import noLogger from '../../test/fixtures/no-logger';
|
|||||||
import SlackAddon from './slack';
|
import SlackAddon from './slack';
|
||||||
import type { IAddonConfig, IFlagResolver } from '../types';
|
import type { IAddonConfig, IFlagResolver } from '../types';
|
||||||
import type { IntegrationEventsService } from '../services';
|
import type { IntegrationEventsService } from '../services';
|
||||||
|
import type EventEmitter from 'events';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
nock.disableNetConnect();
|
nock.disableNetConnect();
|
||||||
@ -16,6 +17,7 @@ const ARGS: IAddonConfig = {
|
|||||||
unleashUrl: url,
|
unleashUrl: url,
|
||||||
integrationEventsService: {} as IntegrationEventsService,
|
integrationEventsService: {} as IntegrationEventsService,
|
||||||
flagResolver: {} as IFlagResolver,
|
flagResolver: {} as IFlagResolver,
|
||||||
|
eventBus: {} as EventEmitter,
|
||||||
};
|
};
|
||||||
|
|
||||||
test('Does not retry if request succeeds', async () => {
|
test('Does not retry if request succeeds', async () => {
|
||||||
|
@ -5,6 +5,7 @@ import type { IAddonConfig, IAddonDefinition } from '../types/model';
|
|||||||
import type { IEvent } from '../types/events';
|
import type { IEvent } from '../types/events';
|
||||||
import type { IntegrationEventsService } from '../features/integration-events/integration-events-service';
|
import type { IntegrationEventsService } from '../features/integration-events/integration-events-service';
|
||||||
import type { IntegrationEventWriteModel } from '../features/integration-events/integration-events-store';
|
import type { IntegrationEventWriteModel } from '../features/integration-events/integration-events-store';
|
||||||
|
import type EventEmitter from 'events';
|
||||||
import type { IFlagResolver } from '../types';
|
import type { IFlagResolver } from '../types';
|
||||||
|
|
||||||
export default abstract class Addon {
|
export default abstract class Addon {
|
||||||
@ -16,11 +17,18 @@ export default abstract class Addon {
|
|||||||
|
|
||||||
integrationEventsService: IntegrationEventsService;
|
integrationEventsService: IntegrationEventsService;
|
||||||
|
|
||||||
|
eventBus: EventEmitter;
|
||||||
|
|
||||||
flagResolver: IFlagResolver;
|
flagResolver: IFlagResolver;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
definition: IAddonDefinition,
|
definition: IAddonDefinition,
|
||||||
{ getLogger, integrationEventsService, flagResolver }: IAddonConfig,
|
{
|
||||||
|
getLogger,
|
||||||
|
integrationEventsService,
|
||||||
|
flagResolver,
|
||||||
|
eventBus,
|
||||||
|
}: IAddonConfig,
|
||||||
) {
|
) {
|
||||||
this.logger = getLogger(`addon/${definition.name}`);
|
this.logger = getLogger(`addon/${definition.name}`);
|
||||||
const { error } = addonDefinitionSchema.validate(definition);
|
const { error } = addonDefinitionSchema.validate(definition);
|
||||||
@ -34,6 +42,7 @@ export default abstract class Addon {
|
|||||||
this._name = definition.name;
|
this._name = definition.name;
|
||||||
this._definition = definition;
|
this._definition = definition;
|
||||||
this.integrationEventsService = integrationEventsService;
|
this.integrationEventsService = integrationEventsService;
|
||||||
|
this.eventBus = eventBus;
|
||||||
this.flagResolver = flagResolver;
|
this.flagResolver = flagResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import DatadogAddon from './datadog';
|
|||||||
|
|
||||||
import noLogger from '../../test/fixtures/no-logger';
|
import noLogger from '../../test/fixtures/no-logger';
|
||||||
import {
|
import {
|
||||||
|
type IFlagKey,
|
||||||
serializeDates,
|
serializeDates,
|
||||||
type IAddonConfig,
|
type IAddonConfig,
|
||||||
type IFlagResolver,
|
type IFlagResolver,
|
||||||
@ -24,7 +25,8 @@ const ARGS: IAddonConfig = {
|
|||||||
getLogger: noLogger,
|
getLogger: noLogger,
|
||||||
unleashUrl: 'http://some-url.com',
|
unleashUrl: 'http://some-url.com',
|
||||||
integrationEventsService: {} as IntegrationEventsService,
|
integrationEventsService: {} as IntegrationEventsService,
|
||||||
flagResolver: {} as IFlagResolver,
|
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
|
||||||
|
eventBus: {} as any,
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
|
@ -2,7 +2,11 @@ import Addon from './addon';
|
|||||||
|
|
||||||
import definition from './datadog-definition';
|
import definition from './datadog-definition';
|
||||||
import Mustache from 'mustache';
|
import Mustache from 'mustache';
|
||||||
import { type IAddonConfig, serializeDates } from '../types';
|
import {
|
||||||
|
type IAddonConfig,
|
||||||
|
type IFlagResolver,
|
||||||
|
serializeDates,
|
||||||
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
type FeatureEventFormatter,
|
type FeatureEventFormatter,
|
||||||
FeatureEventFormatterMd,
|
FeatureEventFormatterMd,
|
||||||
@ -10,6 +14,7 @@ import {
|
|||||||
} from './feature-event-formatter-md';
|
} from './feature-event-formatter-md';
|
||||||
import type { IEvent } from '../types/events';
|
import type { IEvent } from '../types/events';
|
||||||
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
||||||
|
import { ADDON_EVENTS_HANDLED } from '../metric-events';
|
||||||
|
|
||||||
interface IDatadogParameters {
|
interface IDatadogParameters {
|
||||||
url: string;
|
url: string;
|
||||||
@ -29,12 +34,15 @@ interface DDRequestBody {
|
|||||||
export default class DatadogAddon extends Addon {
|
export default class DatadogAddon extends Addon {
|
||||||
private msgFormatter: FeatureEventFormatter;
|
private msgFormatter: FeatureEventFormatter;
|
||||||
|
|
||||||
|
flagResolver: IFlagResolver;
|
||||||
|
|
||||||
constructor(config: IAddonConfig) {
|
constructor(config: IAddonConfig) {
|
||||||
super(definition, config);
|
super(definition, config);
|
||||||
this.msgFormatter = new FeatureEventFormatterMd(
|
this.msgFormatter = new FeatureEventFormatterMd(
|
||||||
config.unleashUrl,
|
config.unleashUrl,
|
||||||
LinkStyle.MD,
|
LinkStyle.MD,
|
||||||
);
|
);
|
||||||
|
this.flagResolver = config.flagResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEvent(
|
async handleEvent(
|
||||||
@ -107,6 +115,13 @@ export default class DatadogAddon extends Addon {
|
|||||||
state = 'failed';
|
state = 'failed';
|
||||||
const failedMessage = `Datadog Events API request failed with status code: ${res.status}.`;
|
const failedMessage = `Datadog Events API request failed with status code: ${res.status}.`;
|
||||||
stateDetails.push(failedMessage);
|
stateDetails.push(failedMessage);
|
||||||
|
if (this.flagResolver.isEnabled('addonUsageMetrics')) {
|
||||||
|
this.eventBus.emit(ADDON_EVENTS_HANDLED, {
|
||||||
|
result: state,
|
||||||
|
destination: 'datadog',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.warn(failedMessage);
|
this.logger.warn(failedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
type IAddonConfig,
|
type IAddonConfig,
|
||||||
type IEvent,
|
type IEvent,
|
||||||
serializeDates,
|
serializeDates,
|
||||||
|
type IFlagKey,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import type { Logger } from '../logger';
|
import type { Logger } from '../logger';
|
||||||
|
|
||||||
@ -26,7 +27,8 @@ const ARGS: IAddonConfig = {
|
|||||||
getLogger: noLogger,
|
getLogger: noLogger,
|
||||||
unleashUrl: 'http://some-url.com',
|
unleashUrl: 'http://some-url.com',
|
||||||
integrationEventsService: {} as IntegrationEventsService,
|
integrationEventsService: {} as IntegrationEventsService,
|
||||||
flagResolver: {} as IFlagResolver,
|
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
|
||||||
|
eventBus: {} as any,
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
type IAddonConfig,
|
type IAddonConfig,
|
||||||
type IEvent,
|
type IEvent,
|
||||||
type IEventType,
|
type IEventType,
|
||||||
|
type IFlagResolver,
|
||||||
serializeDates,
|
serializeDates,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
@ -16,6 +17,7 @@ import {
|
|||||||
import { gzip } from 'node:zlib';
|
import { gzip } from 'node:zlib';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
||||||
|
import { ADDON_EVENTS_HANDLED } from '../metric-events';
|
||||||
|
|
||||||
const asyncGzip = promisify(gzip);
|
const asyncGzip = promisify(gzip);
|
||||||
|
|
||||||
@ -39,12 +41,15 @@ interface INewRelicRequestBody {
|
|||||||
export default class NewRelicAddon extends Addon {
|
export default class NewRelicAddon extends Addon {
|
||||||
private msgFormatter: FeatureEventFormatter;
|
private msgFormatter: FeatureEventFormatter;
|
||||||
|
|
||||||
|
flagResolver: IFlagResolver;
|
||||||
|
|
||||||
constructor(config: IAddonConfig) {
|
constructor(config: IAddonConfig) {
|
||||||
super(definition, config);
|
super(definition, config);
|
||||||
this.msgFormatter = new FeatureEventFormatterMd(
|
this.msgFormatter = new FeatureEventFormatterMd(
|
||||||
config.unleashUrl,
|
config.unleashUrl,
|
||||||
LinkStyle.MD,
|
LinkStyle.MD,
|
||||||
);
|
);
|
||||||
|
this.flagResolver = config.flagResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEvent(
|
async handleEvent(
|
||||||
@ -117,6 +122,13 @@ export default class NewRelicAddon extends Addon {
|
|||||||
this.logger.warn(failedMessage);
|
this.logger.warn(failedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.flagResolver.isEnabled('addonUsageMetrics')) {
|
||||||
|
this.eventBus.emit(ADDON_EVENTS_HANDLED, {
|
||||||
|
result: state,
|
||||||
|
destination: 'new-relic',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.registerEvent({
|
this.registerEvent({
|
||||||
integrationId,
|
integrationId,
|
||||||
state,
|
state,
|
||||||
|
@ -3,6 +3,7 @@ import SlackAppAddon from './slack-app';
|
|||||||
import { type ChatPostMessageArguments, ErrorCode } from '@slack/web-api';
|
import { type ChatPostMessageArguments, ErrorCode } from '@slack/web-api';
|
||||||
import {
|
import {
|
||||||
type IAddonConfig,
|
type IAddonConfig,
|
||||||
|
type IFlagKey,
|
||||||
type IFlagResolver,
|
type IFlagResolver,
|
||||||
serializeDates,
|
serializeDates,
|
||||||
SYSTEM_USER_ID,
|
SYSTEM_USER_ID,
|
||||||
@ -28,7 +29,8 @@ const ARGS: IAddonConfig = {
|
|||||||
getLogger,
|
getLogger,
|
||||||
unleashUrl: 'http://some-url.com',
|
unleashUrl: 'http://some-url.com',
|
||||||
integrationEventsService: {} as IntegrationEventsService,
|
integrationEventsService: {} as IntegrationEventsService,
|
||||||
flagResolver: {} as IFlagResolver,
|
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
|
||||||
|
eventBus: {} as any,
|
||||||
};
|
};
|
||||||
|
|
||||||
let postMessage = jest.fn().mockImplementation((options) => {
|
let postMessage = jest.fn().mockImplementation((options) => {
|
||||||
|
@ -13,7 +13,11 @@ import {
|
|||||||
import Addon from './addon';
|
import Addon from './addon';
|
||||||
|
|
||||||
import slackAppDefinition from './slack-app-definition';
|
import slackAppDefinition from './slack-app-definition';
|
||||||
import { type IAddonConfig, serializeDates } from '../types';
|
import {
|
||||||
|
type IAddonConfig,
|
||||||
|
type IFlagResolver,
|
||||||
|
serializeDates,
|
||||||
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
type FeatureEventFormatter,
|
type FeatureEventFormatter,
|
||||||
FeatureEventFormatterMd,
|
FeatureEventFormatterMd,
|
||||||
@ -21,6 +25,7 @@ import {
|
|||||||
} from './feature-event-formatter-md';
|
} from './feature-event-formatter-md';
|
||||||
import type { IEvent } from '../types/events';
|
import type { IEvent } from '../types/events';
|
||||||
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
||||||
|
import { ADDON_EVENTS_HANDLED } from '../metric-events';
|
||||||
|
|
||||||
interface ISlackAppAddonParameters {
|
interface ISlackAppAddonParameters {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
@ -30,6 +35,8 @@ interface ISlackAppAddonParameters {
|
|||||||
export default class SlackAppAddon extends Addon {
|
export default class SlackAppAddon extends Addon {
|
||||||
private msgFormatter: FeatureEventFormatter;
|
private msgFormatter: FeatureEventFormatter;
|
||||||
|
|
||||||
|
flagResolver: IFlagResolver;
|
||||||
|
|
||||||
private accessToken?: string;
|
private accessToken?: string;
|
||||||
|
|
||||||
private slackClient?: WebClient;
|
private slackClient?: WebClient;
|
||||||
@ -40,6 +47,7 @@ export default class SlackAppAddon extends Addon {
|
|||||||
args.unleashUrl,
|
args.unleashUrl,
|
||||||
LinkStyle.SLACK,
|
LinkStyle.SLACK,
|
||||||
);
|
);
|
||||||
|
this.flagResolver = args.flagResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEvent(
|
async handleEvent(
|
||||||
@ -168,6 +176,13 @@ export default class SlackAppAddon extends Addon {
|
|||||||
stateDetails.push(eventErrorMessage);
|
stateDetails.push(eventErrorMessage);
|
||||||
this.logger.warn(eventErrorMessage);
|
this.logger.warn(eventErrorMessage);
|
||||||
const errorMessage = this.parseError(error);
|
const errorMessage = this.parseError(error);
|
||||||
|
if (this.flagResolver.isEnabled('addonUsageMetrics')) {
|
||||||
|
this.eventBus.emit(ADDON_EVENTS_HANDLED, {
|
||||||
|
result: state,
|
||||||
|
destination: 'slack-app',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
stateDetails.push(errorMessage);
|
stateDetails.push(errorMessage);
|
||||||
this.logger.warn(errorMessage, error);
|
this.logger.warn(errorMessage, error);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -11,6 +11,7 @@ import SlackAddon from './slack';
|
|||||||
import noLogger from '../../test/fixtures/no-logger';
|
import noLogger from '../../test/fixtures/no-logger';
|
||||||
import {
|
import {
|
||||||
type IAddonConfig,
|
type IAddonConfig,
|
||||||
|
type IFlagKey,
|
||||||
type IFlagResolver,
|
type IFlagResolver,
|
||||||
serializeDates,
|
serializeDates,
|
||||||
SYSTEM_USER_ID,
|
SYSTEM_USER_ID,
|
||||||
@ -25,7 +26,8 @@ const ARGS: IAddonConfig = {
|
|||||||
getLogger: noLogger,
|
getLogger: noLogger,
|
||||||
unleashUrl: 'http://some-url.com',
|
unleashUrl: 'http://some-url.com',
|
||||||
integrationEventsService: {} as IntegrationEventsService,
|
integrationEventsService: {} as IntegrationEventsService,
|
||||||
flagResolver: {} as IFlagResolver,
|
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
|
||||||
|
eventBus: {} as any,
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import Addon from './addon';
|
import Addon from './addon';
|
||||||
|
|
||||||
import slackDefinition from './slack-definition';
|
import slackDefinition from './slack-definition';
|
||||||
import { type IAddonConfig, serializeDates } from '../types';
|
import {
|
||||||
|
type IAddonConfig,
|
||||||
|
type IFlagResolver,
|
||||||
|
serializeDates,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type FeatureEventFormatter,
|
type FeatureEventFormatter,
|
||||||
@ -10,6 +14,7 @@ import {
|
|||||||
} from './feature-event-formatter-md';
|
} from './feature-event-formatter-md';
|
||||||
import type { IEvent } from '../types/events';
|
import type { IEvent } from '../types/events';
|
||||||
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
||||||
|
import { ADDON_EVENTS_HANDLED } from '../metric-events';
|
||||||
|
|
||||||
interface ISlackAddonParameters {
|
interface ISlackAddonParameters {
|
||||||
url: string;
|
url: string;
|
||||||
@ -21,12 +26,15 @@ interface ISlackAddonParameters {
|
|||||||
export default class SlackAddon extends Addon {
|
export default class SlackAddon extends Addon {
|
||||||
private msgFormatter: FeatureEventFormatter;
|
private msgFormatter: FeatureEventFormatter;
|
||||||
|
|
||||||
|
flagResolver: IFlagResolver;
|
||||||
|
|
||||||
constructor(args: IAddonConfig) {
|
constructor(args: IAddonConfig) {
|
||||||
super(slackDefinition, args);
|
super(slackDefinition, args);
|
||||||
this.msgFormatter = new FeatureEventFormatterMd(
|
this.msgFormatter = new FeatureEventFormatterMd(
|
||||||
args.unleashUrl,
|
args.unleashUrl,
|
||||||
LinkStyle.SLACK,
|
LinkStyle.SLACK,
|
||||||
);
|
);
|
||||||
|
this.flagResolver = args.flagResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
@ -121,6 +129,13 @@ export default class SlackAddon extends Addon {
|
|||||||
state = 'successWithErrors';
|
state = 'successWithErrors';
|
||||||
const successWithErrorsMessage = `Some (${failedRequests.length} of ${results.length}) Slack webhook requests failed. Status codes: ${codes}.`;
|
const successWithErrorsMessage = `Some (${failedRequests.length} of ${results.length}) Slack webhook requests failed. Status codes: ${codes}.`;
|
||||||
stateDetails.push(successWithErrorsMessage);
|
stateDetails.push(successWithErrorsMessage);
|
||||||
|
if (this.flagResolver.isEnabled('addonUsageMetrics')) {
|
||||||
|
this.eventBus.emit(ADDON_EVENTS_HANDLED, {
|
||||||
|
result: state,
|
||||||
|
destination: 'slack',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.warn(successWithErrorsMessage);
|
this.logger.warn(successWithErrorsMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import TeamsAddon from './teams';
|
|||||||
import noLogger from '../../test/fixtures/no-logger';
|
import noLogger from '../../test/fixtures/no-logger';
|
||||||
import {
|
import {
|
||||||
type IAddonConfig,
|
type IAddonConfig,
|
||||||
|
type IFlagKey,
|
||||||
type IFlagResolver,
|
type IFlagResolver,
|
||||||
serializeDates,
|
serializeDates,
|
||||||
SYSTEM_USER_ID,
|
SYSTEM_USER_ID,
|
||||||
@ -26,7 +27,8 @@ const ARGS: IAddonConfig = {
|
|||||||
getLogger: noLogger,
|
getLogger: noLogger,
|
||||||
unleashUrl: 'http://some-url.com',
|
unleashUrl: 'http://some-url.com',
|
||||||
integrationEventsService: {} as IntegrationEventsService,
|
integrationEventsService: {} as IntegrationEventsService,
|
||||||
flagResolver: {} as IFlagResolver,
|
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
|
||||||
|
eventBus: {} as any,
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import Addon from './addon';
|
import Addon from './addon';
|
||||||
|
|
||||||
import teamsDefinition from './teams-definition';
|
import teamsDefinition from './teams-definition';
|
||||||
import { type IAddonConfig, serializeDates } from '../types';
|
import {
|
||||||
|
type IAddonConfig,
|
||||||
|
type IFlagResolver,
|
||||||
|
serializeDates,
|
||||||
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
type FeatureEventFormatter,
|
type FeatureEventFormatter,
|
||||||
FeatureEventFormatterMd,
|
FeatureEventFormatterMd,
|
||||||
} from './feature-event-formatter-md';
|
} from './feature-event-formatter-md';
|
||||||
import type { IEvent } from '../types/events';
|
import type { IEvent } from '../types/events';
|
||||||
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
||||||
|
import { ADDON_EVENTS_HANDLED } from '../metric-events';
|
||||||
|
|
||||||
interface ITeamsParameters {
|
interface ITeamsParameters {
|
||||||
url: string;
|
url: string;
|
||||||
@ -16,9 +21,12 @@ interface ITeamsParameters {
|
|||||||
export default class TeamsAddon extends Addon {
|
export default class TeamsAddon extends Addon {
|
||||||
private msgFormatter: FeatureEventFormatter;
|
private msgFormatter: FeatureEventFormatter;
|
||||||
|
|
||||||
|
flagResolver: IFlagResolver;
|
||||||
|
|
||||||
constructor(args: IAddonConfig) {
|
constructor(args: IAddonConfig) {
|
||||||
super(teamsDefinition, args);
|
super(teamsDefinition, args);
|
||||||
this.msgFormatter = new FeatureEventFormatterMd(args.unleashUrl);
|
this.msgFormatter = new FeatureEventFormatterMd(args.unleashUrl);
|
||||||
|
this.flagResolver = args.flagResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
@ -97,6 +105,13 @@ export default class TeamsAddon extends Addon {
|
|||||||
state = 'failed';
|
state = 'failed';
|
||||||
const failedMessage = `Teams webhook request failed with status code: ${res.status}.`;
|
const failedMessage = `Teams webhook request failed with status code: ${res.status}.`;
|
||||||
stateDetails.push(failedMessage);
|
stateDetails.push(failedMessage);
|
||||||
|
if (this.flagResolver.isEnabled('addonUsageMetrics')) {
|
||||||
|
this.eventBus.emit(ADDON_EVENTS_HANDLED, {
|
||||||
|
result: state,
|
||||||
|
destination: 'teams',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.warn(failedMessage);
|
this.logger.warn(failedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import WebhookAddon from './webhook';
|
|||||||
import noLogger from '../../test/fixtures/no-logger';
|
import noLogger from '../../test/fixtures/no-logger';
|
||||||
import {
|
import {
|
||||||
type IAddonConfig,
|
type IAddonConfig,
|
||||||
|
type IFlagKey,
|
||||||
type IFlagResolver,
|
type IFlagResolver,
|
||||||
serializeDates,
|
serializeDates,
|
||||||
SYSTEM_USER_ID,
|
SYSTEM_USER_ID,
|
||||||
@ -21,7 +22,8 @@ const ARGS: IAddonConfig = {
|
|||||||
getLogger: noLogger,
|
getLogger: noLogger,
|
||||||
unleashUrl: 'http://some-url.com',
|
unleashUrl: 'http://some-url.com',
|
||||||
integrationEventsService: {} as IntegrationEventsService,
|
integrationEventsService: {} as IntegrationEventsService,
|
||||||
flagResolver: {} as IFlagResolver,
|
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
|
||||||
|
eventBus: {} as any,
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
|
@ -2,16 +2,22 @@ import Mustache from 'mustache';
|
|||||||
import Addon from './addon';
|
import Addon from './addon';
|
||||||
import definition from './webhook-definition';
|
import definition from './webhook-definition';
|
||||||
import type { IEvent } from '../types/events';
|
import type { IEvent } from '../types/events';
|
||||||
import { type IAddonConfig, serializeDates } from '../types';
|
import {
|
||||||
|
type IAddonConfig,
|
||||||
|
type IFlagResolver,
|
||||||
|
serializeDates,
|
||||||
|
} from '../types';
|
||||||
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
|
||||||
import {
|
import {
|
||||||
type FeatureEventFormatter,
|
type FeatureEventFormatter,
|
||||||
FeatureEventFormatterMd,
|
FeatureEventFormatterMd,
|
||||||
LinkStyle,
|
LinkStyle,
|
||||||
} from './feature-event-formatter-md';
|
} from './feature-event-formatter-md';
|
||||||
|
import { ADDON_EVENTS_HANDLED } from '../metric-events';
|
||||||
|
|
||||||
interface IParameters {
|
interface IParameters {
|
||||||
url: string;
|
url: string;
|
||||||
|
serviceName?: string;
|
||||||
bodyTemplate?: string;
|
bodyTemplate?: string;
|
||||||
contentType?: string;
|
contentType?: string;
|
||||||
authorization?: string;
|
authorization?: string;
|
||||||
@ -21,12 +27,15 @@ interface IParameters {
|
|||||||
export default class Webhook extends Addon {
|
export default class Webhook extends Addon {
|
||||||
private msgFormatter: FeatureEventFormatter;
|
private msgFormatter: FeatureEventFormatter;
|
||||||
|
|
||||||
|
flagResolver: IFlagResolver;
|
||||||
|
|
||||||
constructor(args: IAddonConfig) {
|
constructor(args: IAddonConfig) {
|
||||||
super(definition, args);
|
super(definition, args);
|
||||||
this.msgFormatter = new FeatureEventFormatterMd(
|
this.msgFormatter = new FeatureEventFormatterMd(
|
||||||
args.unleashUrl,
|
args.unleashUrl,
|
||||||
LinkStyle.MD,
|
LinkStyle.MD,
|
||||||
);
|
);
|
||||||
|
this.flagResolver = args.flagResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEvent(
|
async handleEvent(
|
||||||
@ -94,6 +103,13 @@ export default class Webhook extends Addon {
|
|||||||
state = 'failed';
|
state = 'failed';
|
||||||
const failedMessage = `Webhook request failed with status code: ${res.status}.`;
|
const failedMessage = `Webhook request failed with status code: ${res.status}.`;
|
||||||
stateDetails.push(failedMessage);
|
stateDetails.push(failedMessage);
|
||||||
|
if (this.flagResolver.isEnabled('addonUsageMetrics')) {
|
||||||
|
this.eventBus.emit(ADDON_EVENTS_HANDLED, {
|
||||||
|
result: state,
|
||||||
|
destination: 'webhook',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.warn(failedMessage);
|
this.logger.warn(failedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ const PROXY_FEATURES_FOR_TOKEN_TIME = 'proxy_features_for_token_time';
|
|||||||
const STAGE_ENTERED = 'stage-entered' as const;
|
const STAGE_ENTERED = 'stage-entered' as const;
|
||||||
const EXCEEDS_LIMIT = 'exceeds-limit' as const;
|
const EXCEEDS_LIMIT = 'exceeds-limit' as const;
|
||||||
const REQUEST_ORIGIN = 'request_origin' as const;
|
const REQUEST_ORIGIN = 'request_origin' as const;
|
||||||
|
const ADDON_EVENTS_HANDLED = 'addon-event-handled' as const;
|
||||||
|
|
||||||
type MetricEvent =
|
type MetricEvent =
|
||||||
| typeof REQUEST_TIME
|
| typeof REQUEST_TIME
|
||||||
@ -71,6 +72,7 @@ export {
|
|||||||
STAGE_ENTERED,
|
STAGE_ENTERED,
|
||||||
EXCEEDS_LIMIT,
|
EXCEEDS_LIMIT,
|
||||||
REQUEST_ORIGIN,
|
REQUEST_ORIGIN,
|
||||||
|
ADDON_EVENTS_HANDLED,
|
||||||
type MetricEvent,
|
type MetricEvent,
|
||||||
type MetricEventPayload,
|
type MetricEventPayload,
|
||||||
emitMetricEvent,
|
emitMetricEvent,
|
||||||
|
@ -359,6 +359,12 @@ export default class MetricsMonitor {
|
|||||||
labelNames: ['resource'],
|
labelNames: ['resource'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const addonEventsHandledCounter = createCounter({
|
||||||
|
name: 'addon_events_handled',
|
||||||
|
help: 'Events handled by addons and the result.',
|
||||||
|
labelNames: ['result', 'destination'],
|
||||||
|
});
|
||||||
|
|
||||||
async function collectStaticCounters() {
|
async function collectStaticCounters() {
|
||||||
try {
|
try {
|
||||||
const stats = await instanceStatsService.getStats();
|
const stats = await instanceStatsService.getStats();
|
||||||
@ -906,6 +912,10 @@ export default class MetricsMonitor {
|
|||||||
projectEnvironmentsDisabled.increment({ project_id: project });
|
projectEnvironmentsDisabled.increment({ project_id: project });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
eventBus.on(events.ADDON_EVENTS_HANDLED, ({ result, destination }) => {
|
||||||
|
addonEventsHandledCounter.increment({ result, destination });
|
||||||
|
});
|
||||||
|
|
||||||
await this.configureDbMetrics(
|
await this.configureDbMetrics(
|
||||||
db,
|
db,
|
||||||
eventBus,
|
eventBus,
|
||||||
|
@ -15,6 +15,7 @@ const ARGS: IAddonConfig = {
|
|||||||
unleashUrl: 'http://some-url.com',
|
unleashUrl: 'http://some-url.com',
|
||||||
integrationEventsService: {} as IntegrationEventsService,
|
integrationEventsService: {} as IntegrationEventsService,
|
||||||
flagResolver: {} as IFlagResolver,
|
flagResolver: {} as IFlagResolver,
|
||||||
|
eventBus: {} as any,
|
||||||
};
|
};
|
||||||
|
|
||||||
const definition: IAddonDefinition = {
|
const definition: IAddonDefinition = {
|
||||||
|
@ -64,7 +64,11 @@ export default class AddonService {
|
|||||||
getLogger,
|
getLogger,
|
||||||
server,
|
server,
|
||||||
flagResolver,
|
flagResolver,
|
||||||
}: Pick<IUnleashConfig, 'getLogger' | 'server' | 'flagResolver'>,
|
eventBus,
|
||||||
|
}: Pick<
|
||||||
|
IUnleashConfig,
|
||||||
|
'getLogger' | 'server' | 'flagResolver' | 'eventBus'
|
||||||
|
>,
|
||||||
tagTypeService: TagTypeService,
|
tagTypeService: TagTypeService,
|
||||||
eventService: EventService,
|
eventService: EventService,
|
||||||
integrationEventsService,
|
integrationEventsService,
|
||||||
@ -83,6 +87,7 @@ export default class AddonService {
|
|||||||
unleashUrl: server.unleashUrl,
|
unleashUrl: server.unleashUrl,
|
||||||
integrationEventsService,
|
integrationEventsService,
|
||||||
flagResolver,
|
flagResolver,
|
||||||
|
eventBus,
|
||||||
});
|
});
|
||||||
this.sensitiveParams = this.loadSensitiveParams(this.addonProviders);
|
this.sensitiveParams = this.loadSensitiveParams(this.addonProviders);
|
||||||
if (addonStore) {
|
if (addonStore) {
|
||||||
|
@ -10,6 +10,7 @@ import type { FeatureSearchEnvironmentSchema } from '../openapi/spec/feature-sea
|
|||||||
import type { IntegrationEventsService } from '../features/integration-events/integration-events-service';
|
import type { IntegrationEventsService } from '../features/integration-events/integration-events-service';
|
||||||
import type { IFlagResolver } from './experimental';
|
import type { IFlagResolver } from './experimental';
|
||||||
import type { Collaborator } from '../features/feature-toggle/types/feature-collaborators-read-model-type';
|
import type { Collaborator } from '../features/feature-toggle/types/feature-collaborators-read-model-type';
|
||||||
|
import type { EventEmitter } from 'events';
|
||||||
|
|
||||||
export type Operator = (typeof ALL_OPERATORS)[number];
|
export type Operator = (typeof ALL_OPERATORS)[number];
|
||||||
|
|
||||||
@ -384,6 +385,7 @@ export interface IAddonConfig {
|
|||||||
unleashUrl: string;
|
unleashUrl: string;
|
||||||
integrationEventsService: IntegrationEventsService;
|
integrationEventsService: IntegrationEventsService;
|
||||||
flagResolver: IFlagResolver;
|
flagResolver: IFlagResolver;
|
||||||
|
eventBus: EventEmitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserWithRole {
|
export interface IUserWithRole {
|
||||||
|
Loading…
Reference in New Issue
Block a user