mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-27 00:19:39 +01:00
feat: Persist dependent features (#4772)
This commit is contained in:
parent
12d9297f68
commit
be7f0d8b4e
@ -37,6 +37,7 @@ import ProjectStatsStore from './project-stats-store';
|
||||
import { Db } from './db';
|
||||
import { ImportTogglesStore } from '../features/export-import-toggles/import-toggles-store';
|
||||
import PrivateProjectStore from '../features/private-project/privateProjectStore';
|
||||
import { DependentFeaturesStore } from '../features/dependent-features/dependent-features-store';
|
||||
|
||||
export const createStores = (
|
||||
config: IUnleashConfig,
|
||||
@ -130,6 +131,7 @@ export const createStores = (
|
||||
projectStatsStore: new ProjectStatsStore(db, eventBus, getLogger),
|
||||
importTogglesStore: new ImportTogglesStore(db),
|
||||
privateProjectStore: new PrivateProjectStore(db, getLogger),
|
||||
dependentFeaturesStore: new DependentFeaturesStore(db),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { CreateDependentFeatureSchema } from '../../openapi';
|
||||
import { IDependentFeaturesStore } from './dependent-features-store-type';
|
||||
|
||||
export type FeatureDependency =
|
||||
| {
|
||||
@ -9,6 +10,12 @@ export type FeatureDependency =
|
||||
}
|
||||
| { parent: string; child: string; enabled: false };
|
||||
export class DependentFeaturesService {
|
||||
private dependentFeaturesStore: IDependentFeaturesStore;
|
||||
|
||||
constructor(dependentFeaturesStore: IDependentFeaturesStore) {
|
||||
this.dependentFeaturesStore = dependentFeaturesStore;
|
||||
}
|
||||
|
||||
async upsertFeatureDependency(
|
||||
parentFeature: string,
|
||||
dependentFeature: CreateDependentFeatureSchema,
|
||||
@ -27,6 +34,6 @@ export class DependentFeaturesService {
|
||||
enabled: true,
|
||||
variants,
|
||||
};
|
||||
console.log(featureDependency);
|
||||
await this.dependentFeaturesStore.upsert(featureDependency);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
import { FeatureDependency } from './dependent-features-service';
|
||||
|
||||
export interface IDependentFeaturesStore {
|
||||
upsert(featureDependency: FeatureDependency): Promise<void>;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
import { FeatureDependency } from './dependent-features-service';
|
||||
import { Db } from '../../db/db';
|
||||
import { IDependentFeaturesStore } from './dependent-features-store-type';
|
||||
|
||||
type SerializableFeatureDependency = Omit<FeatureDependency, 'variants'> & {
|
||||
variants?: string;
|
||||
};
|
||||
export class DependentFeaturesStore implements IDependentFeaturesStore {
|
||||
private db: Db;
|
||||
|
||||
constructor(db: Db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
async upsert(featureDependency: FeatureDependency): Promise<void> {
|
||||
const serializableFeatureDependency: SerializableFeatureDependency = {
|
||||
parent: featureDependency.parent,
|
||||
child: featureDependency.child,
|
||||
enabled: featureDependency.enabled,
|
||||
};
|
||||
if ('variants' in featureDependency) {
|
||||
serializableFeatureDependency.variants = JSON.stringify(
|
||||
featureDependency.variants,
|
||||
);
|
||||
}
|
||||
await this.db('dependent_features')
|
||||
.insert(serializableFeatureDependency)
|
||||
.onConflict(['parent', 'child'])
|
||||
.merge();
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_dependencies', getLogger);
|
||||
db = await dbInit('dependent_features', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
@ -50,7 +50,14 @@ test('should add feature dependency', async () => {
|
||||
await app.createFeature(parent);
|
||||
await app.createFeature(child);
|
||||
|
||||
// save explicit enabled and variants
|
||||
await addFeatureDependency(parent, {
|
||||
feature: child,
|
||||
enabled: false,
|
||||
});
|
||||
// overwrite with implicit enabled: true and variants
|
||||
await addFeatureDependency(parent, {
|
||||
feature: child,
|
||||
variants: ['variantB'],
|
||||
});
|
||||
});
|
@ -0,0 +1,7 @@
|
||||
import { IDependentFeaturesStore } from './dependent-features-store-type';
|
||||
|
||||
export class FakeDependentFeaturesStore implements IDependentFeaturesStore {
|
||||
async upsert(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
@ -290,7 +290,9 @@ export const createServices = (
|
||||
|
||||
const eventAnnouncerService = new EventAnnouncerService(stores, config);
|
||||
|
||||
const dependentFeaturesService = new DependentFeaturesService();
|
||||
const dependentFeaturesService = new DependentFeaturesService(
|
||||
stores.dependentFeaturesStore,
|
||||
);
|
||||
|
||||
return {
|
||||
accessService,
|
||||
|
@ -34,6 +34,7 @@ import { IAccountStore } from './stores/account-store';
|
||||
import { IProjectStatsStore } from './stores/project-stats-store-type';
|
||||
import { IImportTogglesStore } from '../features/export-import-toggles/import-toggles-store-type';
|
||||
import { IPrivateProjectStore } from '../features/private-project/privateProjectStoreType';
|
||||
import { IDependentFeaturesStore } from '../features/dependent-features/dependent-features-store-type';
|
||||
|
||||
export interface IUnleashStores {
|
||||
accessStore: IAccessStore;
|
||||
@ -72,6 +73,7 @@ export interface IUnleashStores {
|
||||
projectStatsStore: IProjectStatsStore;
|
||||
importTogglesStore: IImportTogglesStore;
|
||||
privateProjectStore: IPrivateProjectStore;
|
||||
dependentFeaturesStore: IDependentFeaturesStore;
|
||||
}
|
||||
|
||||
export {
|
||||
@ -110,4 +112,5 @@ export {
|
||||
IFavoriteProjectsStore,
|
||||
IImportTogglesStore,
|
||||
IPrivateProjectStore,
|
||||
IDependentFeaturesStore,
|
||||
};
|
||||
|
28
src/migrations/20230919104006-dependent-features.js
Normal file
28
src/migrations/20230919104006-dependent-features.js
Normal file
@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
exports.up = function (db, cb) {
|
||||
db.runSql(
|
||||
`
|
||||
CREATE TABLE IF NOT EXISTS dependent_features
|
||||
(
|
||||
parent varchar(255) NOT NULL,
|
||||
child varchar(255) NOT NULL,
|
||||
enabled boolean DEFAULT true NOT NULL,
|
||||
variants JSONB DEFAULT '[]'::jsonb NOT NULL,
|
||||
PRIMARY KEY (parent, child),
|
||||
FOREIGN KEY (parent) REFERENCES features (name) ON DELETE RESTRICT,
|
||||
FOREIGN KEY (child) REFERENCES features (name) ON DELETE CASCADE
|
||||
);
|
||||
`,
|
||||
cb(),
|
||||
);
|
||||
};
|
||||
|
||||
exports.down = function (db, cb) {
|
||||
db.runSql(
|
||||
`
|
||||
DROP TABLE dependent_features;
|
||||
`,
|
||||
cb,
|
||||
);
|
||||
};
|
2
src/test/fixtures/store.ts
vendored
2
src/test/fixtures/store.ts
vendored
@ -37,6 +37,7 @@ import FakeFavoriteFeaturesStore from './fake-favorite-features-store';
|
||||
import FakeFavoriteProjectsStore from './fake-favorite-projects-store';
|
||||
import { FakeAccountStore } from './fake-account-store';
|
||||
import FakeProjectStatsStore from './fake-project-stats-store';
|
||||
import { FakeDependentFeaturesStore } from '../../lib/features/dependent-features/fake-dependent-features-store';
|
||||
|
||||
const db = {
|
||||
select: () => ({
|
||||
@ -83,6 +84,7 @@ const createStores: () => IUnleashStores = () => {
|
||||
projectStatsStore: new FakeProjectStatsStore(),
|
||||
importTogglesStore: {} as IImportTogglesStore,
|
||||
privateProjectStore: {} as IPrivateProjectStore,
|
||||
dependentFeaturesStore: new FakeDependentFeaturesStore(),
|
||||
};
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user