Skip to content

Commit

Permalink
Merge pull request #2071 from CarnegieLearningWeb/feature/import-feat…
Browse files Browse the repository at this point in the history
…ure-flag-list

Import feature flag list feature
  • Loading branch information
Yagnik56 authored Nov 5, 2024
2 parents 733dc51 + 6557f70 commit 073985d
Show file tree
Hide file tree
Showing 32 changed files with 784 additions and 69 deletions.
219 changes: 217 additions & 2 deletions backend/packages/Upgrade/src/api/controllers/FeatureFlagController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,19 @@ import {
import { FeatureFlagFilterModeUpdateValidator } from './validators/FeatureFlagFilterModeUpdateValidator';
import { AppRequest, PaginationResponse } from '../../types';
import { IImportError, FEATURE_FLAG_LIST_FILTER_MODE, SERVER_ERROR } from 'upgrade_types';
import { FeatureFlagImportValidation, FeatureFlagValidation, IdValidator } from './validators/FeatureFlagValidator';
import {
FeatureFlagImportValidation,
FeatureFlagListImportValidation,
FeatureFlagValidation,
IdValidator,
} from './validators/FeatureFlagValidator';
import { ExperimentUserService } from '../services/ExperimentUserService';
import { FeatureFlagListValidator } from '../controllers/validators/FeatureFlagListValidator';
import { Segment } from 'src/api/models/Segment';
import { Response } from 'express';
import { UserDTO } from '../DTO/UserDTO';
import { ImportFeatureFlagListValidator } from './validators/FeatureFlagImportValidator';
import { NotFoundException } from '@nestjs/common/exceptions';

interface FeatureFlagsPaginationInfo extends PaginationResponse {
nodes: FeatureFlag[];
Expand Down Expand Up @@ -123,6 +130,19 @@ interface FeatureFlagsPaginationInfo extends PaginationResponse {
* list:
* type: object
* $ref: '#/definitions/FeatureFlagInclusionExclusionList'
* FeatureFlagListImportObject:
* required:
* - files
* - listType
* - flagId
* properties:
* files:
* type: object
* listType:
* type: string
* enum: [featureFlagSegmentInclusion, featureFlagSegmentExclusion]
* flagId:
* type: string
*/

/**
Expand Down Expand Up @@ -741,7 +761,7 @@ export class FeatureFlagsController {
* @swagger
* /flags/import:
* post:
* description: Validating Feature Flag
* description: Importing Feature Flag
* consumes:
* - application/json
* parameters:
Expand Down Expand Up @@ -833,4 +853,199 @@ export class FeatureFlagsController {
}
return response.status(404).send('Feature Flag not found');
}

/**
* @swagger
* /flags/lists/import/validation:
* post:
* description: Validating Feature Flag List
* consumes:
* - application/json
* parameters:
* - in: body
* name: lists
* description: Import FeatureFlag List Files
* required: true
* schema:
* type: object
* $ref: '#/definitions/FeatureFlagListImportObject'
* tags:
* - Feature Flags
* produces:
* - application/json
* responses:
* '200':
* description: Validations are completed
* schema:
* type: array
* items:
* type: object
* properties:
* fileName:
* type: string
* compatibilityType:
* type: string
* enum: [compatible, warning, incompatible]
* '401':
* description: AuthorizationRequiredError
* '500':
* description: Internal Server Error
*/
@Post('/lists/import/validation')
public async validateImportFeatureFlagList(
@Body({ validate: true }) lists: FeatureFlagListImportValidation,
@Req() request: AppRequest
): Promise<ValidatedFeatureFlagsError[]> {
return await this.featureFlagService.validateImportFeatureFlagLists(lists.files, lists.flagId, request.logger);
}

/**
* @swagger
* /flags/lists/import:
* post:
* description: Importing Feature Flag List
* consumes:
* - application/json
* parameters:
* - in: body
* name: lists
* description: Import FeatureFlag List Files
* required: true
* schema:
* type: object
* $ref: '#/definitions/FeatureFlagListImportObject'
* tags:
* - Feature Flag Lists
* produces:
* - application/json
* responses:
* '200':
* description: New Feature flag is imported
* '401':
* description: AuthorizationRequiredError
* '500':
* description: Internal Server Error
*/
@Post('/lists/import')
public async importFeatureFlagLists(
@Body({ validate: true }) lists: FeatureFlagListImportValidation,
@CurrentUser() currentUser: UserDTO,
@Req() request: AppRequest
): Promise<IImportError[]> {
return await this.featureFlagService.importFeatureFlagLists(
lists.files,
lists.flagId,
lists.listType,
currentUser,
request.logger
);
}

/**
* @swagger
* /flags/export/includeLists/{id}:
* get:
* description: Export All Include lists of Feature Flag JSON
* tags:
* - Feature Flags
* produces:
* - application/json
* parameters:
* - in: path
* flagId: Id
* description: Feature Flag Id
* required: true
* schema:
* type: string
* responses:
* '200':
* description: Get Feature Flag''s All Include Lists JSON
* '401':
* description: Authorization Required Error
* '404':
* description: Feature Flag not found
* '400':
* description: id must be a UUID
* '500':
* description: Internal Server Error
*/
@Get('/export/includeLists/:id')
public async exportAllIncludeLists(
@Params({ validate: true }) { id }: IdValidator,
@Req() request: AppRequest,
@Res() response: Response
): Promise<ImportFeatureFlagListValidator[]> {
const lists = await this.featureFlagService.exportAllLists(
id,
FEATURE_FLAG_LIST_FILTER_MODE.INCLUSION,
request.logger
);
if (lists?.length) {
// download JSON file with appropriate headers to response body;
if (lists.length === 1) {
response.setHeader('Content-Disposition', `attachment; filename="${lists[0].segment.name}.json"`);
} else {
response.setHeader('Content-Disposition', `attachment; filename="lists.zip"`);
}
response.setHeader('Content-Type', 'application/json');
} else {
throw new NotFoundException('Include lists not found.');
}

return lists;
}

/**
* @swagger
* /flags/export/excludeLists/{id}:
* get:
* description: Export All Exclude lists of Feature Flag JSON
* tags:
* - Feature Flags
* produces:
* - application/json
* parameters:
* - in: path
* flagId: Id
* description: Feature Flag Id
* required: true
* schema:
* type: string
* responses:
* '200':
* description: Get Feature Flag''s All Include Lists JSON
* '401':
* description: Authorization Required Error
* '404':
* description: Feature Flag not found
* '400':
* description: id must be a UUID
* '500':
* description: Internal Server Error
*/
@Get('/export/excludeLists/:id')
public async exportAllExcludeLists(
@Params({ validate: true }) { id }: IdValidator,
@Req() request: AppRequest,
@Res() response: Response
): Promise<ImportFeatureFlagListValidator[]> {
const lists = await this.featureFlagService.exportAllLists(
id,
FEATURE_FLAG_LIST_FILTER_MODE.EXCLUSION,
request.logger
);
if (lists?.length) {
// download JSON file with appropriate headers to response body;
if (lists.length === 1) {
response.setHeader('Content-Disposition', `attachment; filename="${lists[0].segment.name}.json"`);
} else {
response.setHeader('Content-Disposition', `attachment; filename="lists.zip"`);
}
response.setHeader('Content-Type', 'application/json');
} else {
throw new NotFoundException('Exclude lists not found.');
}

return lists;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { SEGMENT_TYPE } from 'upgrade_types';
import { Type } from 'class-transformer';
import { FeatureFlagCoreValidation } from './FeatureFlagValidator';
import { SegmentInputValidator } from './SegmentInputValidator';

class IndividualValidator {
@IsNotEmpty()
Expand Down Expand Up @@ -88,7 +89,7 @@ class SegmentImportValidator {
public subSegments: SegmentValidator[];
}

class FeatureFlagListImportValidator {
export class FeatureFlagListImportValidator {
@IsDefined()
@IsBoolean()
public enabled: boolean;
Expand All @@ -101,6 +102,15 @@ class FeatureFlagListImportValidator {
public segment: SegmentImportValidator;
}

export class ImportFeatureFlagListValidator {
@IsNotEmpty()
public listType: string;

@ValidateNested()
@Type(() => SegmentInputValidator)
public segment: SegmentInputValidator;
}

export class FeatureFlagImportDataValidation extends FeatureFlagCoreValidation {
@IsOptional()
@IsArray()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { IsNotEmpty, IsDefined, IsString, IsArray, IsEnum, IsOptional, ValidateNested, IsUUID } from 'class-validator';
import { FILTER_MODE } from 'upgrade_types';
import { FEATURE_FLAG_STATUS } from 'upgrade_types';
import { FILTER_MODE, FEATURE_FLAG_STATUS, FEATURE_FLAG_LIST_FILTER_MODE } from 'upgrade_types';
import { Type } from 'class-transformer';
import { FeatureFlagListValidator } from './FeatureFlagListValidator';

Expand Down Expand Up @@ -67,6 +66,22 @@ export class FeatureFlagImportValidation {
public files: FeatureFlagFile[];
}

export class FeatureFlagListImportValidation {
@IsArray()
@ValidateNested({ each: true })
@Type(() => FeatureFlagFile)
public files: FeatureFlagFile[];

@IsString()
@IsNotEmpty()
@IsEnum(FEATURE_FLAG_LIST_FILTER_MODE)
public listType: FEATURE_FLAG_LIST_FILTER_MODE;

@IsUUID()
@IsNotEmpty()
public flagId: string;
}

class FeatureFlagFile {
@IsString()
@IsNotEmpty()
Expand Down
Loading

0 comments on commit 073985d

Please sign in to comment.