1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-04 00:18:40 +01:00

feat: support filtering on project and environment fields for events (#1801)

* feat: support filtering on project and environment fields for events

Co-authored-by: Nuno Góis <github@nunogois.com>
This commit is contained in:
Christopher Kolstad 2022-07-12 14:13:25 +02:00 committed by GitHub
parent 847119f964
commit e3c9eaae3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 339 additions and 2 deletions

View File

@ -115,6 +115,8 @@ export default class AddonStore implements IAddonStore {
description: row.description,
parameters: row.parameters,
events: row.events,
projects: row.projects || [],
environments: row.environments || [],
createdAt: row.created_at,
};
}
@ -127,6 +129,8 @@ export default class AddonStore implements IAddonStore {
description: addon.description,
parameters: JSON.stringify(addon.parameters),
events: JSON.stringify(addon.events),
projects: JSON.stringify(addon.projects || []),
environments: JSON.stringify(addon.environments || []),
};
}
}

View File

@ -32,6 +32,18 @@ export const addonSchema = {
type: 'string',
},
},
projects: {
type: 'array',
items: {
type: 'string',
},
},
environments: {
type: 'array',
items: {
type: 'string',
},
},
},
components: {},
} as const;

View File

@ -12,5 +12,7 @@ export const addonSchema = joi
.pattern(joi.string(), [joi.string(), joi.number(), joi.boolean()])
.optional(),
events: joi.array().optional().items(joi.string()),
projects: joi.array().optional().items(joi.string()),
environments: joi.array().optional().items(joi.string()),
})
.options({ allowUnknown: false, stripUnknown: true });

View File

@ -13,15 +13,17 @@ import createStores from '../../test/fixtures/store';
import AddonService from './addon-service';
import { IAddonDto } from '../types/stores/addon-store';
import SimpleAddon from './addon-service-test-simple-addon';
import { IAddonProviders } from '../addons';
const MASKED_VALUE = '*****';
const addonProvider = { simple: new SimpleAddon() };
let addonProvider: IAddonProviders;
function getSetup() {
const stores = createStores();
const tagTypeService = new TagTypeService(stores, { getLogger });
addonProvider = { simple: new SimpleAddon() };
return {
addonService: new AddonService(
stores,
@ -49,7 +51,7 @@ test('should load addon configurations', async () => {
test('should load provider definitions', async () => {
const { addonService } = getSetup();
const providerDefinitions = await addonService.getProviderDefinitions();
const providerDefinitions = addonService.getProviderDefinitions();
const simple = providerDefinitions.find((p) => p.name === 'simple');
@ -110,6 +112,276 @@ test('should trigger simple-addon eventHandler', async () => {
expect(events[0].event.data.name).toBe('some-toggle');
});
test('should not trigger event handler if project of event is different from addon', async () => {
const { addonService, stores } = getSetup();
const config = {
provider: 'simple',
enabled: true,
events: [FEATURE_CREATED],
projects: ['someproject'],
description: '',
parameters: {
url: 'http://localhost:wh',
},
};
await addonService.createAddon(config, 'me@mail.com');
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: 'someotherproject',
data: {
name: 'some-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
});
const simpleProvider = addonService.addonProviders.simple;
// @ts-expect-error
const events = simpleProvider.getEvents();
expect(events.length).toBe(0);
});
test('should trigger event handler if project for event is one of the desired projects for addon', async () => {
const { addonService, stores } = getSetup();
const desiredProject = 'desired';
const otherProject = 'other';
const config = {
provider: 'simple',
enabled: true,
events: [FEATURE_CREATED],
projects: [desiredProject],
description: '',
parameters: {
url: 'http://localhost:wh',
},
};
await addonService.createAddon(config, 'me@mail.com');
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: desiredProject,
data: {
name: 'some-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
});
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: otherProject,
data: {
name: 'other-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
});
const simpleProvider = addonService.addonProviders.simple;
// @ts-expect-error
const events = simpleProvider.getEvents();
expect(events.length).toBe(1);
expect(events[0].event.type).toBe(FEATURE_CREATED);
expect(events[0].event.data.name).toBe('some-toggle');
});
test('should trigger events for multiple projects if addon is setup to filter multiple projects', async () => {
const { addonService, stores } = getSetup();
const desiredProjects = ['desired', 'desired2'];
const otherProject = 'other';
const config = {
provider: 'simple',
enabled: true,
events: [FEATURE_CREATED],
projects: desiredProjects,
description: '',
parameters: {
url: 'http://localhost:wh',
},
};
await addonService.createAddon(config, 'me@mail.com');
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: desiredProjects[0],
data: {
name: 'some-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
});
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: otherProject,
data: {
name: 'other-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
});
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: desiredProjects[1],
data: {
name: 'third-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
});
const simpleProvider = addonService.addonProviders.simple;
// @ts-expect-error
const events = simpleProvider.getEvents();
expect(events.length).toBe(2);
expect(events[0].event.type).toBe(FEATURE_CREATED);
expect(events[0].event.data.name).toBe('some-toggle');
expect(events[1].event.type).toBe(FEATURE_CREATED);
expect(events[1].event.data.name).toBe('third-toggle');
});
test('should filter events on environment if addon is setup to filter for it', async () => {
const { addonService, stores } = getSetup();
const desiredEnvironment = 'desired';
const otherEnvironment = 'other';
const config = {
provider: 'simple',
enabled: true,
events: [FEATURE_CREATED],
projects: [],
environments: [desiredEnvironment],
description: '',
parameters: {
url: 'http://localhost:wh',
},
};
await addonService.createAddon(config, 'me@mail.com');
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: desiredEnvironment,
environment: desiredEnvironment,
data: {
name: 'some-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
});
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
environment: otherEnvironment,
data: {
name: 'other-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
});
const simpleProvider = addonService.addonProviders.simple;
// @ts-expect-error
const events = simpleProvider.getEvents();
expect(events.length).toBe(1);
expect(events[0].event.type).toBe(FEATURE_CREATED);
expect(events[0].event.data.name).toBe('some-toggle');
});
test('Should support filtering by both project and environment', async () => {
const { addonService, stores } = getSetup();
const desiredProjects = ['desired1', 'desired2', 'desired3'];
const desiredEnvironments = ['env1', 'env2', 'env3'];
const config = {
provider: 'simple',
enabled: true,
events: [FEATURE_CREATED],
projects: desiredProjects,
environments: desiredEnvironments,
description: '',
parameters: {
url: 'http://localhost:wh',
},
};
const expectedFeatureNames = [
'desired-toggle1',
'desired-toggle2',
'desired-toggle3',
];
await addonService.createAddon(config, 'me@mail.com');
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: desiredProjects[0],
environment: desiredEnvironments[0],
data: {
name: expectedFeatureNames[0],
enabled: false,
strategies: [{ name: 'default' }],
},
});
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: desiredProjects[0],
environment: 'wrongenvironment',
data: {
name: 'other-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
});
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: desiredProjects[2],
environment: desiredEnvironments[1],
data: {
name: expectedFeatureNames[1],
enabled: false,
strategies: [{ name: 'default' }],
},
});
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: desiredProjects[2],
environment: desiredEnvironments[2],
data: {
name: expectedFeatureNames[2],
enabled: false,
strategies: [{ name: 'default' }],
},
});
await stores.eventStore.store({
type: FEATURE_CREATED,
createdBy: 'some@user.com',
project: 'wrongproject',
environment: desiredEnvironments[0],
data: {
name: 'not-expected',
enabled: false,
strategies: [{ name: 'default' }],
},
});
const simpleProvider = addonService.addonProviders.simple;
// @ts-expect-error
const events = simpleProvider.getEvents();
expect(events.length).toBe(3);
expect(events[0].event.type).toBe(FEATURE_CREATED);
expect(events[0].event.data.name).toBe(expectedFeatureNames[0]);
expect(events[1].event.type).toBe(FEATURE_CREATED);
expect(events[1].event.data.name).toBe(expectedFeatureNames[1]);
expect(events[2].event.type).toBe(FEATURE_CREATED);
expect(events[2].event.data.name).toBe(expectedFeatureNames[2]);
});
test('should create simple-addon config', async () => {
const { addonService } = getSetup();

View File

@ -105,6 +105,18 @@ export default class AddonService {
this.fetchAddonConfigs().then((addonInstances) => {
addonInstances
.filter((addon) => addon.events.includes(eventName))
.filter(
(addon) =>
!addon.projects ||
addon.projects.length == 0 ||
addon.projects.includes(event.project),
)
.filter(
(addon) =>
!addon.environments ||
addon.environments.length == 0 ||
addon.environments.includes(event.environment),
)
.filter((addon) => addonProviders[addon.provider])
.forEach((addon) =>
addonProviders[addon.provider].handleEvent(

View File

@ -5,6 +5,8 @@ export interface IAddonDto {
description: string;
enabled: boolean;
parameters: Record<string, unknown>;
projects?: string[];
environments?: string[];
events: string[];
}

View File

@ -0,0 +1,21 @@
exports.up = function (db, cb) {
db.runSql(
`ALTER TABLE addons
ADD COLUMN
projects jsonb DEFAULT '[]'::jsonb;
ALTER TABLE addons
ADD COLUMN environments jsonb DEFAULT '[]'::jsonb;
`,
cb,
);
};
exports.down = function (db, cb) {
db.runSql(
`
ALTER TABLE addons DROP COLUMN projects;
ALTER TABLE addons DROP COLUMN environments;
`,
cb,
);
};

View File

@ -97,6 +97,12 @@ Object {
"enabled": Object {
"type": "boolean",
},
"environments": Object {
"items": Object {
"type": "string",
},
"type": "array",
},
"events": Object {
"items": Object {
"type": "string",
@ -110,6 +116,12 @@ Object {
"additionalProperties": true,
"type": "object",
},
"projects": Object {
"items": Object {
"type": "string",
},
"type": "array",
},
"provider": Object {
"type": "string",
},