1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: file import (#7219)

This commit is contained in:
David Leek 2024-05-31 13:21:41 +02:00 committed by GitHub
parent 6340ecd6bf
commit 80ba3647a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 67 additions and 15 deletions

View File

@ -164,9 +164,9 @@ exports[`should create default config 1`] = `
], ],
"getLogger": [Function], "getLogger": [Function],
"import": { "import": {
"dropBeforeImport": false, "environment": "development",
"file": undefined, "file": undefined,
"keepExisting": false, "project": "default",
}, },
"inlineSegmentConstraints": true, "inlineSegmentConstraints": true,
"isEnterprise": false, "isEnterprise": false,

View File

@ -333,11 +333,8 @@ const defaultAuthentication: IAuthOption = {
const defaultImport: WithOptional<IImportOption, 'file'> = { const defaultImport: WithOptional<IImportOption, 'file'> = {
file: process.env.IMPORT_FILE, file: process.env.IMPORT_FILE,
dropBeforeImport: parseEnvVarBoolean( project: process.env.IMPORT_PROJECT ?? 'default',
process.env.IMPORT_DROP_BEFORE_IMPORT, environment: process.env.IMPORT_ENVIRONMENT ?? 'development',
false,
),
keepExisting: parseEnvVarBoolean(process.env.IMPORT_KEEP_EXISTING, false),
}; };
const defaultEmail: IEmailOption = { const defaultEmail: IEmailOption = {

View File

@ -5,6 +5,8 @@ import type { IFeatureStrategiesStore } from '../feature-toggle/types/feature-to
import { import {
FeaturesExportedEvent, FeaturesExportedEvent,
FeaturesImportedEvent, FeaturesImportedEvent,
SYSTEM_USER,
SYSTEM_USER_AUDIT,
type FeatureToggleDTO, type FeatureToggleDTO,
type IAuditUser, type IAuditUser,
type IContextFieldStore, type IContextFieldStore,
@ -55,6 +57,7 @@ import type { IDependentFeaturesReadModel } from '../dependent-features/dependen
import groupBy from 'lodash.groupby'; import groupBy from 'lodash.groupby';
import { allSettledWithRejection } from '../../util/allSettledWithRejection'; import { allSettledWithRejection } from '../../util/allSettledWithRejection';
import type { ISegmentReadModel } from '../segment/segment-read-model-type'; import type { ISegmentReadModel } from '../segment/segment-read-model-type';
import { readFile } from './import-file-reader';
export type IImportService = { export type IImportService = {
validate( validate(
@ -67,6 +70,12 @@ export type IImportService = {
user: IUser, user: IUser,
auditUser: IAuditUser, auditUser: IAuditUser,
): Promise<void>; ): Promise<void>;
importFromFile(
file: string,
project: string,
environment: string,
): Promise<void>;
}; };
export type IExportService = { export type IExportService = {
@ -264,6 +273,16 @@ export default class ExportImportService
]); ]);
} }
async fileImportVerify(dto: ImportTogglesSchema): Promise<void> {
await allSettledWithRejection([
this.verifyStrategies(dto),
this.verifyContextFields(dto),
this.verifyFeatures(dto),
this.verifySegments(dto),
this.verifyDependencies(dto),
]);
}
async importFeatureData( async importFeatureData(
dto: ImportTogglesSchema, dto: ImportTogglesSchema,
auditUser: IAuditUser, auditUser: IAuditUser,
@ -281,16 +300,40 @@ export default class ExportImportService
auditUser: IAuditUser, auditUser: IAuditUser,
): Promise<void> { ): Promise<void> {
const cleanedDto = await this.cleanData(dto); const cleanedDto = await this.cleanData(dto);
await this.importVerify(cleanedDto, user); await this.importVerify(cleanedDto, user);
await this.processImport(cleanedDto, user, auditUser);
}
await this.importFeatureData(cleanedDto, auditUser); async importFromFile(
file: string,
project: string,
environment: string,
): Promise<void> {
const content = await readFile(file);
const data = JSON.parse(content);
const dto = {
project,
environment,
data,
};
const cleanedDto = await this.cleanData(dto);
await this.importEnvironmentData(cleanedDto, user, auditUser); await this.fileImportVerify(cleanedDto);
await this.processImport(cleanedDto, SYSTEM_USER, SYSTEM_USER_AUDIT);
}
private async processImport(
dto: ImportTogglesSchema,
user: IUser,
auditUser: IAuditUser,
) {
await this.importFeatureData(dto, auditUser);
await this.importEnvironmentData(dto, user, auditUser);
await this.eventService.storeEvent( await this.eventService.storeEvent(
new FeaturesImportedEvent({ new FeaturesImportedEvent({
project: cleanedDto.project, project: dto.project,
environment: cleanedDto.environment, environment: dto.environment,
auditUser, auditUser,
}), }),
); );

View File

@ -0,0 +1,8 @@
import * as fs from 'fs';
export const readFile: (file: string) => Promise<string> = async (file) =>
new Promise((resolve, reject) =>
fs.readFile(file, (err, v) =>
err ? reject(err) : resolve(v.toString('utf-8')),
),
);

View File

@ -97,7 +97,11 @@ async function createApp(
}; };
if (config.import.file) { if (config.import.file) {
// TODO: stateservice was here await services.importService.importFromFile(
config.import.file,
config.import.project,
config.import.environment,
);
} }
if ( if (

View File

@ -81,8 +81,8 @@ export interface IAuthOption {
export interface IImportOption { export interface IImportOption {
file: string; file: string;
keepExisting: boolean; project: string;
dropBeforeImport: boolean; environment: string;
} }
export interface IServerOption { export interface IServerOption {