mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
chore: find segment strategies in CRs (#5365)
This PR adds the ability to detect which strategies use a specific segment in active change requests. It does not wire this functionality up to anything just yet. Follow-up PRs will integrate this with the segment service and eventually with the front end.
This commit is contained in:
parent
7a8c8c8d29
commit
27252f7728
@ -113,7 +113,7 @@ describe.each([
|
|||||||
])('Should handle %s changes correctly', (_, addOrUpdateStrategy) => {
|
])('Should handle %s changes correctly', (_, addOrUpdateStrategy) => {
|
||||||
test.each([
|
test.each([
|
||||||
['Draft', true],
|
['Draft', true],
|
||||||
['In Review', true],
|
['In review', true],
|
||||||
['Scheduled', true],
|
['Scheduled', true],
|
||||||
['Approved', true],
|
['Approved', true],
|
||||||
['Rejected', false],
|
['Rejected', false],
|
||||||
@ -133,3 +133,76 @@ describe.each([
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
['Draft', true],
|
||||||
|
['In review', true],
|
||||||
|
['Scheduled', true],
|
||||||
|
['Approved', true],
|
||||||
|
['Rejected', false],
|
||||||
|
['Cancelled', false],
|
||||||
|
['Applied', false],
|
||||||
|
])(
|
||||||
|
'addStrategy events in %s CRs should show up only of the CR is active',
|
||||||
|
async (state, isActiveCr) => {
|
||||||
|
await createCR(state);
|
||||||
|
|
||||||
|
const segmentId = 3;
|
||||||
|
|
||||||
|
await addStrategyToCr(segmentId, FLAG_NAME);
|
||||||
|
|
||||||
|
const result = await readModel.getStrategiesUsedInActiveChangeRequests(
|
||||||
|
segmentId,
|
||||||
|
);
|
||||||
|
if (isActiveCr) {
|
||||||
|
expect(result).toStrictEqual([
|
||||||
|
{
|
||||||
|
projectId: 'default',
|
||||||
|
strategyName: 'flexibleRollout',
|
||||||
|
environment: 'default',
|
||||||
|
featureName: FLAG_NAME,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
expect(result).toStrictEqual([]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
['Draft', true],
|
||||||
|
['In review', true],
|
||||||
|
['Scheduled', true],
|
||||||
|
['Approved', true],
|
||||||
|
['Rejected', false],
|
||||||
|
['Cancelled', false],
|
||||||
|
['Applied', false],
|
||||||
|
])(
|
||||||
|
`updateStrategy events in %s CRs should show up only of the CR is active`,
|
||||||
|
async (state, isActiveCr) => {
|
||||||
|
await createCR(state);
|
||||||
|
|
||||||
|
const segmentId = 3;
|
||||||
|
|
||||||
|
const strategyId = randomId();
|
||||||
|
await updateStrategyInCr(strategyId, segmentId, FLAG_NAME);
|
||||||
|
|
||||||
|
const result = await readModel.getStrategiesUsedInActiveChangeRequests(
|
||||||
|
segmentId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isActiveCr) {
|
||||||
|
expect(result).toMatchObject([
|
||||||
|
{
|
||||||
|
id: strategyId,
|
||||||
|
projectId: 'default',
|
||||||
|
strategyName: 'flexibleRollout',
|
||||||
|
environment: 'default',
|
||||||
|
featureName: FLAG_NAME,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
expect(result).toStrictEqual([]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
type NewStrategy = {
|
||||||
|
projectId: string;
|
||||||
|
featureName: string;
|
||||||
|
strategyName: string;
|
||||||
|
environment: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ExistingStrategy = NewStrategy & { id: string };
|
||||||
|
|
||||||
|
export type ChangeRequestStrategy = NewStrategy | ExistingStrategy;
|
||||||
|
|
||||||
export interface IChangeRequestSegmentUsageReadModel {
|
export interface IChangeRequestSegmentUsageReadModel {
|
||||||
isSegmentUsedInActiveChangeRequests(segmentId: number): Promise<boolean>;
|
isSegmentUsedInActiveChangeRequests(segmentId: number): Promise<boolean>;
|
||||||
|
getStrategiesUsedInActiveChangeRequests(
|
||||||
|
segmentId: number,
|
||||||
|
): Promise<ChangeRequestStrategy[]>;
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,32 @@
|
|||||||
import { IChangeRequestSegmentUsageReadModel } from './change-request-segment-usage-read-model';
|
import {
|
||||||
|
ChangeRequestStrategy,
|
||||||
|
IChangeRequestSegmentUsageReadModel,
|
||||||
|
} from './change-request-segment-usage-read-model';
|
||||||
|
|
||||||
export class FakeChangeRequestSegmentUsageReadModel
|
export class FakeChangeRequestSegmentUsageReadModel
|
||||||
implements IChangeRequestSegmentUsageReadModel
|
implements IChangeRequestSegmentUsageReadModel
|
||||||
{
|
{
|
||||||
private isSegmentUsedInActiveChangeRequestsValue: boolean;
|
private isSegmentUsedInActiveChangeRequestsValue: boolean;
|
||||||
|
strategiesUsedInActiveChangeRequests: ChangeRequestStrategy[];
|
||||||
|
|
||||||
constructor(isSegmentUsedInActiveChangeRequests = false) {
|
constructor(
|
||||||
|
isSegmentUsedInActiveChangeRequests = false,
|
||||||
|
strategiesUsedInActiveChangeRequests = [],
|
||||||
|
) {
|
||||||
this.isSegmentUsedInActiveChangeRequestsValue =
|
this.isSegmentUsedInActiveChangeRequestsValue =
|
||||||
isSegmentUsedInActiveChangeRequests;
|
isSegmentUsedInActiveChangeRequests;
|
||||||
|
|
||||||
|
this.strategiesUsedInActiveChangeRequests =
|
||||||
|
strategiesUsedInActiveChangeRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async isSegmentUsedInActiveChangeRequests(): Promise<boolean> {
|
public async isSegmentUsedInActiveChangeRequests(): Promise<boolean> {
|
||||||
return this.isSegmentUsedInActiveChangeRequestsValue;
|
return this.isSegmentUsedInActiveChangeRequestsValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getStrategiesUsedInActiveChangeRequests(): Promise<
|
||||||
|
ChangeRequestStrategy[]
|
||||||
|
> {
|
||||||
|
return this.strategiesUsedInActiveChangeRequests;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { Db } from '../../db/db';
|
import { Db } from '../../db/db';
|
||||||
import { IChangeRequestSegmentUsageReadModel } from './change-request-segment-usage-read-model';
|
import {
|
||||||
|
ChangeRequestStrategy,
|
||||||
|
IChangeRequestSegmentUsageReadModel,
|
||||||
|
} from './change-request-segment-usage-read-model';
|
||||||
|
|
||||||
export class ChangeRequestSegmentUsageReadModel
|
export class ChangeRequestSegmentUsageReadModel
|
||||||
implements IChangeRequestSegmentUsageReadModel
|
implements IChangeRequestSegmentUsageReadModel
|
||||||
@ -9,7 +12,6 @@ export class ChangeRequestSegmentUsageReadModel
|
|||||||
constructor(db: Db) {
|
constructor(db: Db) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async isSegmentUsedInActiveChangeRequests(
|
public async isSegmentUsedInActiveChangeRequests(
|
||||||
segmentId: number,
|
segmentId: number,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
@ -17,7 +19,7 @@ export class ChangeRequestSegmentUsageReadModel
|
|||||||
`SELECT events.*
|
`SELECT events.*
|
||||||
FROM change_request_events events
|
FROM change_request_events events
|
||||||
JOIN change_requests cr ON events.change_request_id = cr.id
|
JOIN change_requests cr ON events.change_request_id = cr.id
|
||||||
WHERE cr.state IN ('Draft', 'In Review', 'Scheduled', 'Approved')
|
WHERE cr.state IN ('Draft', 'In review', 'Scheduled', 'Approved')
|
||||||
AND events.action IN ('updateStrategy', 'addStrategy');`,
|
AND events.action IN ('updateStrategy', 'addStrategy');`,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -27,4 +29,34 @@ export class ChangeRequestSegmentUsageReadModel
|
|||||||
|
|
||||||
return isUsed;
|
return isUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapRow = (row): ChangeRequestStrategy => {
|
||||||
|
const { payload, project, environment, feature } = row;
|
||||||
|
return {
|
||||||
|
projectId: project,
|
||||||
|
featureName: feature,
|
||||||
|
environment: environment,
|
||||||
|
strategyName: payload.name,
|
||||||
|
...(payload.id ? { id: payload.id } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
public async getStrategiesUsedInActiveChangeRequests(
|
||||||
|
segmentId: number,
|
||||||
|
): Promise<ChangeRequestStrategy[]> {
|
||||||
|
const query = this.db.raw(
|
||||||
|
`SELECT events.*, cr.project, cr.environment
|
||||||
|
FROM change_request_events events
|
||||||
|
JOIN change_requests cr ON events.change_request_id = cr.id
|
||||||
|
WHERE cr.state NOT IN ('Applied', 'Cancelled', 'Rejected')
|
||||||
|
AND events.action IN ('updateStrategy', 'addStrategy');`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const queryResult = await query;
|
||||||
|
const strategies = queryResult.rows
|
||||||
|
.filter((row) => row.payload?.segments?.includes(segmentId))
|
||||||
|
.map(this.mapRow);
|
||||||
|
|
||||||
|
return strategies;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ export const segmentStrategiesSchema = {
|
|||||||
},
|
},
|
||||||
featureName: {
|
featureName: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The ID of the strategy',
|
description:
|
||||||
|
'The name of the feature flag that this strategy belongs to.',
|
||||||
example: 'new-signup-flow',
|
example: 'new-signup-flow',
|
||||||
},
|
},
|
||||||
projectId: {
|
projectId: {
|
||||||
|
@ -249,7 +249,7 @@ test('should not delete segments used by strategies in CRs', async () => {
|
|||||||
await db.rawDatabase.table('change_requests').insert({
|
await db.rawDatabase.table('change_requests').insert({
|
||||||
id: CR_ID,
|
id: CR_ID,
|
||||||
environment: 'default',
|
environment: 'default',
|
||||||
state: 'In Review',
|
state: 'In review',
|
||||||
project: 'default',
|
project: 'default',
|
||||||
created_by: user.id,
|
created_by: user.id,
|
||||||
created_at: '2023-01-01 00:00:00',
|
created_at: '2023-01-01 00:00:00',
|
||||||
|
Loading…
Reference in New Issue
Block a user