mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: add links to feature read model (#9905)
This commit is contained in:
parent
8e05c92440
commit
b9f1d8414c
@ -0,0 +1,10 @@
|
||||
import type {
|
||||
IFeatureLink,
|
||||
IFeatureLinksReadModel,
|
||||
} from './feature-links-read-model-type';
|
||||
|
||||
export class FakeFeatureLinksReadModel implements IFeatureLinksReadModel {
|
||||
async getLinks(feature: string): Promise<IFeatureLink[]> {
|
||||
return [];
|
||||
}
|
||||
}
|
@ -97,6 +97,10 @@ test('should manage feature links', async () => {
|
||||
featureName: 'my_feature',
|
||||
},
|
||||
]);
|
||||
const { body } = await app.getProjectFeatures('default', 'my_feature');
|
||||
expect(body.links).toMatchObject([
|
||||
{ id: links[0].id, title: 'feature link', url: 'example.com' },
|
||||
]);
|
||||
|
||||
await updatedLink('my_feature', links[0].id, {
|
||||
url: 'example_updated.com',
|
||||
|
@ -0,0 +1,9 @@
|
||||
export interface IFeatureLink {
|
||||
id: string;
|
||||
url: string;
|
||||
title: string | null;
|
||||
}
|
||||
|
||||
export interface IFeatureLinksReadModel {
|
||||
getLinks(feature: string): Promise<IFeatureLink[]>;
|
||||
}
|
25
src/lib/features/feature-links/feature-links-read-model.ts
Normal file
25
src/lib/features/feature-links/feature-links-read-model.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { Db } from '../../db/db';
|
||||
import type {
|
||||
IFeatureLink,
|
||||
IFeatureLinksReadModel,
|
||||
} from './feature-links-read-model-type';
|
||||
|
||||
export class FeatureLinksReadModel implements IFeatureLinksReadModel {
|
||||
private db: Db;
|
||||
|
||||
constructor(db: Db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
async getLinks(feature: string): Promise<IFeatureLink[]> {
|
||||
const links = await this.db
|
||||
.from('feature_link')
|
||||
.where('feature_name', feature);
|
||||
|
||||
return links.map((link) => ({
|
||||
id: link.id,
|
||||
url: link.url,
|
||||
title: link.title,
|
||||
}));
|
||||
}
|
||||
}
|
@ -59,6 +59,8 @@ import { FeatureLifecycleReadModel } from '../feature-lifecycle/feature-lifecycl
|
||||
import { FakeFeatureLifecycleReadModel } from '../feature-lifecycle/fake-feature-lifecycle-read-model';
|
||||
import { FakeFeatureCollaboratorsReadModel } from './fake-feature-collaborators-read-model';
|
||||
import { FeatureCollaboratorsReadModel } from './feature-collaborators-read-model';
|
||||
import { FeatureLinksReadModel } from '../feature-links/feature-links-read-model';
|
||||
import { FakeFeatureLinksReadModel } from '../feature-links/fake-feature-links-read-model';
|
||||
|
||||
export const createFeatureToggleService = (
|
||||
db: Db,
|
||||
@ -128,6 +130,8 @@ export const createFeatureToggleService = (
|
||||
|
||||
const featureCollaboratorsReadModel = new FeatureCollaboratorsReadModel(db);
|
||||
|
||||
const featureLinksReadModel = new FeatureLinksReadModel(db);
|
||||
|
||||
const featureToggleService = new FeatureToggleService(
|
||||
{
|
||||
featureStrategiesStore,
|
||||
@ -149,6 +153,7 @@ export const createFeatureToggleService = (
|
||||
dependentFeaturesService,
|
||||
featureLifecycleReadModel,
|
||||
featureCollaboratorsReadModel,
|
||||
featureLinksReadModel,
|
||||
);
|
||||
return featureToggleService;
|
||||
};
|
||||
@ -189,6 +194,7 @@ export const createFakeFeatureToggleService = (config: IUnleashConfig) => {
|
||||
const featureLifecycleReadModel = new FakeFeatureLifecycleReadModel();
|
||||
const featureCollaboratorsReadModel =
|
||||
new FakeFeatureCollaboratorsReadModel();
|
||||
const featureLinksReadModel = new FakeFeatureLinksReadModel();
|
||||
|
||||
const featureToggleService = new FeatureToggleService(
|
||||
{
|
||||
@ -216,6 +222,7 @@ export const createFakeFeatureToggleService = (config: IUnleashConfig) => {
|
||||
dependentFeaturesService,
|
||||
featureLifecycleReadModel,
|
||||
featureCollaboratorsReadModel,
|
||||
featureLinksReadModel,
|
||||
);
|
||||
return {
|
||||
featureToggleService,
|
||||
|
@ -113,6 +113,10 @@ import type { ResourceLimitsSchema } from '../../openapi';
|
||||
import { throwExceedsLimitError } from '../../error/exceeds-limit-error';
|
||||
import type { Collaborator } from './types/feature-collaborators-read-model-type';
|
||||
import { sortStrategies } from '../../util/sortStrategies';
|
||||
import type {
|
||||
IFeatureLink,
|
||||
IFeatureLinksReadModel,
|
||||
} from '../feature-links/feature-links-read-model-type';
|
||||
|
||||
interface IFeatureContext {
|
||||
featureName: string;
|
||||
@ -182,6 +186,8 @@ class FeatureToggleService {
|
||||
|
||||
private featureCollaboratorsReadModel: IFeatureCollaboratorsReadModel;
|
||||
|
||||
private featureLinksReadModel: IFeatureLinksReadModel;
|
||||
|
||||
private dependentFeaturesService: DependentFeaturesService;
|
||||
|
||||
private eventBus: EventEmitter;
|
||||
@ -227,6 +233,7 @@ class FeatureToggleService {
|
||||
dependentFeaturesService: DependentFeaturesService,
|
||||
featureLifecycleReadModel: IFeatureLifecycleReadModel,
|
||||
featureCollaboratorsReadModel: IFeatureCollaboratorsReadModel,
|
||||
featureLinksReadModel: IFeatureLinksReadModel,
|
||||
) {
|
||||
this.logger = getLogger('services/feature-toggle-service.ts');
|
||||
this.featureStrategiesStore = featureStrategiesStore;
|
||||
@ -247,6 +254,7 @@ class FeatureToggleService {
|
||||
this.dependentFeaturesService = dependentFeaturesService;
|
||||
this.featureLifecycleReadModel = featureLifecycleReadModel;
|
||||
this.featureCollaboratorsReadModel = featureCollaboratorsReadModel;
|
||||
this.featureLinksReadModel = featureLinksReadModel;
|
||||
this.eventBus = eventBus;
|
||||
this.resourceLimits = resourceLimits;
|
||||
}
|
||||
@ -442,6 +450,7 @@ class FeatureToggleService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async validateStrategyType(
|
||||
strategyName: string | undefined,
|
||||
): Promise<void> {
|
||||
@ -1074,14 +1083,19 @@ class FeatureToggleService {
|
||||
let children: string[] = [];
|
||||
let lifecycle: IFeatureLifecycleStage | undefined = undefined;
|
||||
let collaborators: Collaborator[] = [];
|
||||
[dependencies, children, lifecycle, collaborators] = await Promise.all([
|
||||
this.dependentFeaturesReadModel.getParents(featureName),
|
||||
this.dependentFeaturesReadModel.getChildren([featureName]),
|
||||
this.featureLifecycleReadModel.findCurrentStage(featureName),
|
||||
this.featureCollaboratorsReadModel.getFeatureCollaborators(
|
||||
featureName,
|
||||
),
|
||||
]);
|
||||
let links: IFeatureLink[] = [];
|
||||
[dependencies, children, lifecycle, collaborators, links] =
|
||||
await Promise.all([
|
||||
this.dependentFeaturesReadModel.getParents(featureName),
|
||||
this.dependentFeaturesReadModel.getChildren([featureName]),
|
||||
this.featureLifecycleReadModel.findCurrentStage(featureName),
|
||||
this.featureCollaboratorsReadModel.getFeatureCollaborators(
|
||||
featureName,
|
||||
),
|
||||
this.flagResolver.isEnabled('featureLinks')
|
||||
? this.featureLinksReadModel.getLinks(featureName)
|
||||
: Promise.resolve([]),
|
||||
]);
|
||||
|
||||
if (environmentVariants) {
|
||||
const result =
|
||||
@ -1095,6 +1109,7 @@ class FeatureToggleService {
|
||||
dependencies,
|
||||
children,
|
||||
lifecycle,
|
||||
links,
|
||||
collaborators: { users: collaborators },
|
||||
};
|
||||
} else {
|
||||
@ -1110,6 +1125,7 @@ class FeatureToggleService {
|
||||
dependencies,
|
||||
children,
|
||||
lifecycle,
|
||||
links,
|
||||
collaborators: { users: collaborators },
|
||||
};
|
||||
}
|
||||
|
@ -248,6 +248,35 @@ export const featureSchema = {
|
||||
},
|
||||
},
|
||||
},
|
||||
links: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
required: ['id', 'url'],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
example: '01JTJNCJ5XVP2KPJFA03YRBZCA',
|
||||
description: 'The id of the link',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
example:
|
||||
'https://github.com/search?q=cleanupReminder&type=code',
|
||||
description: 'The URL the feature is linked to',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
example: 'Github cleanup',
|
||||
description: 'The description of the link',
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'The list of links. This is an experimental field and may change.',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
|
@ -16,6 +16,7 @@ import EventService from '../features/events/event-service';
|
||||
import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store';
|
||||
import type { DependentFeaturesService } from '../features/dependent-features/dependent-features-service';
|
||||
import type { IFeatureLifecycleReadModel } from '../features/feature-lifecycle/feature-lifecycle-read-model-type';
|
||||
import type { IFeatureLinksReadModel } from '../features/feature-links/feature-links-read-model-type';
|
||||
|
||||
test('Should only store events for potentially stale on', async () => {
|
||||
expect.assertions(2);
|
||||
@ -72,6 +73,7 @@ test('Should only store events for potentially stale on', async () => {
|
||||
{} as DependentFeaturesService,
|
||||
{} as IFeatureLifecycleReadModel,
|
||||
{} as IFeatureCollaboratorsReadModel,
|
||||
{} as IFeatureLinksReadModel,
|
||||
);
|
||||
|
||||
await featureToggleService.updatePotentiallyStaleFeatures();
|
||||
|
@ -160,6 +160,8 @@ import {
|
||||
} from '../features/context/createContextService';
|
||||
import { UniqueConnectionService } from '../features/unique-connection/unique-connection-service';
|
||||
import { createFakeFeatureLinkService } from '../features/feature-links/createFeatureLinkService';
|
||||
import { FeatureLinksReadModel } from '../features/feature-links/feature-links-read-model';
|
||||
import { FakeFeatureLinksReadModel } from '../features/feature-links/fake-feature-links-read-model';
|
||||
|
||||
export const createServices = (
|
||||
stores: IUnleashStores,
|
||||
@ -284,6 +286,10 @@ export const createServices = (
|
||||
? new FeatureCollaboratorsReadModel(db)
|
||||
: new FakeFeatureCollaboratorsReadModel();
|
||||
|
||||
const featureLinkReadModel = db
|
||||
? new FeatureLinksReadModel(db)
|
||||
: new FakeFeatureLinksReadModel();
|
||||
|
||||
const featureToggleService = new FeatureToggleService(
|
||||
stores,
|
||||
config,
|
||||
@ -296,6 +302,7 @@ export const createServices = (
|
||||
dependentFeaturesService,
|
||||
featureLifecycleReadModel,
|
||||
featureCollaboratorsReadModel,
|
||||
featureLinkReadModel,
|
||||
);
|
||||
const transactionalEnvironmentService = db
|
||||
? withTransactional(createEnvironmentService(config), db)
|
||||
|
@ -14,6 +14,7 @@ import type { IntegrationEventsService } from '../features/integration-events/in
|
||||
import type { IFlagResolver } from './experimental';
|
||||
import type { Collaborator } from '../features/feature-toggle/types/feature-collaborators-read-model-type';
|
||||
import type { EventEmitter } from 'events';
|
||||
import type { IFeatureLink } from '../features/feature-links/feature-links-read-model-type';
|
||||
|
||||
export type Operator = (typeof ALL_OPERATORS)[number];
|
||||
|
||||
@ -123,6 +124,7 @@ export interface FeatureToggleView extends FeatureToggleWithEnvironment {
|
||||
children: string[];
|
||||
lifecycle: IFeatureLifecycleStage | undefined;
|
||||
collaborators?: { users: Collaborator[] };
|
||||
links: IFeatureLink[];
|
||||
}
|
||||
|
||||
export interface IEnvironmentDetail extends IEnvironmentBase {
|
||||
|
Loading…
Reference in New Issue
Block a user