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:
parent
6b22e2eb61
commit
80c444aa99
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -120,7 +120,6 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
||||
enabled: r.enabled,
|
||||
featureName: r.feature_name,
|
||||
environment: r.environment,
|
||||
variants: r.variants,
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user