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