From 674f8159f733fcb5cc9f8f0eb2f2c25c410dbe71 Mon Sep 17 00:00:00 2001 From: RidhamShah Date: Tue, 20 Aug 2024 12:24:36 +0530 Subject: [PATCH] added validator to validate import json --- .../Upgrade/src/api/DTO/ExperimentDTO.ts | 9 +-- .../validators/FeatureFlagValidator.ts | 16 ++--- .../src/api/services/FeatureFlagService.ts | 62 +++++-------------- 3 files changed, 27 insertions(+), 60 deletions(-) diff --git a/backend/packages/Upgrade/src/api/DTO/ExperimentDTO.ts b/backend/packages/Upgrade/src/api/DTO/ExperimentDTO.ts index b4158ca568..5d89189088 100644 --- a/backend/packages/Upgrade/src/api/DTO/ExperimentDTO.ts +++ b/backend/packages/Upgrade/src/api/DTO/ExperimentDTO.ts @@ -43,6 +43,7 @@ export { EXPERIMENT_SORT_KEY, IExperimentSearchParams, IExperimentSortParams, + SegmentValidator, }; class PayloadValidator { @@ -296,14 +297,6 @@ export class ParticipantsValidator { public segment: SegmentValidator; } -export class ParticipantsArrayValidator { - @IsNotEmpty() - @IsArray() - @ValidateNested({ each: true }) - @Type(() => SegmentValidator) - public segments: SegmentValidator[]; -} - class StateTimeLogValidator { @IsNotEmpty() @IsString() diff --git a/backend/packages/Upgrade/src/api/controllers/validators/FeatureFlagValidator.ts b/backend/packages/Upgrade/src/api/controllers/validators/FeatureFlagValidator.ts index 7dfa2c4095..7a700c8fa4 100644 --- a/backend/packages/Upgrade/src/api/controllers/validators/FeatureFlagValidator.ts +++ b/backend/packages/Upgrade/src/api/controllers/validators/FeatureFlagValidator.ts @@ -1,8 +1,8 @@ import { IsNotEmpty, IsDefined, IsString, IsArray, IsEnum, IsOptional, ValidateNested, IsUUID } from 'class-validator'; -import { ParticipantsArrayValidator } from '../../DTO/ExperimentDTO'; import { FILTER_MODE } from 'upgrade_types'; import { FEATURE_FLAG_STATUS } from 'upgrade_types'; import { Type } from 'class-transformer'; +import { FeatureFlagListValidator } from './FeatureFlagListValidator'; export class FeatureFlagValidation { @IsOptional() @@ -43,14 +43,16 @@ export class FeatureFlagValidation { public tags: string[]; @IsOptional() - @ValidateNested() - @Type(() => ParticipantsArrayValidator) - public featureFlagSegmentInclusion?: ParticipantsArrayValidator; + @IsArray() + @ValidateNested({ each: true }) + @Type(() => FeatureFlagListValidator) + public featureFlagSegmentInclusion?: FeatureFlagListValidator[]; @IsOptional() - @ValidateNested() - @Type(() => ParticipantsArrayValidator) - public featureFlagSegmentExclusion?: ParticipantsArrayValidator; + @IsArray() + @ValidateNested({ each: true }) + @Type(() => FeatureFlagListValidator) + public featureFlagSegmentExclusion?: FeatureFlagListValidator[]; } export class UserParamsValidator { diff --git a/backend/packages/Upgrade/src/api/services/FeatureFlagService.ts b/backend/packages/Upgrade/src/api/services/FeatureFlagService.ts index df6a82e730..a46936c56b 100644 --- a/backend/packages/Upgrade/src/api/services/FeatureFlagService.ts +++ b/backend/packages/Upgrade/src/api/services/FeatureFlagService.ts @@ -25,6 +25,8 @@ import { ExperimentAssignmentService } from './ExperimentAssignmentService'; import { SegmentService } from './SegmentService'; import { ErrorWithType } from '../errors/ErrorWithType'; import { RequestedExperimentUser } from '../controllers/validators/ExperimentUserValidator'; +import { validate } from 'class-validator'; +import { plainToClass } from 'class-transformer'; @Service() export class FeatureFlagService { @@ -140,7 +142,7 @@ export class FeatureFlagService { await transactionalEntityManager.getRepository(Segment).delete(segmentInclusion.segment.id); } catch (err) { const error = err as ErrorWithType; - error.details = 'Error in deleting Feature Flag Included Segment fron DB'; + error.details = 'Error in deleting Feature Flag Included Segment from DB'; error.type = SERVER_ERROR.QUERY_FAILED; logger.error(error); throw error; @@ -151,7 +153,7 @@ export class FeatureFlagService { await transactionalEntityManager.getRepository(Segment).delete(segmentExclusion.segment.id); } catch (err) { const error = err as ErrorWithType; - error.details = 'Error in deleting Feature Flag Excluded Segment fron DB'; + error.details = 'Error in deleting Feature Flag Excluded Segment from DB'; error.type = SERVER_ERROR.QUERY_FAILED; logger.error(error); throw error; @@ -450,7 +452,7 @@ export class FeatureFlagService { logger.info({ message: 'Validate feature flags' }); const validationErrors = await Promise.allSettled( featureFlagFiles.map(async (featureFlagFile) => { - let featureFlag: FeatureFlag; + let featureFlag: FeatureFlagValidation; try { featureFlag = JSON.parse(featureFlagFile.fileContent as string); } catch (parseError) { @@ -478,54 +480,24 @@ export class FeatureFlagService { .filter((error) => error !== null); } - private async validateImportFeatureFlag(fileName: string, flag: FeatureFlag) { + private async validateImportFeatureFlag(fileName: string, flag: FeatureFlagValidation) { let compatibilityType = FF_COMPATIBILITY_TYPE.COMPATIBLE; - // check for subSegmentIds - let segmentValidator = false; - flag.featureFlagSegmentInclusion.forEach((segmentInclusion) => { - if (!segmentInclusion.segment.subSegments) { - segmentValidator = true; - return; + flag = plainToClass(FeatureFlagValidation, flag); + await validate(flag).then((errors) => { + if (errors.length > 0) { + compatibilityType = FF_COMPATIBILITY_TYPE.INCOMPATIBLE; } - segmentInclusion.segment.subSegments.forEach((subSegment) => { - if (subSegment.id == undefined) { - segmentValidator = true; - return; - } - }); - }); - - flag.featureFlagSegmentExclusion.forEach((segmentExclusion) => { - if (!segmentExclusion.segment.subSegments) { - segmentValidator = true; - return; - } - segmentExclusion.segment.subSegments.forEach((subSegment) => { - if (subSegment.id == undefined) { - segmentValidator = true; - return; - } - }); }); - if (segmentValidator) { - return { - fileName: fileName, - compatibilityType: FF_COMPATIBILITY_TYPE.INCOMPATIBLE, - }; - } - - if (!flag.name || !flag.key || !flag.context) { - compatibilityType = FF_COMPATIBILITY_TYPE.INCOMPATIBLE; - } else { + if (compatibilityType === FF_COMPATIBILITY_TYPE.COMPATIBLE) { const segmentIds = [ - ...flag.featureFlagSegmentInclusion.flatMap((segmentInclusion) => - segmentInclusion.segment.subSegments.map((subSegment) => subSegment.id) - ), - ...flag.featureFlagSegmentExclusion.flatMap((segmentExclusion) => - segmentExclusion.segment.subSegments.map((subSegment) => subSegment.id) - ), + ...flag.featureFlagSegmentInclusion.flatMap((segmentInclusion) => { + return segmentInclusion.list.subSegmentIds; + }), + ...flag.featureFlagSegmentExclusion.flatMap((segmentExclusion) => { + return segmentExclusion.list.subSegmentIds; + }), ]; const segments = await this.segmentService.getSegmentByIds(segmentIds);