diff --git a/src/lib/addons/__snapshots__/datadog.test.ts.snap b/src/lib/addons/__snapshots__/datadog.test.ts.snap index c2031adf15..70fb7abd6c 100644 --- a/src/lib/addons/__snapshots__/datadog.test.ts.snap +++ b/src/lib/addons/__snapshots__/datadog.test.ts.snap @@ -1,14 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Should call datadog webhook for archived toggle 1`] = `"{"text":"%%% \\n some@user.com just archived feature toggle *[some-toggle](http://some-url.com/archive)* \\n %%% ","title":"Unleash notification update"}"`; +exports[`Should call datadog webhook for archived toggle 1`] = `"{"text":"%%% \\n some@user.com just archived feature toggle *[some-toggle](http://some-url.com/archive)* \\n %%% ","title":"Unleash notification update","tags":[]}"`; -exports[`Should call datadog webhook for archived toggle with project info 1`] = `"{"text":"%%% \\n some@user.com just archived feature toggle *[some-toggle](http://some-url.com/projects/some-project/archive)* \\n %%% ","title":"Unleash notification update"}"`; +exports[`Should call datadog webhook for archived toggle with project info 1`] = `"{"text":"%%% \\n some@user.com just archived feature toggle *[some-toggle](http://some-url.com/projects/some-project/archive)* \\n %%% ","title":"Unleash notification update","tags":[]}"`; -exports[`Should call datadog webhook 1`] = `"{"text":"%%% \\n some@user.com created feature toggle [some-toggle](http://some-url.com/projects//features/some-toggle) in project *undefined* \\n %%% ","title":"Unleash notification update"}"`; +exports[`Should call datadog webhook 1`] = `"{"text":"%%% \\n some@user.com created feature toggle [some-toggle](http://some-url.com/projects//features/some-toggle) in project *undefined* \\n %%% ","title":"Unleash notification update","tags":[]}"`; -exports[`Should call datadog webhook for toggled environment 1`] = `"{"text":"%%% \\n some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default* \\n %%% ","title":"Unleash notification update"}"`; +exports[`Should call datadog webhook for toggled environment 1`] = `"{"text":"%%% \\n some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default* \\n %%% ","title":"Unleash notification update","tags":[]}"`; -exports[`Should include customHeaders in headers when calling service 1`] = `"{"text":"%%% \\n some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default* \\n %%% ","title":"Unleash notification update"}"`; +exports[`Should include customHeaders in headers when calling service 1`] = `"{"text":"%%% \\n some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default* \\n %%% ","title":"Unleash notification update","tags":[]}"`; exports[`Should include customHeaders in headers when calling service 2`] = ` { @@ -18,7 +18,7 @@ exports[`Should include customHeaders in headers when calling service 2`] = ` } `; -exports[`Should not include source_type_name when included in the config 1`] = `"{"text":"%%% \\n some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default* \\n %%% ","title":"Unleash notification update","source_type_name":"my-custom-source-type"}"`; +exports[`Should not include source_type_name when included in the config 1`] = `"{"text":"%%% \\n some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default* \\n %%% ","title":"Unleash notification update","tags":[],"source_type_name":"my-custom-source-type"}"`; exports[`Should not include source_type_name when included in the config 2`] = ` { diff --git a/src/lib/addons/addon.test.ts b/src/lib/addons/addon.test.ts index d7703dbb00..6706669c17 100644 --- a/src/lib/addons/addon.test.ts +++ b/src/lib/addons/addon.test.ts @@ -2,6 +2,7 @@ import nock from 'nock'; import noLogger from '../../test/fixtures/no-logger'; import SlackAddon from './slack'; +import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store'; beforeEach(() => { nock.disableNetConnect(); @@ -12,6 +13,7 @@ test('Does not retry if request succeeds', async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: url, + featureTagStore: new FakeFeatureTagStore(), }); nock(url).get('/').reply(201); const res = await addon.fetchRetry(url); @@ -23,6 +25,7 @@ test('Retries once, and succeeds', async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: url, + featureTagStore: new FakeFeatureTagStore(), }); nock(url).get('/').replyWithError('testing retry'); nock(url).get('/').reply(200); @@ -36,6 +39,7 @@ test('Does not throw if response is error', async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: url, + featureTagStore: new FakeFeatureTagStore(), }); nock(url).get('/').twice().replyWithError('testing retry'); const res = await addon.fetchRetry(url); @@ -47,6 +51,7 @@ test('Supports custom number of retries', async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: url, + featureTagStore: new FakeFeatureTagStore(), }); let retries = 0; nock(url).get('/').twice().replyWithError('testing retry'); diff --git a/src/lib/addons/datadog.test.ts b/src/lib/addons/datadog.test.ts index 43857afa33..06091ab912 100644 --- a/src/lib/addons/datadog.test.ts +++ b/src/lib/addons/datadog.test.ts @@ -9,6 +9,7 @@ import { Logger } from '../logger'; import DatadogAddon from './datadog'; import noLogger from '../../test/fixtures/no-logger'; +import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store'; let fetchRetryCalls: any[] = []; @@ -39,6 +40,7 @@ test('Should call datadog webhook', async () => { const addon = new DatadogAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore: new FakeFeatureTagStore(), }); const event: IEvent = { id: 1, @@ -68,6 +70,7 @@ test('Should call datadog webhook for archived toggle', async () => { const addon = new DatadogAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore: new FakeFeatureTagStore(), }); const event: IEvent = { id: 2, @@ -95,6 +98,7 @@ test('Should call datadog webhook for archived toggle with project info', async const addon = new DatadogAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore: new FakeFeatureTagStore(), }); const event: IEvent = { id: 2, @@ -123,6 +127,7 @@ test(`Should call datadog webhook for toggled environment`, async () => { const addon = new DatadogAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore: new FakeFeatureTagStore(), }); const event: IEvent = { id: 2, @@ -153,6 +158,7 @@ test(`Should include customHeaders in headers when calling service`, async () => const addon = new DatadogAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore: new FakeFeatureTagStore(), }); const event: IEvent = { id: 2, @@ -184,6 +190,7 @@ test(`Should not include source_type_name when included in the config`, async () const addon = new DatadogAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore: new FakeFeatureTagStore(), }); const event: IEvent = { id: 2, diff --git a/src/lib/addons/datadog.ts b/src/lib/addons/datadog.ts index ee03c93d34..d48de8c5aa 100644 --- a/src/lib/addons/datadog.ts +++ b/src/lib/addons/datadog.ts @@ -8,6 +8,7 @@ import { LinkStyle, } from './feature-event-formatter-md'; import { IEvent } from '../types/events'; +import { IFeatureTagStore, ITag } from '../types'; interface IDatadogParameters { url: string; @@ -23,15 +24,22 @@ interface DDRequestBody { source_type_name?: string; } +interface IDatadogAddonConfig extends IAddonConfig { + featureTagStore: IFeatureTagStore; +} + export default class DatadogAddon extends Addon { private msgFormatter: FeatureEventFormatter; - constructor(config: IAddonConfig) { + private featureTagStore: IFeatureTagStore; + + constructor(config: IDatadogAddonConfig) { super(definition, config); this.msgFormatter = new FeatureEventFormatterMd( config.unleashUrl, LinkStyle.MD, ); + this.featureTagStore = config.featureTagStore; } async handleEvent( @@ -47,7 +55,8 @@ export default class DatadogAddon extends Addon { const text = this.msgFormatter.format(event); - const { tags: eventTags } = event; + const eventTags = await this.getFeatureTags(event); + const tags = eventTags && eventTags.map((tag) => `${tag.type}:${tag.value}`); const body: DDRequestBody = { @@ -82,4 +91,13 @@ export default class DatadogAddon extends Addon { `Handled event ${event.type}. Status codes=${res.status}`, ); } + + async getFeatureTags({ + featureName, + }: Pick): Promise { + if (featureName) { + return this.featureTagStore.getAllTagsForFeature(featureName); + } + return []; + } } diff --git a/src/lib/addons/feature-event-formatter-md.test.ts b/src/lib/addons/feature-event-formatter-md.test.ts index 93866e44f9..95e82021ff 100644 --- a/src/lib/addons/feature-event-formatter-md.test.ts +++ b/src/lib/addons/feature-event-formatter-md.test.ts @@ -52,7 +52,6 @@ const testCases: [string, IEvent, string][] = [ }, constraints: [], }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -86,7 +85,6 @@ const testCases: [string, IEvent, string][] = [ }, constraints: [], }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -120,7 +118,6 @@ const testCases: [string, IEvent, string][] = [ }, constraints: [], }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -162,7 +159,6 @@ const testCases: [string, IEvent, string][] = [ }, constraints: [], }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -196,7 +192,6 @@ const testCases: [string, IEvent, string][] = [ }, constraints: [], }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -221,7 +216,6 @@ const testCases: [string, IEvent, string][] = [ }, }, preData: null, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -242,7 +236,6 @@ const testCases: [string, IEvent, string][] = [ parameters: {}, constraints: [], }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -293,7 +286,6 @@ const testCases: [string, IEvent, string][] = [ parameters: {}, constraints: [], }, - tags: [], featureName: 'aaa', project: 'default', environment: 'production', @@ -345,7 +337,6 @@ const testCases: [string, IEvent, string][] = [ }, ], }, - tags: [], featureName: 'aaa', project: 'default', environment: 'production', @@ -386,7 +377,6 @@ const testCases: [string, IEvent, string][] = [ sortOrder: 9999, id: '9a995d94-5944-4897-a82f-0f7e65c2fb3f', }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -422,7 +412,6 @@ const testCases: [string, IEvent, string][] = [ IPs: '', }, }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -458,7 +447,6 @@ const testCases: [string, IEvent, string][] = [ hostNames: '', }, }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', @@ -494,7 +482,6 @@ const testCases: [string, IEvent, string][] = [ IPs: '', }, }, - tags: [], featureName: 'new-feature', project: 'my-other-project', environment: 'production', diff --git a/src/lib/addons/index.ts b/src/lib/addons/index.ts index 8643e4c4ce..39f5262f9e 100644 --- a/src/lib/addons/index.ts +++ b/src/lib/addons/index.ts @@ -5,7 +5,7 @@ import DatadogAddon from './datadog'; import Addon from './addon'; import { LogProvider } from '../logger'; import SlackAppAddon from './slack-app'; -import { IFlagResolver } from '../types'; +import { IFeatureTagStore, IFlagResolver } from '../types'; export interface IAddonProviders { [key: string]: Addon; @@ -15,10 +15,20 @@ export const getAddons: (args: { getLogger: LogProvider; unleashUrl: string; flagResolver: IFlagResolver; -}) => IAddonProviders = ({ getLogger, unleashUrl, flagResolver }) => { + featureTagStore: IFeatureTagStore; +}) => IAddonProviders = ({ + getLogger, + unleashUrl, + flagResolver, + featureTagStore, +}) => { const slackAppAddonEnabled = flagResolver.isEnabled('slackAppAddon'); - const slackAddon = new SlackAddon({ getLogger, unleashUrl }); + const slackAddon = new SlackAddon({ + getLogger, + unleashUrl, + featureTagStore, + }); if (slackAppAddonEnabled) { slackAddon.definition.deprecated = @@ -29,11 +39,13 @@ export const getAddons: (args: { new Webhook({ getLogger }), slackAddon, new TeamsAddon({ getLogger, unleashUrl }), - new DatadogAddon({ getLogger, unleashUrl }), + new DatadogAddon({ getLogger, unleashUrl, featureTagStore }), ]; if (slackAppAddonEnabled) { - addons.push(new SlackAppAddon({ getLogger, unleashUrl })); + addons.push( + new SlackAppAddon({ getLogger, unleashUrl, featureTagStore }), + ); } return addons.reduce((map, addon) => { diff --git a/src/lib/addons/slack-app.test.ts b/src/lib/addons/slack-app.test.ts index ffd56e944c..d57c7b6800 100644 --- a/src/lib/addons/slack-app.test.ts +++ b/src/lib/addons/slack-app.test.ts @@ -1,3 +1,4 @@ +import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store'; import { IEvent, FEATURE_ENVIRONMENT_ENABLED } from '../types/events'; import SlackAppAddon from './slack-app'; import { ChatPostMessageArguments, ErrorCode } from '@slack/web-api'; @@ -35,11 +36,45 @@ describe('SlackAppAddon', () => { fatal: jest.fn(), }; const getLogger = jest.fn(() => loggerMock); + const featureTagStore = new FakeFeatureTagStore(); const mockError = { code: ErrorCode.PlatformError, data: 'Platform error message', }; + featureTagStore.tagFeatures([ + { + featureName: 'some-toggle', + tagType: 'slack', + tagValue: 'general', + }, + { + featureName: 'toggle-2tags', + tagType: 'slack', + tagValue: 'general', + }, + { + featureName: 'toggle-2tags', + tagType: 'slack', + tagValue: 'another-channel-1', + }, + { + featureName: 'toggle-3tags', + tagType: 'slack', + tagValue: 'general', + }, + { + featureName: 'toggle-3tags', + tagType: 'slack', + tagValue: 'another-channel-1', + }, + { + featureName: 'toggle-3tags', + tagType: 'slack', + tagValue: 'another-channel-2', + }, + ]); + const event: IEvent = { id: 1, createdAt: new Date(), @@ -54,7 +89,6 @@ describe('SlackAppAddon', () => { type: 'release', strategies: [{ name: 'default' }], }, - tags: [{ type: 'slack', value: 'general' }], }; beforeEach(() => { @@ -64,6 +98,7 @@ describe('SlackAppAddon', () => { addon = new SlackAppAddon({ getLogger, unleashUrl: 'http://some-url.com', + featureTagStore: featureTagStore, }); }); @@ -81,10 +116,7 @@ describe('SlackAppAddon', () => { it('should post to all channels in tags', async () => { const eventWith2Tags: IEvent = { ...event, - tags: [ - { type: 'slack', value: 'general' }, - { type: 'slack', value: 'another-channel-1' }, - ], + featureName: 'toggle-2tags', }; await addon.handleEvent(eventWith2Tags, { accessToken }); @@ -97,7 +129,7 @@ describe('SlackAppAddon', () => { it('should not post a message if there are no tagged channels and no defaultChannels', async () => { const eventWithoutTags: IEvent = { ...event, - tags: [], + featureName: 'toggle-no-tags', }; await addon.handleEvent(eventWithoutTags, { @@ -110,7 +142,7 @@ describe('SlackAppAddon', () => { it('should use defaultChannels if no tagged channels are found', async () => { const eventWithoutTags: IEvent = { ...event, - tags: [], + featureName: 'toggle-no-tags', }; await addon.handleEvent(eventWithoutTags, { @@ -137,11 +169,7 @@ describe('SlackAppAddon', () => { it('should handle rejections in chat.postMessage', async () => { const eventWith3Tags: IEvent = { ...event, - tags: [ - { type: 'slack', value: 'general' }, - { type: 'slack', value: 'another-channel-1' }, - { type: 'slack', value: 'another-channel-2' }, - ], + featureName: 'toggle-3tags', }; postMessage = jest diff --git a/src/lib/addons/slack-app.ts b/src/lib/addons/slack-app.ts index 5727ff4d6d..119ae51929 100644 --- a/src/lib/addons/slack-app.ts +++ b/src/lib/addons/slack-app.ts @@ -11,13 +11,14 @@ import { import Addon from './addon'; import slackAppDefinition from './slack-app-definition'; -import { IAddonConfig } from '../types/model'; +import { IAddonConfig, ITag } from '../types/model'; import { FeatureEventFormatter, FeatureEventFormatterMd, LinkStyle, } from './feature-event-formatter-md'; import { IEvent } from '../types/events'; +import { IFeatureTagStore } from '../types'; interface ISlackAppAddonParameters { accessToken: string; @@ -25,6 +26,10 @@ interface ISlackAppAddonParameters { alwaysPostToDefault: string; } +interface ISlackAppAddonConfig extends IAddonConfig { + featureTagStore: IFeatureTagStore; +} + export default class SlackAppAddon extends Addon { private msgFormatter: FeatureEventFormatter; @@ -32,12 +37,15 @@ export default class SlackAppAddon extends Addon { private slackClient?: WebClient; - constructor(args: IAddonConfig) { + private featureTagStore: IFeatureTagStore; + + constructor(args: ISlackAppAddonConfig) { super(slackAppDefinition, args); this.msgFormatter = new FeatureEventFormatterMd( args.unleashUrl, LinkStyle.SLACK, ); + this.featureTagStore = args.featureTagStore; } async handleEvent( @@ -56,7 +64,8 @@ export default class SlackAppAddon extends Addon { alwaysPostToDefault === 'true' || alwaysPostToDefault === 'yes'; this.logger.debug(`Post to default was set to ${postToDefault}`); - const taggedChannels = this.findTaggedChannels(event); + const taggedChannels = await this.findTaggedSlackChannels(event); + let eventChannels: string[]; if (postToDefault) { eventChannels = taggedChannels.concat( @@ -139,8 +148,18 @@ export default class SlackAppAddon extends Addon { } } - findTaggedChannels({ tags }: Pick): string[] { - if (tags) { + async getFeatureTags(featureName?: string): Promise { + if (featureName) { + return this.featureTagStore.getAllTagsForFeature(featureName); + } + return []; + } + + async findTaggedSlackChannels({ + featureName, + }: Pick): Promise { + const tags = await this.getFeatureTags(featureName); + if (tags.length) { return tags .filter((tag) => tag.type === 'slack') .map((t) => t.value); diff --git a/src/lib/addons/slack.test.ts b/src/lib/addons/slack.test.ts index 416ffc1d70..d1fd4ad285 100644 --- a/src/lib/addons/slack.test.ts +++ b/src/lib/addons/slack.test.ts @@ -9,6 +9,9 @@ import { Logger } from '../logger'; import SlackAddon from './slack'; import noLogger from '../../test/fixtures/no-logger'; +import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store'; + +const featureTagStore = new FakeFeatureTagStore(); let fetchRetryCalls: any[] = []; @@ -35,10 +38,15 @@ jest.mock( }, ); +beforeEach(() => { + featureTagStore.deleteAll(); +}); + test('Should call slack webhook', async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore, }); const event: IEvent = { id: 1, @@ -70,6 +78,7 @@ test('Should call slack webhook for archived toggle', async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore, }); const event: IEvent = { id: 2, @@ -97,6 +106,7 @@ test('Should call slack webhook for archived toggle with project info', async () const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore, }); const event: IEvent = { id: 2, @@ -125,6 +135,7 @@ test(`Should call webhook for toggled environment`, async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore, }); const event: IEvent = { id: 2, @@ -155,6 +166,7 @@ test('Should use default channel', async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore, }); const event: IEvent = { id: 3, @@ -185,6 +197,7 @@ test('Should override default channel with data from tag', async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore, }); const event: IEvent = { id: 4, @@ -197,14 +210,13 @@ test('Should override default channel with data from tag', async () => { enabled: false, strategies: [{ name: 'default' }], }, - tags: [ - { - type: 'slack', - value: 'another-channel', - }, - ], }; + featureTagStore.tagFeature('some-toggle', { + type: 'slack', + value: 'another-channel', + }); + const parameters = { url: 'http://hooks.slack.com', defaultChannel: 'some-channel', @@ -221,6 +233,7 @@ test('Should post to all channels in tags', async () => { const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore, }); const event: IEvent = { id: 5, @@ -233,18 +246,21 @@ test('Should post to all channels in tags', async () => { enabled: false, strategies: [{ name: 'default' }], }, - tags: [ - { - type: 'slack', - value: 'another-channel-1', - }, - { - type: 'slack', - value: 'another-channel-2', - }, - ], }; + featureTagStore.tagFeatures([ + { + featureName: 'some-toggle', + tagType: 'slack', + tagValue: 'another-channel-1', + }, + { + featureName: 'some-toggle', + tagType: 'slack', + tagValue: 'another-channel-2', + }, + ]); + const parameters = { url: 'http://hooks.slack.com', defaultChannel: 'some-channel', @@ -264,6 +280,7 @@ test('Should include custom headers from parameters in call to service', async ( const addon = new SlackAddon({ getLogger: noLogger, unleashUrl: 'http://some-url.com', + featureTagStore, }); const event: IEvent = { id: 2, diff --git a/src/lib/addons/slack.ts b/src/lib/addons/slack.ts index 4400b704e2..e5020b72ed 100644 --- a/src/lib/addons/slack.ts +++ b/src/lib/addons/slack.ts @@ -1,7 +1,7 @@ import Addon from './addon'; import slackDefinition from './slack-definition'; -import { IAddonConfig } from '../types/model'; +import { IAddonConfig, ITag } from '../types/model'; import { FeatureEventFormatter, @@ -9,6 +9,7 @@ import { LinkStyle, } from './feature-event-formatter-md'; import { IEvent } from '../types/events'; +import { IFeatureTagStore } from '../types'; interface ISlackAddonParameters { url: string; @@ -17,15 +18,23 @@ interface ISlackAddonParameters { emojiIcon?: string; customHeaders?: string; } + +interface ISlackAddonConfig extends IAddonConfig { + featureTagStore: IFeatureTagStore; +} + export default class SlackAddon extends Addon { private msgFormatter: FeatureEventFormatter; - constructor(args: IAddonConfig) { + private featureTagStore: IFeatureTagStore; + + constructor(args: ISlackAddonConfig) { super(slackDefinition, args); this.msgFormatter = new FeatureEventFormatterMd( args.unleashUrl, LinkStyle.SLACK, ); + this.featureTagStore = args.featureTagStore; } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types @@ -41,7 +50,7 @@ export default class SlackAddon extends Addon { customHeaders, } = parameters; - const slackChannels = this.findSlackChannels(event); + const slackChannels = await this.findTaggedSlackChannels(event); if (slackChannels.length === 0) { slackChannels.push(defaultChannel); @@ -98,8 +107,18 @@ export default class SlackAddon extends Addon { this.logger.info(`Handled event ${event.type}. Status codes=${codes}`); } - findSlackChannels({ tags }: Pick): string[] { - if (tags) { + async getFeatureTags(featureName?: string): Promise { + if (featureName) { + return this.featureTagStore.getAllTagsForFeature(featureName); + } + return []; + } + + async findTaggedSlackChannels({ + featureName, + }: Pick): Promise { + const tags = await this.getFeatureTags(featureName); + if (tags.length) { return tags .filter((tag) => tag.type === 'slack') .map((t) => t.value); diff --git a/src/lib/db/event-store.ts b/src/lib/db/event-store.ts index 767f3a519d..0bfc4a9692 100644 --- a/src/lib/db/event-store.ts +++ b/src/lib/db/event-store.ts @@ -6,7 +6,6 @@ import { } from '../types/events'; import { LogProvider, Logger } from '../logger'; import { IEventStore } from '../types/stores/event-store'; -import { ITag } from '../types/model'; import { SearchEventsSchema } from '../openapi/spec/search-events-schema'; import { sharedEventEmitter } from '../util/anyEventEmitter'; import { Db } from './db'; @@ -20,7 +19,6 @@ const EVENT_COLUMNS = [ 'created_at', 'data', 'pre_data', - 'tags', 'feature_name', 'project', 'environment', @@ -77,7 +75,6 @@ export interface IEventTable { feature_name?: string; project?: string; environment?: string; - tags: ITag[]; } const TABLE = 'events'; @@ -342,7 +339,6 @@ class EventStore implements IEventStore { .orWhereRaw('type::text ILIKE ?', `%${search.query}%`) .orWhereRaw('created_by::text ILIKE ?', `%${search.query}%`) .orWhereRaw('data::text ILIKE ?', `%${search.query}%`) - .orWhereRaw('tags::text ILIKE ?', `%${search.query}%`) .orWhereRaw('pre_data::text ILIKE ?', `%${search.query}%`), ); } @@ -362,7 +358,6 @@ class EventStore implements IEventStore { createdAt: row.created_at, data: row.data, preData: row.pre_data, - tags: row.tags || [], featureName: row.feature_name, project: row.project, environment: row.environment, @@ -377,8 +372,6 @@ class EventStore implements IEventStore { pre_data: Array.isArray(e.preData) ? JSON.stringify(e.preData) : e.preData, - // @ts-expect-error workaround for json-array - tags: JSON.stringify(e.tags), feature_name: e.featureName, project: e.project, environment: e.environment, diff --git a/src/lib/features/feature-toggle/createFeatureToggleService.ts b/src/lib/features/feature-toggle/createFeatureToggleService.ts index 4f616357fb..3da6d49b69 100644 --- a/src/lib/features/feature-toggle/createFeatureToggleService.ts +++ b/src/lib/features/feature-toggle/createFeatureToggleService.ts @@ -7,7 +7,6 @@ import FeatureStrategiesStore from '../../db/feature-strategy-store'; import FeatureToggleStore from '../../db/feature-toggle-store'; import FeatureToggleClientStore from '../../db/feature-toggle-client-store'; import ProjectStore from '../../db/project-store'; -import FeatureTagStore from '../../db/feature-tag-store'; import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; import ContextFieldStore from '../../db/context-field-store'; import GroupStore from '../../db/group-store'; @@ -22,7 +21,6 @@ import FakeFeatureStrategiesStore from '../../../test/fixtures/fake-feature-stra import FakeFeatureToggleStore from '../../../test/fixtures/fake-feature-toggle-store'; import FakeFeatureToggleClientStore from '../../../test/fixtures/fake-feature-toggle-client-store'; import FakeProjectStore from '../../../test/fixtures/fake-project-store'; -import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store'; import FakeFeatureEnvironmentStore from '../../../test/fixtures/fake-feature-environment-store'; import FakeContextFieldStore from '../../../test/fixtures/fake-context-field-store'; import FakeGroupStore from '../../../test/fixtures/fake-group-store'; @@ -66,7 +64,6 @@ export const createFeatureToggleService = ( getLogger, flagResolver, ); - const featureTagStore = new FeatureTagStore(db, eventBus, getLogger); const featureEnvironmentStore = new FeatureEnvironmentStore( db, eventBus, @@ -105,7 +102,6 @@ export const createFeatureToggleService = ( featureToggleClientStore, projectStore, eventStore, - featureTagStore, featureEnvironmentStore, contextFieldStore, strategyStore, @@ -128,7 +124,6 @@ export const createFakeFeatureToggleService = ( const featureToggleStore = new FakeFeatureToggleStore(); const featureToggleClientStore = new FakeFeatureToggleClientStore(); const projectStore = new FakeProjectStore(); - const featureTagStore = new FakeFeatureTagStore(); const featureEnvironmentStore = new FakeFeatureEnvironmentStore(); const contextFieldStore = new FakeContextFieldStore(); const groupStore = new FakeGroupStore(); @@ -154,7 +149,6 @@ export const createFakeFeatureToggleService = ( featureToggleClientStore, projectStore, eventStore, - featureTagStore, featureEnvironmentStore, contextFieldStore, strategyStore, diff --git a/src/lib/routes/admin-api/events.test.ts b/src/lib/routes/admin-api/events.test.ts index 7e999e6240..61e85b0a75 100644 --- a/src/lib/routes/admin-api/events.test.ts +++ b/src/lib/routes/admin-api/events.test.ts @@ -45,7 +45,6 @@ test('should get events list via admin', async () => { data: { name: 'test', project: 'default' }, featureName: 'test', project: 'default', - tags: [], }), ); const { body } = await request @@ -65,7 +64,6 @@ test('should anonymise events list via admin', async () => { data: { name: 'test', project: 'default' }, featureName: 'test', project: 'default', - tags: [], }), ); const { body } = await request diff --git a/src/lib/services/addon-service.ts b/src/lib/services/addon-service.ts index 3105d321a9..4ff930d8aa 100644 --- a/src/lib/services/addon-service.ts +++ b/src/lib/services/addon-service.ts @@ -45,9 +45,13 @@ export default class AddonService { addonStore, eventStore, featureToggleStore, + featureTagStore, }: Pick< IUnleashStores, - 'addonStore' | 'eventStore' | 'featureToggleStore' + | 'addonStore' + | 'eventStore' + | 'featureToggleStore' + | 'featureTagStore' >, { getLogger, @@ -69,6 +73,7 @@ export default class AddonService { getLogger, unleashUrl: server.unleashUrl, flagResolver, + featureTagStore, }); this.sensitiveParams = this.loadSensitiveParams(this.addonProviders); if (addonStore) { diff --git a/src/lib/services/feature-toggle-service.ts b/src/lib/services/feature-toggle-service.ts index fee121e1af..1bcdf8292d 100644 --- a/src/lib/services/feature-toggle-service.ts +++ b/src/lib/services/feature-toggle-service.ts @@ -25,7 +25,6 @@ import { IFeatureEnvironmentStore, IFeatureOverview, IFeatureStrategy, - IFeatureTagStore, IFeatureToggleClientStore, IFeatureToggleQuery, IFeatureToggleStore, @@ -136,8 +135,6 @@ class FeatureToggleService { private featureToggleClientStore: IFeatureToggleClientStore; - private tagStore: IFeatureTagStore; - private featureEnvironmentStore: IFeatureEnvironmentStore; private projectStore: IProjectStore; @@ -161,7 +158,6 @@ class FeatureToggleService { featureToggleClientStore, projectStore, eventStore, - featureTagStore, featureEnvironmentStore, contextFieldStore, strategyStore, @@ -172,7 +168,6 @@ class FeatureToggleService { | 'featureToggleClientStore' | 'projectStore' | 'eventStore' - | 'featureTagStore' | 'featureEnvironmentStore' | 'contextFieldStore' | 'strategyStore' @@ -190,7 +185,6 @@ class FeatureToggleService { this.strategyStore = strategyStore; this.featureToggleStore = featureToggleStore; this.featureToggleClientStore = featureToggleClientStore; - this.tagStore = featureTagStore; this.projectStore = projectStore; this.eventStore = eventStore; this.featureEnvironmentStore = featureEnvironmentStore; @@ -382,15 +376,12 @@ class FeatureToggleService { ); if (featureToggle.stale !== newDocument.stale) { - const tags = await this.tagStore.getAllTagsForFeature(featureName); - await this.eventStore.store( new FeatureStaleEvent({ stale: newDocument.stale, project, featureName, createdBy, - tags, }), ); } @@ -489,8 +480,6 @@ class FeatureToggleService { .map((strategy) => strategy.id); const eventData: StrategyIds = { strategyIds: newOrder }; - - const tags = await this.tagStore.getAllTagsForFeature(featureName); const event = new StrategiesOrderChangedEvent({ featureName, environment, @@ -498,7 +487,6 @@ class FeatureToggleService { createdBy, preData: eventPreData, data: eventData, - tags: tags, }); await this.eventStore.store(event); } @@ -594,8 +582,6 @@ class FeatureToggleService { segments, ); - const tags = await this.tagStore.getAllTagsForFeature(featureName); - await this.eventStore.store( new FeatureStrategyAddEvent({ project: projectId, @@ -603,7 +589,6 @@ class FeatureToggleService { createdBy, environment, data: strategy, - tags, }), ); return strategy; @@ -712,7 +697,6 @@ class FeatureToggleService { ); // Store event! - const tags = await this.tagStore.getAllTagsForFeature(featureName); const data = this.featureStrategyToPublic(strategy, segments); const preData = this.featureStrategyToPublic( existingStrategy, @@ -726,7 +710,6 @@ class FeatureToggleService { createdBy: userName, data, preData, - tags, }), ); await this.optionallyDisableFeature( @@ -758,7 +741,6 @@ class FeatureToggleService { id, existingStrategy, ); - const tags = await this.tagStore.getAllTagsForFeature(featureName); const segments = await this.segmentService.getByStrategy( strategy.id, ); @@ -775,7 +757,6 @@ class FeatureToggleService { createdBy: userName, data, preData, - tags, }), ); return data; @@ -840,7 +821,6 @@ class FeatureToggleService { ); } - const tags = await this.tagStore.getAllTagsForFeature(featureName); const preData = this.featureStrategyToPublic(existingStrategy); await this.eventStore.store( @@ -850,7 +830,6 @@ class FeatureToggleService { environment, createdBy, preData, - tags, }), ); @@ -1085,15 +1064,12 @@ class FeatureToggleService { ); } - const tags = await this.tagStore.getAllTagsForFeature(featureName); - await this.eventStore.store( new FeatureCreatedEvent({ featureName, createdBy, project: projectId, data: createdToggle, - tags, }), ); @@ -1243,8 +1219,6 @@ class FeatureToggleService { name: featureName, }); - const tags = await this.tagStore.getAllTagsForFeature(featureName); - await this.eventStore.store( new FeatureMetadataUpdateEvent({ createdBy: userName, @@ -1252,7 +1226,6 @@ class FeatureToggleService { preData, featureName, project: projectId, - tags, }), ); return featureToggle; @@ -1379,7 +1352,6 @@ class FeatureToggleService { const { project } = feature; feature.stale = isStale; await this.featureToggleStore.update(project, feature); - const tags = await this.tagStore.getAllTagsForFeature(featureName); await this.eventStore.store( new FeatureStaleEvent({ @@ -1387,7 +1359,6 @@ class FeatureToggleService { project, featureName, createdBy, - tags, }), ); @@ -1409,13 +1380,12 @@ class FeatureToggleService { } await this.featureToggleStore.archive(featureName); - const tags = await this.tagStore.getAllTagsForFeature(featureName); + await this.eventStore.store( new FeatureArchivedEvent({ featureName, createdBy, project: feature.project, - tags, }), ); } @@ -1430,8 +1400,9 @@ class FeatureToggleService { const features = await this.featureToggleStore.getAllByNames( featureNames, ); + await this.featureToggleStore.batchArchive(featureNames); - const tags = await this.tagStore.getAllByFeatures(featureNames); + await this.eventStore.batchStore( features.map( (feature) => @@ -1439,12 +1410,6 @@ class FeatureToggleService { featureName: feature.name, createdBy, project: feature.project, - tags: tags - .filter((tag) => tag.featureName === feature.name) - .map((tag) => ({ - value: tag.tagValue, - type: tag.tagType, - })), }), ), ); @@ -1467,8 +1432,9 @@ class FeatureToggleService { const relevantFeatureNames = relevantFeatures.map( (feature) => feature.name, ); + await this.featureToggleStore.batchStale(relevantFeatureNames, stale); - const tags = await this.tagStore.getAllByFeatures(relevantFeatureNames); + await this.eventStore.batchStore( relevantFeatures.map( (feature) => @@ -1477,12 +1443,6 @@ class FeatureToggleService { project: projectId, featureName: feature.name, createdBy, - tags: tags - .filter((tag) => tag.featureName === feature.name) - .map((tag) => ({ - value: tag.tagValue, - type: tag.tagType, - })), }), ), ); @@ -1627,7 +1587,6 @@ class FeatureToggleService { const feature = await this.featureToggleStore.get(featureName); if (updatedEnvironmentStatus > 0) { - const tags = await this.tagStore.getAllTagsForFeature(featureName); await this.eventStore.store( new FeatureEnvironmentEvent({ enabled, @@ -1635,7 +1594,6 @@ class FeatureToggleService { featureName, environment, createdBy, - tags, }), ); } @@ -1647,7 +1605,6 @@ class FeatureToggleService { featureName: string, createdBy: string, ): Promise { - const tags = await this.tagStore.getAllTagsForFeature(featureName); const feature = await this.getFeatureToggleLegacy(featureName); // Legacy event. Will not be used from v4.3. @@ -1657,7 +1614,6 @@ class FeatureToggleService { createdBy, featureName, data: feature, - tags, project: feature.project, }); return feature; @@ -1719,14 +1675,12 @@ class FeatureToggleService { feature.project = newProject; await this.featureToggleStore.update(newProject, feature); - const tags = await this.tagStore.getAllTagsForFeature(featureName); await this.eventStore.store( new FeatureChangeProjectEvent({ createdBy, oldProject, newProject, featureName, - tags, }), ); } @@ -1738,15 +1692,15 @@ class FeatureToggleService { // TODO: add project id. async deleteFeature(featureName: string, createdBy: string): Promise { const toggle = await this.featureToggleStore.get(featureName); - const tags = await this.tagStore.getAllTagsForFeature(featureName); + await this.featureToggleStore.delete(featureName); + await this.eventStore.store( new FeatureDeletedEvent({ featureName, project: toggle.project, createdBy, preData: toggle, - tags, }), ); } @@ -1767,8 +1721,9 @@ class FeatureToggleService { const eligibleFeatureNames = eligibleFeatures.map( (toggle) => toggle.name, ); - const tags = await this.tagStore.getAllByFeatures(eligibleFeatureNames); + await this.featureToggleStore.batchDelete(eligibleFeatureNames); + await this.eventStore.batchStore( eligibleFeatures.map( (feature) => @@ -1777,12 +1732,6 @@ class FeatureToggleService { createdBy, project: feature.project, preData: feature, - tags: tags - .filter((tag) => tag.featureName === feature.name) - .map((tag) => ({ - value: tag.tagValue, - type: tag.tagType, - })), }), ), ); @@ -1804,8 +1753,9 @@ class FeatureToggleService { const eligibleFeatureNames = eligibleFeatures.map( (toggle) => toggle.name, ); - const tags = await this.tagStore.getAllByFeatures(eligibleFeatureNames); + await this.featureToggleStore.batchRevive(eligibleFeatureNames); + await this.eventStore.batchStore( eligibleFeatures.map( (feature) => @@ -1813,12 +1763,6 @@ class FeatureToggleService { featureName: feature.name, createdBy, project: feature.project, - tags: tags - .filter((tag) => tag.featureName === feature.name) - .map((tag) => ({ - value: tag.tagValue, - type: tag.tagType, - })), }), ), ); @@ -1827,13 +1771,12 @@ class FeatureToggleService { // TODO: add project id. async reviveFeature(featureName: string, createdBy: string): Promise { const toggle = await this.featureToggleStore.revive(featureName); - const tags = await this.tagStore.getAllTagsForFeature(featureName); + await this.eventStore.store( new FeatureRevivedEvent({ createdBy, featureName, project: toggle.project, - tags, }), ); } @@ -1930,13 +1873,12 @@ class FeatureToggleService { featureName, fixedVariants, ); - const tags = await this.tagStore.getAllTagsForFeature(featureName); + await this.eventStore.store( new FeatureVariantEvent({ project, featureName, createdBy, - tags, oldVariants, newVariants: featureToggle.variants as IVariant[], }), @@ -1964,8 +1906,6 @@ class FeatureToggleService { ).variants || []; - const tags = await this.tagStore.getAllTagsForFeature(featureName); - await this.eventStore.store( new EnvironmentVariantEvent({ featureName, @@ -1974,7 +1914,6 @@ class FeatureToggleService { createdBy: user, oldVariants: theOldVariants, newVariants: fixedVariants, - tags, }), ); await this.featureEnvironmentStore.setVariantsToFeatureEnvironments( @@ -2041,8 +1980,6 @@ class FeatureToggleService { oldVariants[env] = featureEnv.variants || []; } - const tags = await this.tagStore.getAllTagsForFeature(featureName); - await this.eventStore.batchStore( environments.map( (environment) => @@ -2053,7 +1990,6 @@ class FeatureToggleService { createdBy: user, oldVariants: oldVariants[environment], newVariants: fixedVariants, - tags, }), ), ); @@ -2169,9 +2105,6 @@ class FeatureToggleService { new PotentiallyStaleOnEvent({ featureName: name, project, - tags: await this.tagStore.getAllTagsForFeature( - name, - ), }), ), ), diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts index 68064d5c26..bf9ef7485a 100644 --- a/src/lib/types/events.ts +++ b/src/lib/types/events.ts @@ -1,5 +1,5 @@ import { extractUsernameFromUser } from '../util'; -import { FeatureToggle, IStrategyConfig, ITag, IVariant } from './model'; +import { FeatureToggle, IStrategyConfig, IVariant } from './model'; import { IApiToken } from './models/api-token'; import { IUser } from './user'; @@ -258,7 +258,6 @@ export interface IBaseEvent { featureName?: string; data?: any; preData?: any; - tags?: ITag[]; } export interface IEvent extends IBaseEvent { @@ -276,22 +275,15 @@ class BaseEvent implements IBaseEvent { readonly createdBy: string; - readonly tags: ITag[]; - /** * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ - constructor( - type: IEventType, - createdBy: string | IUser, - tags: ITag[] = [], - ) { + constructor(type: IEventType, createdBy: string | IUser) { this.type = type; this.createdBy = typeof createdBy === 'string' ? createdBy : extractUsernameFromUser(createdBy); - this.tags = tags; } } @@ -308,13 +300,8 @@ export class FeatureStaleEvent extends BaseEvent { project: string; featureName: string; createdBy: string | IUser; - tags: ITag[]; }) { - super( - p.stale ? FEATURE_STALE_ON : FEATURE_STALE_OFF, - p.createdBy, - p.tags, - ); + super(p.stale ? FEATURE_STALE_ON : FEATURE_STALE_OFF, p.createdBy); this.project = p.project; this.featureName = p.featureName; } @@ -336,14 +323,12 @@ export class FeatureEnvironmentEvent extends BaseEvent { featureName: string; environment: string; createdBy: string | IUser; - tags: ITag[]; }) { super( p.enabled ? FEATURE_ENVIRONMENT_ENABLED : FEATURE_ENVIRONMENT_DISABLED, p.createdBy, - p.tags, ); this.project = p.project; this.featureName = p.featureName; @@ -372,9 +357,8 @@ export class StrategiesOrderChangedEvent extends BaseEvent { createdBy: string | IUser; data: StrategyIds; preData: StrategyIds; - tags: ITag[]; }) { - super(STRATEGY_ORDER_CHANGED, p.createdBy, p.tags); + super(STRATEGY_ORDER_CHANGED, p.createdBy); const { project, featureName, environment, data, preData } = p; this.project = project; this.featureName = featureName; @@ -400,11 +384,10 @@ export class FeatureVariantEvent extends BaseEvent { project: string; featureName: string; createdBy: string | IUser; - tags: ITag[]; newVariants: IVariant[]; oldVariants: IVariant[]; }) { - super(FEATURE_VARIANTS_UPDATED, p.createdBy, p.tags); + super(FEATURE_VARIANTS_UPDATED, p.createdBy); this.project = p.project; this.featureName = p.featureName; this.data = { variants: p.newVariants }; @@ -431,11 +414,10 @@ export class EnvironmentVariantEvent extends BaseEvent { environment: string; project: string; createdBy: string | IUser; - tags: ITag[]; newVariants: IVariant[]; oldVariants: IVariant[]; }) { - super(FEATURE_ENVIRONMENT_VARIANTS_UPDATED, p.createdBy, p.tags); + super(FEATURE_ENVIRONMENT_VARIANTS_UPDATED, p.createdBy); this.featureName = p.featureName; this.environment = p.environment; this.project = p.project; @@ -462,9 +444,8 @@ export class FeatureChangeProjectEvent extends BaseEvent { newProject: string; featureName: string; createdBy: string | IUser; - tags: ITag[]; }) { - super(FEATURE_PROJECT_CHANGE, p.createdBy, p.tags); + super(FEATURE_PROJECT_CHANGE, p.createdBy); const { newProject, oldProject, featureName } = p; this.project = newProject; this.featureName = featureName; @@ -487,9 +468,8 @@ export class FeatureCreatedEvent extends BaseEvent { featureName: string; createdBy: string | IUser; data: FeatureToggle; - tags: ITag[]; }) { - super(FEATURE_CREATED, p.createdBy, p.tags); + super(FEATURE_CREATED, p.createdBy); const { project, featureName, data } = p; this.project = project; this.featureName = featureName; @@ -509,9 +489,8 @@ export class FeatureArchivedEvent extends BaseEvent { project: string; featureName: string; createdBy: string | IUser; - tags: ITag[]; }) { - super(FEATURE_ARCHIVED, p.createdBy, p.tags); + super(FEATURE_ARCHIVED, p.createdBy); const { project, featureName } = p; this.project = project; this.featureName = featureName; @@ -530,9 +509,8 @@ export class FeatureRevivedEvent extends BaseEvent { project: string; featureName: string; createdBy: string | IUser; - tags: ITag[]; }) { - super(FEATURE_REVIVED, p.createdBy, p.tags); + super(FEATURE_REVIVED, p.createdBy); const { project, featureName } = p; this.project = project; this.featureName = featureName; @@ -554,9 +532,8 @@ export class FeatureDeletedEvent extends BaseEvent { featureName: string; preData: FeatureToggle; createdBy: string | IUser; - tags: ITag[]; }) { - super(FEATURE_DELETED, p.createdBy, p.tags); + super(FEATURE_DELETED, p.createdBy); const { project, featureName, preData } = p; this.project = project; this.featureName = featureName; @@ -582,9 +559,8 @@ export class FeatureMetadataUpdateEvent extends BaseEvent { project: string; data: FeatureToggle; preData: FeatureToggle; - tags: ITag[]; }) { - super(FEATURE_METADATA_UPDATED, p.createdBy, p.tags); + super(FEATURE_METADATA_UPDATED, p.createdBy); const { project, featureName, data, preData } = p; this.project = project; this.featureName = featureName; @@ -611,9 +587,8 @@ export class FeatureStrategyAddEvent extends BaseEvent { environment: string; createdBy: string | IUser; data: IStrategyConfig; - tags: ITag[]; }) { - super(FEATURE_STRATEGY_ADD, p.createdBy, p.tags); + super(FEATURE_STRATEGY_ADD, p.createdBy); const { project, featureName, environment, data } = p; this.project = project; this.featureName = featureName; @@ -643,9 +618,8 @@ export class FeatureStrategyUpdateEvent extends BaseEvent { createdBy: string | IUser; data: IStrategyConfig; preData: IStrategyConfig; - tags: ITag[]; }) { - super(FEATURE_STRATEGY_UPDATE, p.createdBy, p.tags); + super(FEATURE_STRATEGY_UPDATE, p.createdBy); const { project, featureName, environment, data, preData } = p; this.project = project; this.featureName = featureName; @@ -673,9 +647,8 @@ export class FeatureStrategyRemoveEvent extends BaseEvent { environment: string; createdBy: string | IUser; preData: IStrategyConfig; - tags: ITag[]; }) { - super(FEATURE_STRATEGY_REMOVE, p.createdBy, p.tags); + super(FEATURE_STRATEGY_REMOVE, p.createdBy); const { project, featureName, environment, preData } = p; this.project = project; this.featureName = featureName; @@ -1073,12 +1046,8 @@ export class PotentiallyStaleOnEvent extends BaseEvent { readonly project: string; - constructor(eventData: { - featureName: string; - project: string; - tags: ITag[]; - }) { - super(FEATURE_POTENTIALLY_STALE_ON, 'unleash-system', eventData.tags); + constructor(eventData: { featureName: string; project: string }) { + super(FEATURE_POTENTIALLY_STALE_ON, 'unleash-system'); this.featureName = eventData.featureName; this.project = eventData.project; } diff --git a/src/test/e2e/api/admin/event.e2e.test.ts b/src/test/e2e/api/admin/event.e2e.test.ts index e5685321b5..659f7d4038 100644 --- a/src/test/e2e/api/admin/event.e2e.test.ts +++ b/src/test/e2e/api/admin/event.e2e.test.ts @@ -54,7 +54,6 @@ test('Can filter by project', async () => { type: FEATURE_CREATED, project: 'something-else', data: { id: 'some-other-feature' }, - tags: [], createdBy: 'test-user', environment: 'test', }); @@ -62,7 +61,6 @@ test('Can filter by project', async () => { type: FEATURE_CREATED, project: 'default', data: { id: 'feature' }, - tags: [], createdBy: 'test-user', environment: 'test', }); @@ -81,7 +79,6 @@ test('can search for events', async () => { type: FEATURE_CREATED, project: randomId(), data: { id: randomId() }, - tags: [], createdBy: randomId(), }, { @@ -89,7 +86,6 @@ test('can search for events', async () => { project: randomId(), data: { id: randomId() }, preData: { id: randomId() }, - tags: [{ type: 'simple', value: randomId() }], createdBy: randomId(), }, ]; @@ -130,12 +126,4 @@ test('can search for events', async () => { expect(res.body.events).toHaveLength(1); expect(res.body.events[0].preData.id).toEqual(events[1].preData.id); }); - await app.request - .post('/api/admin/events/search') - .send({ query: events[1].tags![0].value }) - .expect(200) - .expect((res) => { - expect(res.body.events).toHaveLength(1); - expect(res.body.events[0].data.id).toEqual(events[1].data.id); - }); }); diff --git a/src/test/e2e/services/project-service.e2e.test.ts b/src/test/e2e/services/project-service.e2e.test.ts index cd860a93f1..368afeeb25 100644 --- a/src/test/e2e/services/project-service.e2e.test.ts +++ b/src/test/e2e/services/project-service.e2e.test.ts @@ -1323,7 +1323,6 @@ test('should calculate average time to production', async () => { featureName: toggle.name, environment: 'default', createdBy: 'Fredrik', - tags: [], }), ); }), @@ -1599,7 +1598,6 @@ test('should return average time to production per toggle', async () => { featureName: toggle.name, environment: 'default', createdBy: 'Fredrik', - tags: [], }), ); }), @@ -1678,7 +1676,6 @@ test('should return average time to production per toggle for a specific project featureName: toggle.name, environment: 'default', createdBy: 'Fredrik', - tags: [], }), ); }), @@ -1693,7 +1690,6 @@ test('should return average time to production per toggle for a specific project featureName: toggle.name, environment: 'default', createdBy: 'Fredrik', - tags: [], }), ); }), @@ -1757,7 +1753,6 @@ test('should return average time to production per toggle and include archived t featureName: toggle.name, environment: 'default', createdBy: 'Fredrik', - tags: [], }), ); }), diff --git a/src/test/e2e/stores/event-store.e2e.test.ts b/src/test/e2e/stores/event-store.e2e.test.ts index 6c56769f43..1ed339f81a 100644 --- a/src/test/e2e/stores/event-store.e2e.test.ts +++ b/src/test/e2e/stores/event-store.e2e.test.ts @@ -52,33 +52,6 @@ test('Should include id and createdAt when saving', async () => { jest.useRealTimers(); }); -test('Should include empty tags array for new event', async () => { - expect.assertions(2); - const event = { - type: FEATURE_CREATED, - createdBy: 'me@mail.com', - data: { - name: 'someName', - enabled: true, - strategies: [{ name: 'default' }], - }, - }; - - const promise = new Promise((resolve) => { - eventStore.on(FEATURE_CREATED, (storedEvent: IEvent) => { - expect(storedEvent.data.name).toBe(event.data.name); - expect(Array.isArray(storedEvent.tags)).toBe(true); - resolve(); - }); - }); - - // Trigger - await eventStore.store(event); - await eventStore.publishUnannouncedEvents(); - - return promise; -}); - test('Should be able to store multiple events at once', async () => { jest.useFakeTimers(); const event1 = { @@ -104,10 +77,9 @@ test('Should be able to store multiple events at once', async () => { clientIp: '127.0.0.1', appName: 'test3', }, - tags: [{ type: 'simple', value: 'mytest' }], }; - const seen = []; - eventStore.on(APPLICATION_CREATED, (e) => seen.push(e)); + const seen: IEvent[] = []; + eventStore.on(APPLICATION_CREATED, (e: IEvent) => seen.push(e)); await eventStore.batchStore([event1, event2, event3]); await eventStore.publishUnannouncedEvents(); expect(seen.length).toBe(3); @@ -198,14 +170,12 @@ test('Should get all events of type', async () => { featureName: data.name, createdBy: 'test-user', data, - tags: [], }) : new FeatureDeletedEvent({ project: data.project, preData: data, featureName: data.name, createdBy: 'test-user', - tags: [], }); return eventStore.store(event); }),