mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-17 13:46:47 +02:00
Detailed slack notifications on feature toggle update (#3155)
## About the changes Adds more specifics to addon notifications when a flexibleRollout is updated. - [x] Specific text for all strategy types - [x] Add constraint differences for all strategy types <!-- Does it close an issue? Multiple? --> Closes [#3140](https://github.com/Unleash/unleash/issues/3140)
This commit is contained in:
parent
eee5365568
commit
4f14549fa1
512
src/lib/addons/feature-event-formatter-md.test.ts
Normal file
512
src/lib/addons/feature-event-formatter-md.test.ts
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
import {
|
||||||
|
FEATURE_STRATEGY_ADD,
|
||||||
|
FEATURE_STRATEGY_REMOVE,
|
||||||
|
FEATURE_STRATEGY_UPDATE,
|
||||||
|
IEvent,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
|
import { FeatureEventFormatterMd } from './feature-event-formatter-md';
|
||||||
|
import {
|
||||||
|
DATE_AFTER,
|
||||||
|
DATE_BEFORE,
|
||||||
|
IN,
|
||||||
|
NOT_IN,
|
||||||
|
NUM_EQ,
|
||||||
|
NUM_GT,
|
||||||
|
NUM_GTE,
|
||||||
|
NUM_LT,
|
||||||
|
NUM_LTE,
|
||||||
|
SEMVER_EQ,
|
||||||
|
SEMVER_GT,
|
||||||
|
SEMVER_LT,
|
||||||
|
STR_CONTAINS,
|
||||||
|
STR_ENDS_WITH,
|
||||||
|
STR_STARTS_WITH,
|
||||||
|
} from '../util';
|
||||||
|
|
||||||
|
const testCases: [string, IEvent, string][] = [
|
||||||
|
[
|
||||||
|
'when groupId changed',
|
||||||
|
{
|
||||||
|
id: 920,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
||||||
|
data: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {
|
||||||
|
groupId: 'different-feature',
|
||||||
|
rollout: '32',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '32',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
constraints: [],
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by updating strategy flexibleRollout in *production* groupId from new-feature to different-feature',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'when rollout percentage changed',
|
||||||
|
{
|
||||||
|
id: 920,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
||||||
|
data: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '32',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '67',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
constraints: [],
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by updating strategy flexibleRollout in *production* rollout from 67% to 32%',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'when stickiness changed',
|
||||||
|
{
|
||||||
|
id: 920,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
||||||
|
data: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '67',
|
||||||
|
stickiness: 'random',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '67',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
constraints: [],
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by updating strategy flexibleRollout in *production* stickiness from default to random',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'when constraints and rollout percentage and stickiness changed',
|
||||||
|
{
|
||||||
|
id: 920,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
||||||
|
data: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
constraints: [
|
||||||
|
{
|
||||||
|
values: ['x', 'y'],
|
||||||
|
inverted: false,
|
||||||
|
operator: IN,
|
||||||
|
contextName: 'appName',
|
||||||
|
caseInsensitive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '32',
|
||||||
|
stickiness: 'random',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '67',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
constraints: [],
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by updating strategy flexibleRollout in *production* stickiness from default to random; rollout from 67% to 32%; constraints from empty set of constraints to [appName is one of (x,y)]',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'when neither rollout percentage nor stickiness changed',
|
||||||
|
{
|
||||||
|
id: 920,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
||||||
|
data: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '67',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '67',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
constraints: [],
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by updating strategy flexibleRollout in *production*',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'when strategy added',
|
||||||
|
{
|
||||||
|
id: 919,
|
||||||
|
type: FEATURE_STRATEGY_ADD,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:08.290Z'),
|
||||||
|
data: {
|
||||||
|
id: '3f4bf713-696c-43a4-8ce7-d6c607108858',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {
|
||||||
|
groupId: 'new-feature',
|
||||||
|
rollout: '67',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preData: null,
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by adding strategy flexibleRollout in *production*',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'when strategy removed',
|
||||||
|
{
|
||||||
|
id: 918,
|
||||||
|
type: FEATURE_STRATEGY_REMOVE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:00.229Z'),
|
||||||
|
data: null,
|
||||||
|
preData: {
|
||||||
|
id: '9591090e-acb0-4088-8958-21faaeb7147d',
|
||||||
|
name: 'default',
|
||||||
|
parameters: {},
|
||||||
|
constraints: [],
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by removing strategy default in *production*',
|
||||||
|
],
|
||||||
|
...[
|
||||||
|
[IN, 'is one of'],
|
||||||
|
[NOT_IN, 'is not one of'],
|
||||||
|
[STR_CONTAINS, 'is a string that contains'],
|
||||||
|
[STR_STARTS_WITH, 'is a string that starts with'],
|
||||||
|
[STR_ENDS_WITH, 'is a string that ends with'],
|
||||||
|
].map(
|
||||||
|
([operator, display]) =>
|
||||||
|
<[string, IEvent, string]>[
|
||||||
|
'when default strategy updated',
|
||||||
|
{
|
||||||
|
id: 39,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'admin',
|
||||||
|
createdAt: new Date('2023-02-20T20:23:28.791Z'),
|
||||||
|
data: {
|
||||||
|
id: 'f2d34aac-52ec-49d2-82d3-08d710e89eaa',
|
||||||
|
name: 'default',
|
||||||
|
constraints: [
|
||||||
|
{
|
||||||
|
values: ['x', 'y'],
|
||||||
|
inverted: false,
|
||||||
|
operator: operator,
|
||||||
|
contextName: 'appName',
|
||||||
|
caseInsensitive: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
values: ['x'],
|
||||||
|
inverted: true,
|
||||||
|
operator: operator,
|
||||||
|
contextName: 'appName',
|
||||||
|
caseInsensitive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parameters: {},
|
||||||
|
segments: [],
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
id: 'f2d34aac-52ec-49d2-82d3-08d710e89eaa',
|
||||||
|
name: 'default',
|
||||||
|
segments: [],
|
||||||
|
parameters: {},
|
||||||
|
constraints: [],
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'aaa',
|
||||||
|
project: 'default',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
`admin updated *[aaa](unleashUrl/projects/default/features/aaa)* in project *default* by updating strategy default in *production* constraints from empty set of constraints to [appName ${display} (x,y), appName not ${display} (x)]`,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
...[
|
||||||
|
[NUM_EQ, 'is a number equal to'],
|
||||||
|
[NUM_GT, 'is a number greater than'],
|
||||||
|
[NUM_GTE, 'is a number greater than or equal to'],
|
||||||
|
[NUM_LT, 'is a number less than'],
|
||||||
|
[NUM_LTE, 'is a number less than or equal to'],
|
||||||
|
[DATE_BEFORE, 'is a date before'],
|
||||||
|
[DATE_AFTER, 'is a date after'],
|
||||||
|
[SEMVER_EQ, 'is a SemVer equal to'],
|
||||||
|
[SEMVER_GT, 'is a SemVer greater than'],
|
||||||
|
[SEMVER_LT, 'is a SemVer less than'],
|
||||||
|
].map(
|
||||||
|
([operator, display]) =>
|
||||||
|
<[string, IEvent, string]>[
|
||||||
|
'when default strategy updated with numeric constraint ' +
|
||||||
|
operator,
|
||||||
|
{
|
||||||
|
id: 39,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'admin',
|
||||||
|
createdAt: new Date('2023-02-20T20:23:28.791Z'),
|
||||||
|
data: {
|
||||||
|
id: 'f2d34aac-52ec-49d2-82d3-08d710e89eaa',
|
||||||
|
name: 'default',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {},
|
||||||
|
segments: [],
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
id: 'f2d34aac-52ec-49d2-82d3-08d710e89eaa',
|
||||||
|
name: 'default',
|
||||||
|
segments: [],
|
||||||
|
parameters: {},
|
||||||
|
constraints: [
|
||||||
|
{
|
||||||
|
value: '4',
|
||||||
|
values: [],
|
||||||
|
inverted: false,
|
||||||
|
operator: operator,
|
||||||
|
contextName: 'appName',
|
||||||
|
caseInsensitive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'aaa',
|
||||||
|
project: 'default',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
`admin updated *[aaa](unleashUrl/projects/default/features/aaa)* in project *default* by updating strategy default in *production* constraints from [appName ${display} 4] to empty set of constraints`,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
'when userIds changed',
|
||||||
|
{
|
||||||
|
id: 920,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
||||||
|
data: {
|
||||||
|
name: 'userWithId',
|
||||||
|
constraints: [
|
||||||
|
{
|
||||||
|
values: ['x', 'y'],
|
||||||
|
inverted: false,
|
||||||
|
operator: IN,
|
||||||
|
contextName: 'appName',
|
||||||
|
caseInsensitive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
userIds: 'a,b',
|
||||||
|
},
|
||||||
|
sortOrder: 9999,
|
||||||
|
id: '9a995d94-5944-4897-a82f-0f7e65c2fb3f',
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
name: 'userWithId',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {
|
||||||
|
userIds: '',
|
||||||
|
},
|
||||||
|
sortOrder: 9999,
|
||||||
|
id: '9a995d94-5944-4897-a82f-0f7e65c2fb3f',
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by updating strategy userWithId in *production* userIds from empty set of userIds to [a,b]; constraints from empty set of constraints to [appName is one of (x,y)]',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'when IPs changed',
|
||||||
|
{
|
||||||
|
id: 920,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
||||||
|
data: {
|
||||||
|
name: 'remoteAddress',
|
||||||
|
constraints: [
|
||||||
|
{
|
||||||
|
values: ['x', 'y'],
|
||||||
|
inverted: false,
|
||||||
|
operator: IN,
|
||||||
|
contextName: 'appName',
|
||||||
|
caseInsensitive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
IPs: '127.0.0.1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
name: 'remoteAddress',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {
|
||||||
|
IPs: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by updating strategy remoteAddress in *production* IPs from empty set of IPs to [127.0.0.1]; constraints from empty set of constraints to [appName is one of (x,y)]',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'when host names changed',
|
||||||
|
{
|
||||||
|
id: 920,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
||||||
|
data: {
|
||||||
|
name: 'applicationHostname',
|
||||||
|
constraints: [
|
||||||
|
{
|
||||||
|
values: ['x', 'y'],
|
||||||
|
inverted: false,
|
||||||
|
operator: IN,
|
||||||
|
contextName: 'appName',
|
||||||
|
caseInsensitive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
hostNames: 'unleash.com',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
name: 'applicationHostname',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {
|
||||||
|
hostNames: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by updating strategy applicationHostname in *production* hostNames from empty set of hostNames to [unleash.com]; constraints from empty set of constraints to [appName is one of (x,y)]',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'when no specific text for strategy exists yet',
|
||||||
|
{
|
||||||
|
id: 920,
|
||||||
|
type: FEATURE_STRATEGY_UPDATE,
|
||||||
|
createdBy: 'user@company.com',
|
||||||
|
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
||||||
|
data: {
|
||||||
|
name: 'newStrategy',
|
||||||
|
constraints: [
|
||||||
|
{
|
||||||
|
values: ['x', 'y'],
|
||||||
|
inverted: false,
|
||||||
|
operator: IN,
|
||||||
|
contextName: 'appName',
|
||||||
|
caseInsensitive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
IPs: '127.0.0.1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preData: {
|
||||||
|
name: 'newStrategy',
|
||||||
|
constraints: [],
|
||||||
|
parameters: {
|
||||||
|
IPs: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
featureName: 'new-feature',
|
||||||
|
project: 'my-other-project',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
'user@company.com updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *my-other-project* by updating strategy newStrategy in *production*',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
testCases.forEach(([description, event, expected]) =>
|
||||||
|
test('Should format specialised text for events ' + description, () => {
|
||||||
|
const formatter = new FeatureEventFormatterMd('unleashUrl');
|
||||||
|
const actual = formatter.format(event);
|
||||||
|
expect(actual).toBe(expected);
|
||||||
|
}),
|
||||||
|
);
|
@ -1,20 +1,21 @@
|
|||||||
import {
|
import {
|
||||||
FEATURE_CREATED,
|
|
||||||
FEATURE_UPDATED,
|
|
||||||
FEATURE_ARCHIVED,
|
FEATURE_ARCHIVED,
|
||||||
FEATURE_STALE_ON,
|
FEATURE_CREATED,
|
||||||
FEATURE_STRATEGY_UPDATE,
|
|
||||||
FEATURE_STRATEGY_ADD,
|
|
||||||
FEATURE_ENVIRONMENT_ENABLED,
|
|
||||||
FEATURE_REVIVED,
|
|
||||||
FEATURE_STALE_OFF,
|
|
||||||
FEATURE_ENVIRONMENT_DISABLED,
|
FEATURE_ENVIRONMENT_DISABLED,
|
||||||
FEATURE_STRATEGY_REMOVE,
|
FEATURE_ENVIRONMENT_ENABLED,
|
||||||
FEATURE_METADATA_UPDATED,
|
FEATURE_METADATA_UPDATED,
|
||||||
FEATURE_PROJECT_CHANGE,
|
FEATURE_PROJECT_CHANGE,
|
||||||
IEvent,
|
FEATURE_REVIVED,
|
||||||
|
FEATURE_STALE_OFF,
|
||||||
|
FEATURE_STALE_ON,
|
||||||
|
FEATURE_STRATEGY_ADD,
|
||||||
|
FEATURE_STRATEGY_REMOVE,
|
||||||
|
FEATURE_STRATEGY_UPDATE,
|
||||||
|
FEATURE_UPDATED,
|
||||||
FEATURE_VARIANTS_UPDATED,
|
FEATURE_VARIANTS_UPDATED,
|
||||||
} from '../types/events';
|
IConstraint,
|
||||||
|
IEvent,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
export interface FeatureEventFormatter {
|
export interface FeatureEventFormatter {
|
||||||
format: (event: IEvent) => string;
|
format: (event: IEvent) => string;
|
||||||
@ -27,9 +28,9 @@ export enum LinkStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
||||||
private unleashUrl: string;
|
private readonly unleashUrl: string;
|
||||||
|
|
||||||
private linkStyle: LinkStyle;
|
private readonly linkStyle: LinkStyle;
|
||||||
|
|
||||||
constructor(unleashUrl: string, linkStyle: LinkStyle = LinkStyle.MD) {
|
constructor(unleashUrl: string, linkStyle: LinkStyle = LinkStyle.MD) {
|
||||||
this.unleashUrl = unleashUrl;
|
this.unleashUrl = unleashUrl;
|
||||||
@ -71,17 +72,222 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
generateStrategyChangeText(event: IEvent): string {
|
generateStrategyChangeText(event: IEvent): string {
|
||||||
const { createdBy, environment, project, data, preData, type } = event;
|
const { createdBy, environment, project, data, preData } = event;
|
||||||
const feature = this.generateFeatureLink(event);
|
const feature = this.generateFeatureLink(event);
|
||||||
let strategyText: string = '';
|
const strategyText = () => {
|
||||||
if (FEATURE_STRATEGY_UPDATE === type) {
|
switch (data.name) {
|
||||||
strategyText = `by updating strategy ${data?.name} in *${environment}*`;
|
case 'flexibleRollout':
|
||||||
} else if (FEATURE_STRATEGY_ADD === type) {
|
return this.flexibleRolloutStrategyChangeText(
|
||||||
strategyText = `by adding strategy ${data?.name} in *${environment}*`;
|
preData,
|
||||||
} else if (FEATURE_STRATEGY_REMOVE === type) {
|
data,
|
||||||
strategyText = `by removing strategy ${preData?.name} in *${environment}*`;
|
environment,
|
||||||
|
);
|
||||||
|
case 'default':
|
||||||
|
return this.defaultStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment,
|
||||||
|
);
|
||||||
|
case 'userWithId':
|
||||||
|
return this.userWithIdStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment,
|
||||||
|
);
|
||||||
|
case 'remoteAddress':
|
||||||
|
return this.remoteAddressStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment,
|
||||||
|
);
|
||||||
|
case 'applicationHostname':
|
||||||
|
return this.applicationHostnameStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return `by updating strategy ${data?.name} in *${environment}*`;
|
||||||
}
|
}
|
||||||
return `${createdBy} updated *${feature}* in project *${project}* ${strategyText}`;
|
};
|
||||||
|
|
||||||
|
return `${createdBy} updated *${feature}* in project *${project}* ${strategyText()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private applicationHostnameStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment: string | undefined,
|
||||||
|
) {
|
||||||
|
return this.listOfValuesStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment,
|
||||||
|
'hostNames',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private remoteAddressStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment: string | undefined,
|
||||||
|
) {
|
||||||
|
return this.listOfValuesStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment,
|
||||||
|
'IPs',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private userWithIdStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment: string | undefined,
|
||||||
|
) {
|
||||||
|
return this.listOfValuesStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment,
|
||||||
|
'userIds',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private listOfValuesStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment: string | undefined,
|
||||||
|
propertyName: string,
|
||||||
|
) {
|
||||||
|
const userIdText = (values) =>
|
||||||
|
values.length === 0
|
||||||
|
? `empty set of ${propertyName}`
|
||||||
|
: `[${values}]`;
|
||||||
|
const usersText =
|
||||||
|
preData.parameters[propertyName] === data.parameters[propertyName]
|
||||||
|
? ''
|
||||||
|
: ` ${propertyName} from ${userIdText(
|
||||||
|
preData.parameters[propertyName],
|
||||||
|
)} to ${userIdText(data.parameters[propertyName])}`;
|
||||||
|
const constraintText = this.constraintChangeText(
|
||||||
|
preData.constraints,
|
||||||
|
data.constraints,
|
||||||
|
);
|
||||||
|
const strategySpecificText = [usersText, constraintText]
|
||||||
|
.filter((x) => x.length)
|
||||||
|
.join(';');
|
||||||
|
return `by updating strategy ${data?.name} in *${environment}*${strategySpecificText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private flexibleRolloutStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment: string | undefined,
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
rollout: oldRollout,
|
||||||
|
stickiness: oldStickiness,
|
||||||
|
groupId: oldGroupId,
|
||||||
|
} = preData.parameters;
|
||||||
|
const { rollout, stickiness, groupId } = data.parameters;
|
||||||
|
const stickinessText =
|
||||||
|
oldStickiness === stickiness
|
||||||
|
? ''
|
||||||
|
: ` stickiness from ${oldStickiness} to ${stickiness}`;
|
||||||
|
const rolloutText =
|
||||||
|
oldRollout === rollout
|
||||||
|
? ''
|
||||||
|
: ` rollout from ${oldRollout}% to ${rollout}%`;
|
||||||
|
const groupIdText =
|
||||||
|
oldGroupId === groupId
|
||||||
|
? ''
|
||||||
|
: ` groupId from ${oldGroupId} to ${groupId}`;
|
||||||
|
const constraintText = this.constraintChangeText(
|
||||||
|
preData.constraints,
|
||||||
|
data.constraints,
|
||||||
|
);
|
||||||
|
const strategySpecificText = [
|
||||||
|
stickinessText,
|
||||||
|
rolloutText,
|
||||||
|
groupIdText,
|
||||||
|
constraintText,
|
||||||
|
]
|
||||||
|
.filter((txt) => txt.length)
|
||||||
|
.join(';');
|
||||||
|
return `by updating strategy ${data?.name} in *${environment}*${strategySpecificText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private defaultStrategyChangeText(
|
||||||
|
preData,
|
||||||
|
data,
|
||||||
|
environment: string | undefined,
|
||||||
|
) {
|
||||||
|
return `by updating strategy ${
|
||||||
|
data?.name
|
||||||
|
} in *${environment}*${this.constraintChangeText(
|
||||||
|
preData.constraints,
|
||||||
|
data.constraints,
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private constraintChangeText(
|
||||||
|
oldConstraints: IConstraint[],
|
||||||
|
newConstraints: IConstraint[],
|
||||||
|
) {
|
||||||
|
const formatConstraints = (constraints: IConstraint[]) => {
|
||||||
|
const constraintOperatorDescriptions = {
|
||||||
|
IN: 'is one of',
|
||||||
|
NOT_IN: 'is not one of',
|
||||||
|
STR_CONTAINS: 'is a string that contains',
|
||||||
|
STR_STARTS_WITH: 'is a string that starts with',
|
||||||
|
STR_ENDS_WITH: 'is a string that ends with',
|
||||||
|
NUM_EQ: 'is a number equal to',
|
||||||
|
NUM_GT: 'is a number greater than',
|
||||||
|
NUM_GTE: 'is a number greater than or equal to',
|
||||||
|
NUM_LT: 'is a number less than',
|
||||||
|
NUM_LTE: 'is a number less than or equal to',
|
||||||
|
DATE_BEFORE: 'is a date before',
|
||||||
|
DATE_AFTER: 'is a date after',
|
||||||
|
SEMVER_EQ: 'is a SemVer equal to',
|
||||||
|
SEMVER_GT: 'is a SemVer greater than',
|
||||||
|
SEMVER_LT: 'is a SemVer less than',
|
||||||
|
};
|
||||||
|
const formatConstraint = (constraint: IConstraint) => {
|
||||||
|
const val = constraint.hasOwnProperty('value')
|
||||||
|
? constraint.value
|
||||||
|
: `(${constraint.values.join(',')})`;
|
||||||
|
const operator = constraintOperatorDescriptions.hasOwnProperty(
|
||||||
|
constraint.operator,
|
||||||
|
)
|
||||||
|
? constraintOperatorDescriptions[constraint.operator]
|
||||||
|
: constraint.operator;
|
||||||
|
|
||||||
|
return `${constraint.contextName} ${
|
||||||
|
constraint.inverted ? 'not ' : ''
|
||||||
|
}${operator} ${val}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
return constraints.length === 0
|
||||||
|
? 'empty set of constraints'
|
||||||
|
: `[${constraints.map(formatConstraint).join(', ')}]`;
|
||||||
|
};
|
||||||
|
const oldConstraintText = formatConstraints(oldConstraints);
|
||||||
|
const newConstraintText = formatConstraints(newConstraints);
|
||||||
|
return oldConstraintText === newConstraintText
|
||||||
|
? ''
|
||||||
|
: ` constraints from ${oldConstraintText} to ${newConstraintText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateStrategyRemoveText(event: IEvent): string {
|
||||||
|
const { createdBy, environment, project, preData } = event;
|
||||||
|
const feature = this.generateFeatureLink(event);
|
||||||
|
return `${createdBy} updated *${feature}* in project *${project}* by removing strategy ${preData?.name} in *${environment}*`;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateStrategyAddText(event: IEvent): string {
|
||||||
|
const { createdBy, environment, project, data } = event;
|
||||||
|
const feature = this.generateFeatureLink(event);
|
||||||
|
return `${createdBy} updated *${feature}* in project *${project}* by adding strategy ${data?.name} in *${environment}*`;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateMetadataText(event: IEvent): string {
|
generateMetadataText(event: IEvent): string {
|
||||||
@ -138,8 +344,10 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
case FEATURE_ENVIRONMENT_DISABLED:
|
case FEATURE_ENVIRONMENT_DISABLED:
|
||||||
case FEATURE_ENVIRONMENT_ENABLED:
|
case FEATURE_ENVIRONMENT_ENABLED:
|
||||||
return this.generateEnvironmentToggleText(event);
|
return this.generateEnvironmentToggleText(event);
|
||||||
case FEATURE_STRATEGY_ADD:
|
|
||||||
case FEATURE_STRATEGY_REMOVE:
|
case FEATURE_STRATEGY_REMOVE:
|
||||||
|
return this.generateStrategyRemoveText(event);
|
||||||
|
case FEATURE_STRATEGY_ADD:
|
||||||
|
return this.generateStrategyAddText(event);
|
||||||
case FEATURE_STRATEGY_UPDATE:
|
case FEATURE_STRATEGY_UPDATE:
|
||||||
return this.generateStrategyChangeText(event);
|
return this.generateStrategyChangeText(event);
|
||||||
case FEATURE_METADATA_UPDATED:
|
case FEATURE_METADATA_UPDATED:
|
||||||
|
Loading…
Reference in New Issue
Block a user