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

feat: dependencies import validation (#5023)

This commit is contained in:
Jaanus Sellin 2023-10-13 14:17:54 +03:00 committed by GitHub
parent b6d945befc
commit 2263a1f062
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 78 additions and 6 deletions

View File

@ -340,7 +340,7 @@ test('validate import data', async () => {
errors: [
{
message:
'We detected the following custom strategy in the import file that needs to be created first:',
'We detected the following custom strategy that needs to be created first:',
affectedItems: ['customStrategy'],
},
{
@ -351,7 +351,7 @@ test('validate import data', async () => {
{
affectedItems: ['customSegment'],
message:
'We detected the following segments in the import file that need to be created first:',
'We detected the following segments that need to be created first:',
},
],
warnings: [

View File

@ -52,6 +52,7 @@ import { FeatureNameCheckResultWithFeaturePattern } from '../feature-toggle/feat
import { IDependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model-type';
import groupBy from 'lodash.groupby';
import { ISegmentService } from '../../segments/segment-service-interface';
import { FeatureDependenciesSchema } from '../../openapi/spec/feature-dependencies-schema';
export type IImportService = {
validate(
@ -194,6 +195,7 @@ export default class ExportImportService
featureNameCheckResult,
featureLimitResult,
unsupportedSegments,
unsupportedDependencies,
] = await Promise.all([
this.getUnsupportedStrategies(dto),
this.getUsedCustomStrategies(dto),
@ -210,6 +212,7 @@ export default class ExportImportService
this.getInvalidFeatureNames(dto),
this.getFeatureLimit(dto),
this.getUnsupportedSegments(dto),
this.getMissingDependencies(dto),
]);
const errors = ImportValidationMessages.compileErrors({
@ -221,6 +224,7 @@ export default class ExportImportService
featureNameCheckResult,
featureLimitResult,
segments: unsupportedSegments,
dependencies: unsupportedDependencies,
});
const warnings = ImportValidationMessages.compileWarnings({
archivedFeatures,
@ -250,6 +254,7 @@ export default class ExportImportService
this.importPermissionsService.verifyPermissions(dto, user, mode),
this.verifyFeatures(dto),
this.verifySegments(dto),
this.verifyDependencies(dto),
]);
}
@ -459,6 +464,32 @@ export default class ExportImportService
: [];
}
private async getMissingDependencies(
dto: ImportTogglesSchema,
): Promise<string[]> {
const dependentFeatures =
dto.data.dependencies?.flatMap((dependency) =>
dependency.dependencies.map((d) => d.feature),
) || [];
const importedFeatures = dto.data.features.map((f) => f.name);
const missingFromImported = dependentFeatures.filter(
(feature) => !importedFeatures.includes(feature),
);
let missingFeatures: string[] = [];
if (missingFromImported.length) {
const featuresFromStore = (
await this.toggleStore.getAllByNames(missingFromImported)
).map((f) => f.name);
missingFeatures = missingFromImported.filter(
(feature) => !featuresFromStore.includes(feature),
);
}
return missingFeatures;
}
private async verifySegments(dto: ImportTogglesSchema) {
const unsupportedSegments = await this.getUnsupportedSegments(dto);
if (unsupportedSegments.length > 0) {
@ -468,6 +499,17 @@ export default class ExportImportService
}
}
private async verifyDependencies(dto: ImportTogglesSchema) {
const unsupportedDependencies = await this.getMissingDependencies(dto);
if (unsupportedDependencies.length > 0) {
throw new BadDataError(
`The following dependent features are missing: ${unsupportedDependencies.join(
', ',
)}`,
);
}
}
private async verifyContextFields(dto: ImportTogglesSchema) {
const unsupportedContextFields = await this.getUnsupportedContextFields(
dto,

View File

@ -1006,6 +1006,16 @@ test('validate import data', async () => {
},
createdContextField,
],
dependencies: [
{
feature: 'childFeature',
dependencies: [
{
feature: 'parentFeature',
},
],
},
],
},
};
@ -1023,7 +1033,7 @@ test('validate import data', async () => {
errors: [
{
message:
'We detected the following custom strategy in the import file that needs to be created first:',
'We detected the following custom strategy that needs to be created first:',
affectedItems: ['customStrategy'],
},
{
@ -1051,9 +1061,14 @@ test('validate import data', async () => {
},
{
message:
'We detected the following segments in the import file that need to be created first:',
'We detected the following segments that need to be created first:',
affectedItems: ['customSegment'],
},
{
affectedItems: ['parentFeature'],
message:
'We detected the following dependencies that need to be created first:',
},
],
warnings: [
{

View File

@ -15,6 +15,7 @@ export interface IErrorsParams {
featureNameCheckResult: FeatureNameCheckResultWithFeaturePattern;
featureLimitResult: ProjectFeaturesLimit;
segments: string[];
dependencies: string[];
}
export interface IWarningParams {
@ -48,13 +49,14 @@ export class ImportValidationMessages {
featureNameCheckResult,
featureLimitResult,
segments,
dependencies,
}: IErrorsParams): ImportTogglesValidateItemSchema[] {
const errors: ImportTogglesValidateItemSchema[] = [];
if (strategies.length > 0) {
errors.push({
message:
'We detected the following custom strategy in the import file that needs to be created first:',
'We detected the following custom strategy that needs to be created first:',
affectedItems: strategies.map((strategy) => strategy.name),
});
}
@ -111,11 +113,19 @@ export class ImportValidationMessages {
if (segments.length > 0) {
errors.push({
message:
'We detected the following segments in the import file that need to be created first:',
'We detected the following segments that need to be created first:',
affectedItems: segments,
});
}
if (dependencies.length > 0) {
errors.push({
message:
'We detected the following dependencies that need to be created first:',
affectedItems: dependencies,
});
}
return errors;
}

View File

@ -14,6 +14,8 @@ import { legalValueSchema } from './legal-value-schema';
import { tagTypeSchema } from './tag-type-schema';
import { featureEnvironmentSchema } from './feature-environment-schema';
import { strategyVariantSchema } from './strategy-variant-schema';
import { featureDependenciesSchema } from './feature-dependencies-schema';
import { dependentFeatureSchema } from './dependent-feature-schema';
export const importTogglesSchema = {
$id: '#/components/schemas/importTogglesSchema',
@ -56,6 +58,8 @@ export const importTogglesSchema = {
parametersSchema,
legalValueSchema,
tagTypeSchema,
featureDependenciesSchema,
dependentFeatureSchema,
},
},
} as const;

View File

@ -163,3 +163,4 @@ export * from './update-feature-strategy-segments-schema';
export * from './dependent-feature-schema';
export * from './create-dependent-feature-schema';
export * from './parent-feature-options-schema';
export * from './feature-dependencies-schema';