1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

cleanup in export and import (#2973)

This commit is contained in:
Mateusz Kwasniewski 2023-01-24 14:29:59 +01:00 committed by GitHub
parent 6b22e2eb61
commit 80c444aa99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 26 additions and 197 deletions

View File

@ -11,7 +11,7 @@ const ImportExplanationHeader = styled(Typography)(({ theme }) => ({
marginBottom: theme.spacing(0.5),
}));
const ImportExplanationDescription = styled(Typography)(({ theme }) => ({
const ImportExplanationDescription = styled(Box)(({ theme }) => ({
marginBottom: theme.spacing(3),
color: theme.palette.text.secondary,
fontSize: theme.fontSizes.smallBody,

View File

@ -132,11 +132,13 @@ export const ValidationStage: FC<{
this configuration
</ErrorHeader>
{validationResult.errors.map(error => (
<Box sx={{ p: 2 }}>
<Box key={error.message} sx={{ p: 2 }}>
<ErrorMessage>{error.message}</ErrorMessage>
<StyledItems>
{error.affectedItems.map(item => (
<StyledItem>{item}</StyledItem>
<StyledItem key={item}>
{item}
</StyledItem>
))}
</StyledItems>
</Box>

View File

@ -120,7 +120,6 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
enabled: r.enabled,
featureName: r.feature_name,
environment: r.environment,
variants: r.variants,
}));
}

View File

@ -5,9 +5,7 @@ import { IUnleashConfig } from '../../types/option';
import { IUnleashServices } from '../../types/services';
import { Logger } from '../../logger';
import { OpenApiService } from '../../services/openapi-service';
import ExportImportService, {
IImportDTO,
} from 'lib/services/export-import-service';
import ExportImportService from 'lib/services/export-import-service';
import { InvalidOperationError } from '../../error';
import { createRequestSchema, createResponseSchema } from '../../openapi';
import { exportResultSchema } from '../../openapi/spec/export-result-schema';
@ -51,12 +49,6 @@ class ExportImportController extends Controller {
}),
],
});
this.route({
method: 'post',
path: '/import',
permission: NONE,
handler: this.importData,
});
}
async export(
@ -94,16 +86,5 @@ class ExportImportController extends Controller {
);
}
}
async importData(
req: IAuthRequest<unknown, unknown, IImportDTO, unknown>,
res: Response,
): Promise<void> {
const dto = req.body;
const user = req.user;
await this.exportImportService.import(dto, user);
res.status(201).end();
}
}
export default ExportImportController;

View File

@ -1,12 +1,5 @@
import { IUnleashConfig } from '../types/option';
import {
FeatureToggle,
IFeatureEnvironment,
IFeatureStrategy,
IFeatureStrategySegment,
ISegment,
ITag,
} from '../types/model';
import { IFeatureStrategy, IFeatureStrategySegment } from '../types/model';
import { Logger } from '../logger';
import { IFeatureTagStore } from '../types/stores/feature-tag-store';
import { IProjectStore } from '../types/stores/project-store';
@ -20,27 +13,17 @@ import { IEnvironmentStore } from '../types/stores/environment-store';
import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store';
import { IContextFieldStore, IUnleashStores } from '../types/stores';
import { ISegmentStore } from '../types/stores/segment-store';
import { IContextFieldDto } from '../types/stores/context-field-store';
import FeatureToggleService from './feature-toggle-service';
import User from 'lib/types/user';
import { ExportQuerySchema } from '../openapi/spec/export-query-schema';
import { FEATURES_EXPORTED, IFlagResolver, IUnleashServices } from '../types';
import { ExportResultSchema } from '../openapi';
export interface IImportDTO {
data: IExportData;
data: ExportResultSchema;
project: string;
environment: string;
}
export interface IExportData {
features: FeatureToggle[];
tags?: ITag[];
contextFields: IContextFieldDto[];
featureStrategies: IFeatureStrategy[];
featureEnvironments: IFeatureEnvironment[];
segments: ISegment[];
}
export default class ExportImportService {
private logger: Logger;
@ -102,7 +85,7 @@ export default class ExportImportService {
async export(
query: ExportQuerySchema,
userName: string,
): Promise<IExportData> {
): Promise<ExportResultSchema> {
const [
features,
featureEnvironments,
@ -140,10 +123,22 @@ export default class ExportImportService {
),
);
const result = {
features,
featureStrategies,
featureEnvironments,
contextFields: filteredContextFields,
features: features.map((item) => {
const { createdAt, archivedAt, lastSeenAt, ...rest } = item;
return rest;
}),
featureStrategies: featureStrategies.map((item) => ({
name: item.strategyName,
...item,
})),
featureEnvironments: featureEnvironments.map((item) => ({
...item,
name: item.featureName,
})),
contextFields: filteredContextFields.map((item) => {
const { createdAt, ...rest } = item;
return rest;
}),
featureTags,
segments: filteredSegments,
};
@ -169,48 +164,6 @@ export default class ExportImportService {
.map((segment) => segment.segmentId);
});
}
async import(dto: IImportDTO, user: User): Promise<void> {
await Promise.all(
dto.data.features.map((feature) =>
this.featureToggleService.createFeatureToggle(
dto.project,
feature,
user.name,
),
),
);
await Promise.all(
dto.data.featureStrategies.map((featureStrategy) =>
this.featureToggleService.unprotectedCreateStrategy(
{
name: featureStrategy.strategyName,
constraints: featureStrategy.constraints,
parameters: featureStrategy.parameters,
segments: featureStrategy.segments,
sortOrder: featureStrategy.sortOrder,
},
{
featureName: featureStrategy.featureName,
environment: dto.environment,
projectId: dto.project,
},
user.name,
),
),
);
await Promise.all(
dto.data.featureEnvironments.map((featureEnvironment) =>
this.featureToggleService.unprotectedUpdateEnabled(
dto.project,
featureEnvironment.featureName,
dto.environment,
featureEnvironment.enabled,
user.name,
),
),
);
}
}
module.exports = ExportImportService;

View File

@ -6,17 +6,14 @@ import dbInit, { ITestDb } from '../../helpers/database-init';
import getLogger from '../../../fixtures/no-logger';
import { IEventStore } from 'lib/types/stores/event-store';
import {
FeatureToggle,
FeatureToggleDTO,
IEnvironmentStore,
IFeatureStrategy,
IFeatureToggleStore,
IProjectStore,
ISegment,
IStrategyConfig,
} from 'lib/types';
import { DEFAULT_ENV } from '../../../../lib/util';
import { IImportDTO } from '../../../../lib/services/export-import-service';
import { ContextFieldSchema } from '../../../../lib/openapi';
let app: IUnleashTest;
@ -185,7 +182,6 @@ test('exports features', async () => {
enabled: false,
environment: 'default',
featureName: 'first_feature',
variants: [],
},
],
segments: [
@ -248,7 +244,6 @@ test('should export custom context fields', async () => {
enabled: false,
environment: 'default',
featureName: 'first_feature',
variants: [],
},
],
contextFields: [context],
@ -289,7 +284,6 @@ test('should export tags', async () => {
enabled: false,
environment: 'default',
featureName: 'first_feature',
variants: [],
},
],
featureTags: [{ featureName, tagValue: 'tag1' }],
@ -317,103 +311,3 @@ test('returns all features, when no feature was defined', async () => {
expect(body.features).toHaveLength(2);
});
test('import features to existing project and environment', async () => {
const feature = 'first_feature';
const project = 'new_project';
const environment = 'staging';
const variants = [
{
name: 'variantA',
weight: 500,
payload: {
type: 'string',
value: 'payloadA',
},
overrides: [],
stickiness: 'default',
weightType: 'variable',
},
{
name: 'variantB',
weight: 500,
payload: {
type: 'string',
value: 'payloadB',
},
overrides: [],
stickiness: 'default',
weightType: 'variable',
},
];
const exportedFeature: FeatureToggle = {
project: 'old_project',
name: 'first_feature',
variants,
};
const exportedStrategy: IFeatureStrategy = {
id: '798cb25a-2abd-47bd-8a95-40ec13472309',
featureName: feature,
projectId: 'old_project',
environment: 'old_environment',
strategyName: 'default',
parameters: {},
constraints: [],
};
const importPayload: IImportDTO = {
data: {
features: [exportedFeature],
featureStrategies: [exportedStrategy],
featureEnvironments: [
{
enabled: true,
featureName: 'first_feature',
environment: 'irrelevant',
},
],
contextFields: [],
segments: [],
},
project: project,
environment: environment,
};
await createProject(project, environment);
await app.request
.post('/api/admin/features-batch/import')
.send(importPayload)
.set('Content-Type', 'application/json')
.expect(201);
const { body: importedFeature } = await app.request
.get(`/api/admin/features/${feature}`)
.expect(200);
expect(importedFeature).toMatchObject({
name: 'first_feature',
project: project,
variants,
});
const { body: importedFeatureEnvironment } = await app.request
.get(
`/api/admin/projects/${project}/features/${feature}/environments/${environment}`,
)
.expect(200);
expect(importedFeatureEnvironment).toMatchObject({
name: feature,
environment,
enabled: true,
strategies: [
{
featureName: feature,
projectId: project,
environment: environment,
parameters: {},
constraints: [],
sortOrder: 9999,
name: 'default',
},
],
});
});