mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: move release plans to feature environments (#10746)
This commit is contained in:
		
							parent
							
								
									183d436e59
								
							
						
					
					
						commit
						f2115cc3db
					
				| @ -66,6 +66,8 @@ 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, | ||||||
| @ -141,6 +143,8 @@ export const createFeatureToggleService = ( | |||||||
| 
 | 
 | ||||||
|     const resourceLimitsService = new ResourceLimitsService(config); |     const resourceLimitsService = new ResourceLimitsService(config); | ||||||
| 
 | 
 | ||||||
|  |     const releasePlanReadModel = new ReleasePlanReadModel(db); | ||||||
|  | 
 | ||||||
|     const featureToggleService = new FeatureToggleService( |     const featureToggleService = new FeatureToggleService( | ||||||
|         { |         { | ||||||
|             featureStrategiesStore, |             featureStrategiesStore, | ||||||
| @ -166,6 +170,7 @@ export const createFeatureToggleService = ( | |||||||
|             featureLinksReadModel, |             featureLinksReadModel, | ||||||
|             featureLinkService, |             featureLinkService, | ||||||
|             resourceLimitsService, |             resourceLimitsService, | ||||||
|  |             releasePlanReadModel, | ||||||
|         }, |         }, | ||||||
|     ); |     ); | ||||||
|     return featureToggleService; |     return featureToggleService; | ||||||
| @ -211,6 +216,7 @@ 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( | ||||||
|         { |         { | ||||||
| @ -241,6 +247,7 @@ export const createFakeFeatureToggleService = (config: IUnleashConfig) => { | |||||||
|             featureLinksReadModel, |             featureLinksReadModel, | ||||||
|             featureLinkService, |             featureLinkService, | ||||||
|             resourceLimitsService, |             resourceLimitsService, | ||||||
|  |             releasePlanReadModel, | ||||||
|         }, |         }, | ||||||
|     ); |     ); | ||||||
|     return { |     return { | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ 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, | ||||||
| @ -117,6 +118,8 @@ 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; | ||||||
| @ -177,6 +180,7 @@ export type ServicesAndReadModels = { | |||||||
|     featureLinkService: FeatureLinkService; |     featureLinkService: FeatureLinkService; | ||||||
|     featureLinksReadModel: IFeatureLinksReadModel; |     featureLinksReadModel: IFeatureLinksReadModel; | ||||||
|     resourceLimitsService: ResourceLimitsService; |     resourceLimitsService: ResourceLimitsService; | ||||||
|  |     releasePlanReadModel: IReleasePlanReadModel; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export class FeatureToggleService { | export class FeatureToggleService { | ||||||
| @ -226,6 +230,8 @@ export class FeatureToggleService { | |||||||
| 
 | 
 | ||||||
|     private resourceLimitsService: ResourceLimitsService; |     private resourceLimitsService: ResourceLimitsService; | ||||||
| 
 | 
 | ||||||
|  |     private releasePlanReadModel: IReleasePlanReadModel; | ||||||
|  | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         { |         { | ||||||
|             featureStrategiesStore, |             featureStrategiesStore, | ||||||
| @ -251,6 +257,7 @@ 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'); | ||||||
| @ -276,6 +283,7 @@ 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( | ||||||
| @ -1151,8 +1159,16 @@ export class FeatureToggleService { | |||||||
|                     userId, |                     userId, | ||||||
|                     archived, |                     archived, | ||||||
|                 ); |                 ); | ||||||
|  | 
 | ||||||
|  |             const environmentsWithReleasePlans = | ||||||
|  |                 await this.addReleasePlansToEnvironments( | ||||||
|  |                     featureName, | ||||||
|  |                     result.environments, | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|             return { |             return { | ||||||
|                 ...result, |                 ...result, | ||||||
|  |                 environments: environmentsWithReleasePlans, | ||||||
|                 dependencies, |                 dependencies, | ||||||
|                 children, |                 children, | ||||||
|                 lifecycle, |                 lifecycle, | ||||||
| @ -1171,8 +1187,15 @@ 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, | ||||||
| @ -1182,6 +1205,27 @@ export class FeatureToggleService { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private async addReleasePlansToEnvironments( | ||||||
|  |         featureName: string, | ||||||
|  |         environments: IEnvironmentDetail[], | ||||||
|  |     ): Promise<(IEnvironmentDetail & { releasePlans?: ReleasePlan[] })[]> { | ||||||
|  |         if (!this.flagResolver.isEnabled('milestoneProgression')) { | ||||||
|  |             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, | ||||||
|  | |||||||
| @ -0,0 +1,8 @@ | |||||||
|  | import type { ReleasePlan } from './release-plan.js'; | ||||||
|  | 
 | ||||||
|  | export interface IReleasePlanReadModel { | ||||||
|  |     getReleasePlans( | ||||||
|  |         featureName: string, | ||||||
|  |         environments: string[], | ||||||
|  |     ): Promise<Record<string, ReleasePlan[]>>; | ||||||
|  | } | ||||||
							
								
								
									
										184
									
								
								src/lib/features/release-plans/release-plan-read-model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								src/lib/features/release-plans/release-plan-read-model.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,184 @@ | |||||||
|  | import type { Db } from '../../db/db.js'; | ||||||
|  | import type { IReleasePlanReadModel } from './release-plan-read-model-type.js'; | ||||||
|  | import type { ReleasePlan } from './release-plan.js'; | ||||||
|  | 
 | ||||||
|  | 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', | ||||||
|  |     '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, | ||||||
|  |                 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, | ||||||
|  |                     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; | ||||||
|  | 
 | ||||||
|  |     constructor(db: Db) { | ||||||
|  |         this.db = db; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async getReleasePlans( | ||||||
|  |         featureName: string, | ||||||
|  |         environments: string[], | ||||||
|  |     ): Promise<Record<string, ReleasePlan[]>> { | ||||||
|  |         if (environments.length === 0) { | ||||||
|  |             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_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] = []; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return plansByEnvironment; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -252,6 +252,10 @@ export class ReleasePlanStore extends CRUDStore< | |||||||
|         } |         } | ||||||
|         return releasePlans[0]; |         return releasePlans[0]; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     // should be deprecated soon in favor of IReleasePlanReadModel.getReleasePlans
 | ||||||
|  |     // this will remove read model responsibility from the store
 | ||||||
|  |     // also we're moving release plans to feature environments (DB join) instead of separate API calls (browser join)
 | ||||||
|     async getByFeatureFlagAndEnvironment( |     async getByFeatureFlagAndEnvironment( | ||||||
|         featureName: string, |         featureName: string, | ||||||
|         environment: string, |         environment: string, | ||||||
|  | |||||||
| @ -13,6 +13,11 @@ import { constraintSchema } from './constraint-schema.js'; | |||||||
| import { tagTypeSchema } from './tag-type-schema.js'; | import { tagTypeSchema } from './tag-type-schema.js'; | ||||||
| import { strategyVariantSchema } from './strategy-variant-schema.js'; | import { strategyVariantSchema } from './strategy-variant-schema.js'; | ||||||
| import { featureDependenciesSchema } from './feature-dependencies-schema.js'; | import { featureDependenciesSchema } from './feature-dependencies-schema.js'; | ||||||
|  | import { releasePlanSchema } from './release-plan-schema.js'; | ||||||
|  | import { releasePlanMilestoneSchema } from './release-plan-milestone-schema.js'; | ||||||
|  | import { releasePlanMilestoneStrategySchema } from './release-plan-milestone-strategy-schema.js'; | ||||||
|  | import { createFeatureStrategySchema } from './create-feature-strategy-schema.js'; | ||||||
|  | import { createStrategyVariantSchema } from './create-strategy-variant-schema.js'; | ||||||
| import { dependentFeatureSchema } from './dependent-feature-schema.js'; | import { dependentFeatureSchema } from './dependent-feature-schema.js'; | ||||||
| import { tagSchema } from './tag-schema.js'; | import { tagSchema } from './tag-schema.js'; | ||||||
| import { featureLinksSchema } from './feature-links-schema.js'; | import { featureLinksSchema } from './feature-links-schema.js'; | ||||||
| @ -207,6 +212,11 @@ export const exportResultSchema = { | |||||||
|             tagSchema, |             tagSchema, | ||||||
|             featureLinksSchema, |             featureLinksSchema, | ||||||
|             featureLinkSchema, |             featureLinkSchema, | ||||||
|  |             releasePlanSchema, | ||||||
|  |             releasePlanMilestoneSchema, | ||||||
|  |             releasePlanMilestoneStrategySchema, | ||||||
|  |             createFeatureStrategySchema, | ||||||
|  |             createStrategyVariantSchema, | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| } as const; | } as const; | ||||||
|  | |||||||
| @ -4,6 +4,11 @@ import { parametersSchema } from './parameters-schema.js'; | |||||||
| import { featureStrategySchema } from './feature-strategy-schema.js'; | import { featureStrategySchema } from './feature-strategy-schema.js'; | ||||||
| import { variantSchema } from './variant-schema.js'; | import { variantSchema } from './variant-schema.js'; | ||||||
| import { strategyVariantSchema } from './strategy-variant-schema.js'; | import { strategyVariantSchema } from './strategy-variant-schema.js'; | ||||||
|  | import { releasePlanSchema } from './release-plan-schema.js'; | ||||||
|  | import { releasePlanMilestoneSchema } from './release-plan-milestone-schema.js'; | ||||||
|  | import { releasePlanMilestoneStrategySchema } from './release-plan-milestone-strategy-schema.js'; | ||||||
|  | import { createFeatureStrategySchema } from './create-feature-strategy-schema.js'; | ||||||
|  | import { createStrategyVariantSchema } from './create-strategy-variant-schema.js'; | ||||||
| 
 | 
 | ||||||
| export const featureEnvironmentSchema = { | export const featureEnvironmentSchema = { | ||||||
|     $id: '#/components/schemas/featureEnvironmentSchema', |     $id: '#/components/schemas/featureEnvironmentSchema', | ||||||
| @ -106,6 +111,14 @@ export const featureEnvironmentSchema = { | |||||||
|             description: |             description: | ||||||
|                 'Whether the feature has any enabled strategies defined.', |                 'Whether the feature has any enabled strategies defined.', | ||||||
|         }, |         }, | ||||||
|  |         releasePlans: { | ||||||
|  |             type: 'array', | ||||||
|  |             description: | ||||||
|  |                 'Release plans for this feature environment (only available when milestoneProgression feature flag is enabled)', | ||||||
|  |             items: { | ||||||
|  |                 $ref: '#/components/schemas/releasePlanSchema', | ||||||
|  |             }, | ||||||
|  |         }, | ||||||
|     }, |     }, | ||||||
|     components: { |     components: { | ||||||
|         schemas: { |         schemas: { | ||||||
| @ -114,6 +127,11 @@ export const featureEnvironmentSchema = { | |||||||
|             featureStrategySchema, |             featureStrategySchema, | ||||||
|             strategyVariantSchema, |             strategyVariantSchema, | ||||||
|             variantSchema, |             variantSchema, | ||||||
|  |             releasePlanSchema, | ||||||
|  |             releasePlanMilestoneSchema, | ||||||
|  |             releasePlanMilestoneStrategySchema, | ||||||
|  |             createFeatureStrategySchema, | ||||||
|  |             createStrategyVariantSchema, | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| } as const; | } as const; | ||||||
|  | |||||||
| @ -7,6 +7,11 @@ import { featureStrategySchema } from './feature-strategy-schema.js'; | |||||||
| import { tagSchema } from './tag-schema.js'; | import { tagSchema } from './tag-schema.js'; | ||||||
| import { featureEnvironmentSchema } from './feature-environment-schema.js'; | import { featureEnvironmentSchema } from './feature-environment-schema.js'; | ||||||
| import { strategyVariantSchema } from './strategy-variant-schema.js'; | import { strategyVariantSchema } from './strategy-variant-schema.js'; | ||||||
|  | import { releasePlanSchema } from './release-plan-schema.js'; | ||||||
|  | import { releasePlanMilestoneSchema } from './release-plan-milestone-schema.js'; | ||||||
|  | import { releasePlanMilestoneStrategySchema } from './release-plan-milestone-strategy-schema.js'; | ||||||
|  | import { createFeatureStrategySchema } from './create-feature-strategy-schema.js'; | ||||||
|  | import { createStrategyVariantSchema } from './create-strategy-variant-schema.js'; | ||||||
| 
 | 
 | ||||||
| export const featureSchema = { | export const featureSchema = { | ||||||
|     $id: '#/components/schemas/featureSchema', |     $id: '#/components/schemas/featureSchema', | ||||||
| @ -288,6 +293,11 @@ export const featureSchema = { | |||||||
|             parametersSchema, |             parametersSchema, | ||||||
|             variantSchema, |             variantSchema, | ||||||
|             tagSchema, |             tagSchema, | ||||||
|  |             releasePlanSchema, | ||||||
|  |             releasePlanMilestoneSchema, | ||||||
|  |             releasePlanMilestoneStrategySchema, | ||||||
|  |             createFeatureStrategySchema, | ||||||
|  |             createStrategyVariantSchema, | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| } as const; | } as const; | ||||||
|  | |||||||
| @ -5,6 +5,11 @@ import { featureStrategySchema } from './feature-strategy-schema.js'; | |||||||
| import { variantSchema } from './variant-schema.js'; | import { variantSchema } from './variant-schema.js'; | ||||||
| import { strategyVariantSchema } from './strategy-variant-schema.js'; | import { strategyVariantSchema } from './strategy-variant-schema.js'; | ||||||
| import { featureEnvironmentSchema } from './feature-environment-schema.js'; | import { featureEnvironmentSchema } from './feature-environment-schema.js'; | ||||||
|  | import { releasePlanSchema } from './release-plan-schema.js'; | ||||||
|  | import { releasePlanMilestoneSchema } from './release-plan-milestone-schema.js'; | ||||||
|  | import { releasePlanMilestoneStrategySchema } from './release-plan-milestone-strategy-schema.js'; | ||||||
|  | import { createFeatureStrategySchema } from './create-feature-strategy-schema.js'; | ||||||
|  | import { createStrategyVariantSchema } from './create-strategy-variant-schema.js'; | ||||||
| 
 | 
 | ||||||
| export const featureSearchEnvironmentSchema = { | export const featureSearchEnvironmentSchema = { | ||||||
|     $id: '#/components/schemas/featureSearchEnvironmentSchema', |     $id: '#/components/schemas/featureSearchEnvironmentSchema', | ||||||
| @ -37,6 +42,11 @@ export const featureSearchEnvironmentSchema = { | |||||||
|             strategyVariantSchema, |             strategyVariantSchema, | ||||||
|             featureEnvironmentSchema, |             featureEnvironmentSchema, | ||||||
|             variantSchema, |             variantSchema, | ||||||
|  |             releasePlanSchema, | ||||||
|  |             releasePlanMilestoneSchema, | ||||||
|  |             releasePlanMilestoneStrategySchema, | ||||||
|  |             createFeatureStrategySchema, | ||||||
|  |             createStrategyVariantSchema, | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| } as const; | } as const; | ||||||
|  | |||||||
| @ -10,6 +10,9 @@ import { featureEnvironmentSchema } from './feature-environment-schema.js'; | |||||||
| import { projectStatsSchema } from './project-stats-schema.js'; | import { projectStatsSchema } from './project-stats-schema.js'; | ||||||
| import { createFeatureStrategySchema } from './create-feature-strategy-schema.js'; | import { createFeatureStrategySchema } from './create-feature-strategy-schema.js'; | ||||||
| import { projectEnvironmentSchema } from './project-environment-schema.js'; | import { projectEnvironmentSchema } from './project-environment-schema.js'; | ||||||
|  | import { releasePlanSchema } from './release-plan-schema.js'; | ||||||
|  | import { releasePlanMilestoneSchema } from './release-plan-milestone-schema.js'; | ||||||
|  | import { releasePlanMilestoneStrategySchema } from './release-plan-milestone-strategy-schema.js'; | ||||||
| import { createStrategyVariantSchema } from './create-strategy-variant-schema.js'; | import { createStrategyVariantSchema } from './create-strategy-variant-schema.js'; | ||||||
| import { strategyVariantSchema } from './strategy-variant-schema.js'; | import { strategyVariantSchema } from './strategy-variant-schema.js'; | ||||||
| import { createFeatureNamingPatternSchema } from './create-feature-naming-pattern-schema.js'; | import { createFeatureNamingPatternSchema } from './create-feature-naming-pattern-schema.js'; | ||||||
| @ -144,6 +147,9 @@ export const healthOverviewSchema = { | |||||||
|             overrideSchema, |             overrideSchema, | ||||||
|             parametersSchema, |             parametersSchema, | ||||||
|             featureStrategySchema, |             featureStrategySchema, | ||||||
|  |             releasePlanSchema, | ||||||
|  |             releasePlanMilestoneSchema, | ||||||
|  |             releasePlanMilestoneStrategySchema, | ||||||
|             strategyVariantSchema, |             strategyVariantSchema, | ||||||
|             variantSchema, |             variantSchema, | ||||||
|             projectStatsSchema, |             projectStatsSchema, | ||||||
|  | |||||||
| @ -13,6 +13,11 @@ import { parametersSchema } from './parameters-schema.js'; | |||||||
| import { legalValueSchema } from './legal-value-schema.js'; | import { legalValueSchema } from './legal-value-schema.js'; | ||||||
| import { tagTypeSchema } from './tag-type-schema.js'; | import { tagTypeSchema } from './tag-type-schema.js'; | ||||||
| import { featureEnvironmentSchema } from './feature-environment-schema.js'; | import { featureEnvironmentSchema } from './feature-environment-schema.js'; | ||||||
|  | import { releasePlanSchema } from './release-plan-schema.js'; | ||||||
|  | import { releasePlanMilestoneSchema } from './release-plan-milestone-schema.js'; | ||||||
|  | import { releasePlanMilestoneStrategySchema } from './release-plan-milestone-strategy-schema.js'; | ||||||
|  | import { createFeatureStrategySchema } from './create-feature-strategy-schema.js'; | ||||||
|  | import { createStrategyVariantSchema } from './create-strategy-variant-schema.js'; | ||||||
| import { strategyVariantSchema } from './strategy-variant-schema.js'; | import { strategyVariantSchema } from './strategy-variant-schema.js'; | ||||||
| import { featureDependenciesSchema } from './feature-dependencies-schema.js'; | import { featureDependenciesSchema } from './feature-dependencies-schema.js'; | ||||||
| import { dependentFeatureSchema } from './dependent-feature-schema.js'; | import { dependentFeatureSchema } from './dependent-feature-schema.js'; | ||||||
| @ -53,6 +58,11 @@ export const importTogglesSchema = { | |||||||
|             contextFieldSchema, |             contextFieldSchema, | ||||||
|             featureTagSchema, |             featureTagSchema, | ||||||
|             segmentSchema, |             segmentSchema, | ||||||
|  |             releasePlanSchema, | ||||||
|  |             releasePlanMilestoneSchema, | ||||||
|  |             releasePlanMilestoneStrategySchema, | ||||||
|  |             createFeatureStrategySchema, | ||||||
|  |             createStrategyVariantSchema, | ||||||
|             variantsSchema, |             variantsSchema, | ||||||
|             variantSchema, |             variantSchema, | ||||||
|             overrideSchema, |             overrideSchema, | ||||||
|  | |||||||
| @ -15,6 +15,9 @@ import { strategyVariantSchema } from './strategy-variant-schema.js'; | |||||||
| import { createFeatureNamingPatternSchema } from './create-feature-naming-pattern-schema.js'; | import { createFeatureNamingPatternSchema } from './create-feature-naming-pattern-schema.js'; | ||||||
| import { featureTypeCountSchema } from './feature-type-count-schema.js'; | import { featureTypeCountSchema } from './feature-type-count-schema.js'; | ||||||
| import { projectLinkTemplateSchema } from './project-link-template-schema.js'; | import { projectLinkTemplateSchema } from './project-link-template-schema.js'; | ||||||
|  | import { releasePlanSchema } from './release-plan-schema.js'; | ||||||
|  | import { releasePlanMilestoneSchema } from './release-plan-milestone-schema.js'; | ||||||
|  | import { releasePlanMilestoneStrategySchema } from './release-plan-milestone-strategy-schema.js'; | ||||||
| 
 | 
 | ||||||
| export const projectOverviewSchema = { | export const projectOverviewSchema = { | ||||||
|     $id: '#/components/schemas/projectOverviewSchema', |     $id: '#/components/schemas/projectOverviewSchema', | ||||||
| @ -202,6 +205,9 @@ export const projectOverviewSchema = { | |||||||
|             featureStrategySchema, |             featureStrategySchema, | ||||||
|             strategyVariantSchema, |             strategyVariantSchema, | ||||||
|             variantSchema, |             variantSchema, | ||||||
|  |             releasePlanSchema, | ||||||
|  |             releasePlanMilestoneSchema, | ||||||
|  |             releasePlanMilestoneStrategySchema, | ||||||
|             projectStatsSchema, |             projectStatsSchema, | ||||||
|             createFeatureNamingPatternSchema, |             createFeatureNamingPatternSchema, | ||||||
|             featureTypeCountSchema, |             featureTypeCountSchema, | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								src/test/fixtures/fake/fake-release-plan-read-model.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/test/fixtures/fake/fake-release-plan-read-model.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | 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