mirror of
https://github.com/Unleash/unleash.git
synced 2025-11-24 20:06:55 +01:00
chore: remove release plans from getFeature endpoint (#10955)
This commit is contained in:
parent
529726decf
commit
96118836d5
@ -66,8 +66,6 @@ import {
|
|||||||
createFeatureLinkService,
|
createFeatureLinkService,
|
||||||
} from '../feature-links/createFeatureLinkService.js';
|
} from '../feature-links/createFeatureLinkService.js';
|
||||||
import { ResourceLimitsService } from '../resource-limits/resource-limits-service.js';
|
import { ResourceLimitsService } from '../resource-limits/resource-limits-service.js';
|
||||||
import { ReleasePlanReadModel } from '../release-plans/release-plan-read-model.js';
|
|
||||||
import { FakeReleasePlanReadModel } from '../../../test/fixtures/fake/fake-release-plan-read-model.js';
|
|
||||||
|
|
||||||
export const createFeatureToggleService = (
|
export const createFeatureToggleService = (
|
||||||
db: Db,
|
db: Db,
|
||||||
@ -143,8 +141,6 @@ export const createFeatureToggleService = (
|
|||||||
|
|
||||||
const resourceLimitsService = new ResourceLimitsService(config);
|
const resourceLimitsService = new ResourceLimitsService(config);
|
||||||
|
|
||||||
const releasePlanReadModel = new ReleasePlanReadModel(db, eventBus);
|
|
||||||
|
|
||||||
const featureToggleService = new FeatureToggleService(
|
const featureToggleService = new FeatureToggleService(
|
||||||
{
|
{
|
||||||
featureStrategiesStore,
|
featureStrategiesStore,
|
||||||
@ -170,7 +166,6 @@ export const createFeatureToggleService = (
|
|||||||
featureLinksReadModel,
|
featureLinksReadModel,
|
||||||
featureLinkService,
|
featureLinkService,
|
||||||
resourceLimitsService,
|
resourceLimitsService,
|
||||||
releasePlanReadModel,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return featureToggleService;
|
return featureToggleService;
|
||||||
@ -216,7 +211,6 @@ export const createFakeFeatureToggleService = (config: IUnleashConfig) => {
|
|||||||
const { featureLinkService } = createFakeFeatureLinkService(config);
|
const { featureLinkService } = createFakeFeatureLinkService(config);
|
||||||
|
|
||||||
const resourceLimitsService = new ResourceLimitsService(config);
|
const resourceLimitsService = new ResourceLimitsService(config);
|
||||||
const releasePlanReadModel = new FakeReleasePlanReadModel();
|
|
||||||
|
|
||||||
const featureToggleService = new FeatureToggleService(
|
const featureToggleService = new FeatureToggleService(
|
||||||
{
|
{
|
||||||
@ -247,7 +241,6 @@ export const createFakeFeatureToggleService = (config: IUnleashConfig) => {
|
|||||||
featureLinksReadModel,
|
featureLinksReadModel,
|
||||||
featureLinkService,
|
featureLinkService,
|
||||||
resourceLimitsService,
|
resourceLimitsService,
|
||||||
releasePlanReadModel,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import {
|
|||||||
type IAuditUser,
|
type IAuditUser,
|
||||||
type IConstraint,
|
type IConstraint,
|
||||||
type IDependency,
|
type IDependency,
|
||||||
type IEnvironmentDetail,
|
|
||||||
type IFeatureCollaboratorsReadModel,
|
type IFeatureCollaboratorsReadModel,
|
||||||
type IFeatureEnvironmentInfo,
|
type IFeatureEnvironmentInfo,
|
||||||
type IFeatureEnvironmentStore,
|
type IFeatureEnvironmentStore,
|
||||||
@ -118,8 +117,6 @@ import { sortStrategies } from '../../util/sortStrategies.js';
|
|||||||
import type FeatureLinkService from '../feature-links/feature-link-service.js';
|
import type FeatureLinkService from '../feature-links/feature-link-service.js';
|
||||||
import type { IFeatureLink } from '../feature-links/feature-links-read-model-type.js';
|
import type { IFeatureLink } from '../feature-links/feature-links-read-model-type.js';
|
||||||
import type { ResourceLimitsService } from '../resource-limits/resource-limits-service.js';
|
import type { ResourceLimitsService } from '../resource-limits/resource-limits-service.js';
|
||||||
import type { IReleasePlanReadModel } from '../release-plans/release-plan-read-model-type.js';
|
|
||||||
import type { ReleasePlan } from '../release-plans/release-plan.js';
|
|
||||||
interface IFeatureContext {
|
interface IFeatureContext {
|
||||||
featureName: string;
|
featureName: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
@ -180,7 +177,6 @@ export type ServicesAndReadModels = {
|
|||||||
featureLinkService: FeatureLinkService;
|
featureLinkService: FeatureLinkService;
|
||||||
featureLinksReadModel: IFeatureLinksReadModel;
|
featureLinksReadModel: IFeatureLinksReadModel;
|
||||||
resourceLimitsService: ResourceLimitsService;
|
resourceLimitsService: ResourceLimitsService;
|
||||||
releasePlanReadModel: IReleasePlanReadModel;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export class FeatureToggleService {
|
export class FeatureToggleService {
|
||||||
@ -230,8 +226,6 @@ export class FeatureToggleService {
|
|||||||
|
|
||||||
private resourceLimitsService: ResourceLimitsService;
|
private resourceLimitsService: ResourceLimitsService;
|
||||||
|
|
||||||
private releasePlanReadModel: IReleasePlanReadModel;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
featureStrategiesStore,
|
featureStrategiesStore,
|
||||||
@ -257,7 +251,6 @@ export class FeatureToggleService {
|
|||||||
featureLinksReadModel,
|
featureLinksReadModel,
|
||||||
featureLinkService,
|
featureLinkService,
|
||||||
resourceLimitsService,
|
resourceLimitsService,
|
||||||
releasePlanReadModel,
|
|
||||||
}: ServicesAndReadModels,
|
}: ServicesAndReadModels,
|
||||||
) {
|
) {
|
||||||
this.logger = getLogger('services/feature-toggle-service.ts');
|
this.logger = getLogger('services/feature-toggle-service.ts');
|
||||||
@ -283,7 +276,6 @@ export class FeatureToggleService {
|
|||||||
this.featureLinkService = featureLinkService;
|
this.featureLinkService = featureLinkService;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.resourceLimitsService = resourceLimitsService;
|
this.resourceLimitsService = resourceLimitsService;
|
||||||
this.releasePlanReadModel = releasePlanReadModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateFeaturesContext(
|
async validateFeaturesContext(
|
||||||
@ -1160,15 +1152,8 @@ export class FeatureToggleService {
|
|||||||
archived,
|
archived,
|
||||||
);
|
);
|
||||||
|
|
||||||
const environmentsWithReleasePlans =
|
|
||||||
await this.addReleasePlansToEnvironments(
|
|
||||||
featureName,
|
|
||||||
result.environments,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
environments: environmentsWithReleasePlans,
|
|
||||||
dependencies,
|
dependencies,
|
||||||
children,
|
children,
|
||||||
lifecycle,
|
lifecycle,
|
||||||
@ -1187,15 +1172,8 @@ export class FeatureToggleService {
|
|||||||
archived,
|
archived,
|
||||||
);
|
);
|
||||||
|
|
||||||
const environmentsWithReleasePlans =
|
|
||||||
await this.addReleasePlansToEnvironments(
|
|
||||||
featureName,
|
|
||||||
result.environments,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
environments: environmentsWithReleasePlans,
|
|
||||||
dependencies,
|
dependencies,
|
||||||
children,
|
children,
|
||||||
lifecycle,
|
lifecycle,
|
||||||
@ -1205,27 +1183,6 @@ export class FeatureToggleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async addReleasePlansToEnvironments(
|
|
||||||
featureName: string,
|
|
||||||
environments: IEnvironmentDetail[],
|
|
||||||
): Promise<(IEnvironmentDetail & { releasePlans?: ReleasePlan[] })[]> {
|
|
||||||
if (!this.flagResolver.isEnabled('featureReleasePlans')) {
|
|
||||||
return environments;
|
|
||||||
}
|
|
||||||
|
|
||||||
const environmentNames = environments.map((env) => env.name);
|
|
||||||
const releasePlansByEnvironment =
|
|
||||||
await this.releasePlanReadModel.getReleasePlans(
|
|
||||||
featureName,
|
|
||||||
environmentNames,
|
|
||||||
);
|
|
||||||
|
|
||||||
return environments.map((env) => ({
|
|
||||||
...env,
|
|
||||||
releasePlans: releasePlansByEnvironment[env.name] || [],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
async getVariantsForEnv(
|
async getVariantsForEnv(
|
||||||
featureName: string,
|
featureName: string,
|
||||||
environment: string,
|
environment: string,
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
import type { ReleasePlan } from './release-plan.js';
|
|
||||||
|
|
||||||
export interface IReleasePlanReadModel {
|
|
||||||
getReleasePlans(
|
|
||||||
featureName: string,
|
|
||||||
environments: string[],
|
|
||||||
): Promise<Record<string, ReleasePlan[]>>;
|
|
||||||
}
|
|
||||||
@ -1,216 +0,0 @@
|
|||||||
import { ulid } from 'ulidx';
|
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
import dbInit, {
|
|
||||||
type ITestDb,
|
|
||||||
} from '../../../test/e2e/helpers/database-init.js';
|
|
||||||
import getLogger from '../../../test/fixtures/no-logger.js';
|
|
||||||
import { ReleasePlanReadModel } from './release-plan-read-model.js';
|
|
||||||
import type { IReleasePlanReadModel } from './release-plan-read-model-type.js';
|
|
||||||
import type { IFeatureToggleStore } from '../feature-toggle/types/feature-toggle-store-type.js';
|
|
||||||
import type { ReleasePlanStore } from './release-plan-store.js';
|
|
||||||
import type { ReleasePlanMilestoneStore } from './release-plan-milestone-store.js';
|
|
||||||
import type { IFeatureEnvironmentStore } from '../../types/stores/feature-environment-store.js';
|
|
||||||
|
|
||||||
let db: ITestDb;
|
|
||||||
let releasePlanReadModel: IReleasePlanReadModel;
|
|
||||||
let featureToggleStore: IFeatureToggleStore;
|
|
||||||
let releasePlanStore: ReleasePlanStore;
|
|
||||||
let releasePlanMilestoneStore: ReleasePlanMilestoneStore;
|
|
||||||
let featureEnvironmentStore: IFeatureEnvironmentStore;
|
|
||||||
let eventBus: EventEmitter;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
db = await dbInit('release_plan_read_model', getLogger);
|
|
||||||
eventBus = new EventEmitter();
|
|
||||||
releasePlanReadModel = new ReleasePlanReadModel(db.rawDatabase, eventBus);
|
|
||||||
featureToggleStore = db.stores.featureToggleStore;
|
|
||||||
releasePlanStore = db.stores.releasePlanStore;
|
|
||||||
releasePlanMilestoneStore = db.stores.releasePlanMilestoneStore;
|
|
||||||
featureEnvironmentStore = db.stores.featureEnvironmentStore;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
if (db) {
|
|
||||||
await db.destroy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await releasePlanStore.deleteAll();
|
|
||||||
await releasePlanMilestoneStore.deleteAll();
|
|
||||||
await featureToggleStore.deleteAll();
|
|
||||||
await featureEnvironmentStore.deleteAll();
|
|
||||||
|
|
||||||
await db.rawDatabase('milestone_progressions').del();
|
|
||||||
});
|
|
||||||
|
|
||||||
const createReleasePlan = async (planData: {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
featureName: string;
|
|
||||||
environment: string;
|
|
||||||
createdByUserId: number;
|
|
||||||
activeMilestoneId?: string;
|
|
||||||
}) => {
|
|
||||||
const id = ulid();
|
|
||||||
await db.rawDatabase('release_plan_definitions').insert({
|
|
||||||
id,
|
|
||||||
name: planData.name,
|
|
||||||
description: planData.description,
|
|
||||||
feature_name: planData.featureName,
|
|
||||||
environment: planData.environment,
|
|
||||||
created_by_user_id: planData.createdByUserId,
|
|
||||||
active_milestone_id: planData.activeMilestoneId || null,
|
|
||||||
discriminator: 'plan',
|
|
||||||
created_at: new Date(),
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name: planData.name,
|
|
||||||
description: planData.description,
|
|
||||||
featureName: planData.featureName,
|
|
||||||
environment: planData.environment,
|
|
||||||
createdByUserId: planData.createdByUserId,
|
|
||||||
activeMilestoneId: planData.activeMilestoneId || null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMilestone = async (milestoneData: {
|
|
||||||
name: string;
|
|
||||||
sortOrder: number;
|
|
||||||
releasePlanDefinitionId: string;
|
|
||||||
startedAt?: Date;
|
|
||||||
}) => {
|
|
||||||
const milestone = await releasePlanMilestoneStore.insert({
|
|
||||||
name: milestoneData.name,
|
|
||||||
sortOrder: milestoneData.sortOrder,
|
|
||||||
releasePlanDefinitionId: milestoneData.releasePlanDefinitionId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (milestoneData.startedAt) {
|
|
||||||
await db
|
|
||||||
.rawDatabase('milestones')
|
|
||||||
.where('id', milestone.id)
|
|
||||||
.update('started_at', milestoneData.startedAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: milestone.id,
|
|
||||||
name: milestone.name,
|
|
||||||
sortOrder: milestone.sortOrder,
|
|
||||||
releasePlanDefinitionId: milestone.releasePlanDefinitionId,
|
|
||||||
startedAt: milestoneData.startedAt || null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMilestoneProgression = async (progressionData: {
|
|
||||||
sourceMilestoneId: string;
|
|
||||||
targetMilestoneId: string;
|
|
||||||
transitionCondition?: object;
|
|
||||||
executedAt?: Date;
|
|
||||||
}) => {
|
|
||||||
await db.rawDatabase('milestone_progressions').insert({
|
|
||||||
source_milestone: progressionData.sourceMilestoneId,
|
|
||||||
target_milestone: progressionData.targetMilestoneId,
|
|
||||||
transition_condition: progressionData.transitionCondition || null,
|
|
||||||
executed_at: progressionData.executedAt || null,
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
transitionCondition: progressionData.transitionCondition || null,
|
|
||||||
executedAt: progressionData.executedAt || null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
test('should return release plans with complete milestone data', async () => {
|
|
||||||
await featureToggleStore.create('default', {
|
|
||||||
name: 'test-feature',
|
|
||||||
createdByUserId: 1,
|
|
||||||
});
|
|
||||||
await featureEnvironmentStore.addEnvironmentToFeature(
|
|
||||||
'test-feature',
|
|
||||||
'development',
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
const plan = await createReleasePlan({
|
|
||||||
name: 'Test Plan',
|
|
||||||
description: 'Test plan',
|
|
||||||
featureName: 'test-feature',
|
|
||||||
environment: 'development',
|
|
||||||
createdByUserId: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const startedAt = new Date('2024-01-10T08:00:00.000Z');
|
|
||||||
const milestone1 = await createMilestone({
|
|
||||||
name: 'Milestone 1',
|
|
||||||
sortOrder: 1,
|
|
||||||
releasePlanDefinitionId: plan.id,
|
|
||||||
startedAt: startedAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
const milestone2 = await createMilestone({
|
|
||||||
name: 'Milestone 2',
|
|
||||||
sortOrder: 2,
|
|
||||||
releasePlanDefinitionId: plan.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await releasePlanStore.update(plan.id, {
|
|
||||||
activeMilestoneId: milestone1.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const transitionCondition = { intervalMinutes: 60 };
|
|
||||||
const executedAt = new Date('2024-01-15T10:00:00.000Z');
|
|
||||||
const milestoneProgression = await createMilestoneProgression({
|
|
||||||
sourceMilestoneId: milestone1.id,
|
|
||||||
targetMilestoneId: milestone2.id,
|
|
||||||
transitionCondition: transitionCondition,
|
|
||||||
executedAt: executedAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
const releasePlans = await releasePlanReadModel.getReleasePlans(
|
|
||||||
'test-feature',
|
|
||||||
['development'],
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(releasePlans).toMatchObject({
|
|
||||||
development: [
|
|
||||||
{
|
|
||||||
id: plan.id,
|
|
||||||
discriminator: 'plan',
|
|
||||||
name: plan.name,
|
|
||||||
description: plan.description,
|
|
||||||
featureName: plan.featureName,
|
|
||||||
environment: plan.environment,
|
|
||||||
createdByUserId: plan.createdByUserId,
|
|
||||||
createdAt: expect.any(Date),
|
|
||||||
activeMilestoneId: milestone1.id,
|
|
||||||
releasePlanTemplateId: null,
|
|
||||||
milestones: [
|
|
||||||
{
|
|
||||||
id: milestone1.id,
|
|
||||||
name: milestone1.name,
|
|
||||||
sortOrder: milestone1.sortOrder,
|
|
||||||
releasePlanDefinitionId:
|
|
||||||
milestone1.releasePlanDefinitionId,
|
|
||||||
startedAt: milestone1.startedAt,
|
|
||||||
progressionExecutedAt: milestoneProgression.executedAt,
|
|
||||||
transitionCondition:
|
|
||||||
milestoneProgression.transitionCondition,
|
|
||||||
strategies: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: milestone2.id,
|
|
||||||
name: milestone2.name,
|
|
||||||
sortOrder: milestone2.sortOrder,
|
|
||||||
releasePlanDefinitionId:
|
|
||||||
milestone2.releasePlanDefinitionId,
|
|
||||||
startedAt: milestone2.startedAt,
|
|
||||||
progressionExecutedAt: null,
|
|
||||||
transitionCondition: null,
|
|
||||||
strategies: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,210 +0,0 @@
|
|||||||
import type { Db } from '../../db/db.js';
|
|
||||||
import type { IReleasePlanReadModel } from './release-plan-read-model-type.js';
|
|
||||||
import type { ReleasePlan } from './release-plan.js';
|
|
||||||
import metricsHelper from '../../util/metrics-helper.js';
|
|
||||||
import type EventEmitter from 'events';
|
|
||||||
|
|
||||||
const TABLE = 'release_plan_definitions';
|
|
||||||
|
|
||||||
const selectColumns = [
|
|
||||||
'rpd.id AS planId',
|
|
||||||
'rpd.discriminator AS planDiscriminator',
|
|
||||||
'rpd.name AS planName',
|
|
||||||
'rpd.description as planDescription',
|
|
||||||
'rpd.feature_name as planFeatureName',
|
|
||||||
'rpd.environment as planEnvironment',
|
|
||||||
'rpd.created_by_user_id as planCreatedByUserId',
|
|
||||||
'rpd.created_at as planCreatedAt',
|
|
||||||
'rpd.active_milestone_id as planActiveMilestoneId',
|
|
||||||
'rpd.release_plan_template_id as planTemplateId',
|
|
||||||
'mi.id AS milestoneId',
|
|
||||||
'mi.name AS milestoneName',
|
|
||||||
'mi.sort_order AS milestoneSortOrder',
|
|
||||||
'mi.started_at AS milestoneStartedAt',
|
|
||||||
'mp.transition_condition AS milestoneTransitionCondition',
|
|
||||||
'mp.executed_at AS milestoneProgressionExecutedAt',
|
|
||||||
'ms.id AS strategyId',
|
|
||||||
'ms.sort_order AS strategySortOrder',
|
|
||||||
'ms.title AS strategyTitle',
|
|
||||||
'ms.strategy_name AS strategyName',
|
|
||||||
'ms.parameters AS strategyParameters',
|
|
||||||
'ms.constraints AS strategyConstraints',
|
|
||||||
'ms.variants AS strategyVariants',
|
|
||||||
'mss.segment_id AS segmentId',
|
|
||||||
];
|
|
||||||
|
|
||||||
const processReleasePlanRows = (templateRows): ReleasePlan[] =>
|
|
||||||
templateRows.reduce(
|
|
||||||
(
|
|
||||||
acc: ReleasePlan[],
|
|
||||||
{
|
|
||||||
planId,
|
|
||||||
planDiscriminator,
|
|
||||||
planName,
|
|
||||||
planDescription,
|
|
||||||
planFeatureName,
|
|
||||||
planEnvironment,
|
|
||||||
planCreatedByUserId,
|
|
||||||
planCreatedAt,
|
|
||||||
planActiveMilestoneId,
|
|
||||||
planTemplateId,
|
|
||||||
milestoneId,
|
|
||||||
milestoneName,
|
|
||||||
milestoneSortOrder,
|
|
||||||
milestoneStartedAt,
|
|
||||||
milestoneTransitionCondition,
|
|
||||||
milestoneProgressionExecutedAt,
|
|
||||||
strategyId,
|
|
||||||
strategySortOrder,
|
|
||||||
strategyTitle,
|
|
||||||
strategyName,
|
|
||||||
strategyParameters,
|
|
||||||
strategyConstraints,
|
|
||||||
strategyVariants,
|
|
||||||
segmentId,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let plan = acc.find(({ id }) => id === planId);
|
|
||||||
|
|
||||||
if (!plan) {
|
|
||||||
plan = {
|
|
||||||
id: planId,
|
|
||||||
discriminator: planDiscriminator,
|
|
||||||
name: planName,
|
|
||||||
description: planDescription,
|
|
||||||
featureName: planFeatureName,
|
|
||||||
environment: planEnvironment,
|
|
||||||
createdByUserId: planCreatedByUserId,
|
|
||||||
createdAt: planCreatedAt,
|
|
||||||
activeMilestoneId: planActiveMilestoneId,
|
|
||||||
releasePlanTemplateId: planTemplateId,
|
|
||||||
milestones: [],
|
|
||||||
};
|
|
||||||
acc.push(plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!milestoneId) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
let milestone = plan.milestones.find(
|
|
||||||
({ id }) => id === milestoneId,
|
|
||||||
);
|
|
||||||
if (!milestone) {
|
|
||||||
milestone = {
|
|
||||||
id: milestoneId,
|
|
||||||
name: milestoneName,
|
|
||||||
sortOrder: milestoneSortOrder,
|
|
||||||
startedAt: milestoneStartedAt,
|
|
||||||
transitionCondition: milestoneTransitionCondition,
|
|
||||||
progressionExecutedAt: milestoneProgressionExecutedAt,
|
|
||||||
strategies: [],
|
|
||||||
releasePlanDefinitionId: planId,
|
|
||||||
};
|
|
||||||
plan.milestones.push(milestone);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strategyId) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
let strategy = milestone.strategies?.find(
|
|
||||||
({ id }) => id === strategyId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!strategy) {
|
|
||||||
strategy = {
|
|
||||||
id: strategyId,
|
|
||||||
milestoneId: milestoneId,
|
|
||||||
sortOrder: strategySortOrder,
|
|
||||||
title: strategyTitle,
|
|
||||||
strategyName: strategyName,
|
|
||||||
parameters: strategyParameters ?? {},
|
|
||||||
constraints: strategyConstraints,
|
|
||||||
variants: strategyVariants ?? [],
|
|
||||||
segments: [],
|
|
||||||
};
|
|
||||||
milestone.strategies = [
|
|
||||||
...(milestone.strategies || []),
|
|
||||||
strategy,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (segmentId) {
|
|
||||||
strategy.segments = [...(strategy.segments || []), segmentId];
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
export class ReleasePlanReadModel implements IReleasePlanReadModel {
|
|
||||||
private db: Db;
|
|
||||||
|
|
||||||
private timer: Function;
|
|
||||||
|
|
||||||
constructor(db: Db, eventBus: EventEmitter) {
|
|
||||||
this.db = db;
|
|
||||||
this.timer = (action: string) =>
|
|
||||||
metricsHelper.wrapTimer(eventBus, 'db_time', {
|
|
||||||
store: 'release-plan-read-model',
|
|
||||||
action,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getReleasePlans(
|
|
||||||
featureName: string,
|
|
||||||
environments: string[],
|
|
||||||
): Promise<Record<string, ReleasePlan[]>> {
|
|
||||||
const endTimer = this.timer('getReleasePlans');
|
|
||||||
if (environments.length === 0) {
|
|
||||||
endTimer();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const planRows = await this.db(`${TABLE} AS rpd`)
|
|
||||||
.where('rpd.discriminator', 'plan')
|
|
||||||
.andWhere('rpd.feature_name', featureName)
|
|
||||||
.whereIn('rpd.environment', environments)
|
|
||||||
.leftJoin(
|
|
||||||
'milestones AS mi',
|
|
||||||
'mi.release_plan_definition_id',
|
|
||||||
'rpd.id',
|
|
||||||
)
|
|
||||||
.leftJoin(
|
|
||||||
'milestone_progressions AS mp',
|
|
||||||
'mp.source_milestone',
|
|
||||||
'mi.id',
|
|
||||||
)
|
|
||||||
.leftJoin('milestone_strategies AS ms', 'ms.milestone_id', 'mi.id')
|
|
||||||
.leftJoin(
|
|
||||||
'milestone_strategy_segments AS mss',
|
|
||||||
'mss.milestone_strategy_id',
|
|
||||||
'ms.id',
|
|
||||||
)
|
|
||||||
.orderBy('rpd.environment', 'asc')
|
|
||||||
.orderBy('mi.sort_order', 'asc')
|
|
||||||
.orderBy('ms.sort_order', 'asc')
|
|
||||||
.select(selectColumns);
|
|
||||||
|
|
||||||
const allPlans = processReleasePlanRows(planRows);
|
|
||||||
|
|
||||||
const plansByEnvironment: Record<string, ReleasePlan[]> = {};
|
|
||||||
for (const plan of allPlans) {
|
|
||||||
if (!plansByEnvironment[plan.environment]) {
|
|
||||||
plansByEnvironment[plan.environment] = [];
|
|
||||||
}
|
|
||||||
plansByEnvironment[plan.environment].push(plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const env of environments) {
|
|
||||||
if (!plansByEnvironment[env]) {
|
|
||||||
plansByEnvironment[env] = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
endTimer();
|
|
||||||
return plansByEnvironment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -188,8 +188,6 @@ import type { IClientInstance } from './types/stores/client-instance-store.js';
|
|||||||
import EnvironmentStore from './features/project-environments/environment-store.js';
|
import EnvironmentStore from './features/project-environments/environment-store.js';
|
||||||
import ProjectStore from './features/project/project-store.js';
|
import ProjectStore from './features/project/project-store.js';
|
||||||
import type { ReleasePlanMilestoneWriteModel } from './features/release-plans/release-plan-milestone.js';
|
import type { ReleasePlanMilestoneWriteModel } from './features/release-plans/release-plan-milestone.js';
|
||||||
import type { IReleasePlanReadModel } from './features/release-plans/release-plan-read-model-type.js';
|
|
||||||
import { ReleasePlanReadModel } from './features/release-plans/release-plan-read-model.js';
|
|
||||||
import { FakeChangeRequestAccessReadModel } from './features/change-request-access-service/fake-change-request-access-read-model.js';
|
import { FakeChangeRequestAccessReadModel } from './features/change-request-access-service/fake-change-request-access-read-model.js';
|
||||||
import { fakeImpactMetricsResolver } from '../test/fixtures/fake-impact-metrics.js';
|
import { fakeImpactMetricsResolver } from '../test/fixtures/fake-impact-metrics.js';
|
||||||
|
|
||||||
@ -505,7 +503,6 @@ export {
|
|||||||
impactRegister,
|
impactRegister,
|
||||||
EnvironmentStore,
|
EnvironmentStore,
|
||||||
ProjectStore,
|
ProjectStore,
|
||||||
ReleasePlanReadModel,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
@ -547,7 +544,6 @@ export type {
|
|||||||
ReleasePlanMilestoneWriteModel,
|
ReleasePlanMilestoneWriteModel,
|
||||||
ReleasePlanMilestoneStrategyWriteModel,
|
ReleasePlanMilestoneStrategyWriteModel,
|
||||||
IChangeRequestAccessReadModel,
|
IChangeRequestAccessReadModel,
|
||||||
IReleasePlanReadModel,
|
|
||||||
IRoleWithProject,
|
IRoleWithProject,
|
||||||
ISchemaValidationErrors,
|
ISchemaValidationErrors,
|
||||||
IImportService,
|
IImportService,
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
import type { IReleasePlanReadModel } from '../../../lib/features/release-plans/release-plan-read-model-type.js';
|
|
||||||
import type { ReleasePlan } from '../../../lib/features/release-plans/release-plan.js';
|
|
||||||
|
|
||||||
export class FakeReleasePlanReadModel implements IReleasePlanReadModel {
|
|
||||||
private releasePlans: Record<string, ReleasePlan[]>;
|
|
||||||
|
|
||||||
constructor(releasePlans: Record<string, ReleasePlan[]> = {}) {
|
|
||||||
this.releasePlans = releasePlans;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getReleasePlans(
|
|
||||||
_featureName: string,
|
|
||||||
_environments: string[],
|
|
||||||
): Promise<Record<string, ReleasePlan[]>> {
|
|
||||||
return this.releasePlans;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user