mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-27 01:19:00 +02:00
feat: feature link backend stub (#9893)
This commit is contained in:
parent
1166d00e6d
commit
002233e7f6
@ -65,6 +65,7 @@ import { UserUnsubscribeStore } from '../features/user-subscriptions/user-unsubs
|
||||
import { UserSubscriptionsReadModel } from '../features/user-subscriptions/user-subscriptions-read-model';
|
||||
import { UniqueConnectionStore } from '../features/unique-connection/unique-connection-store';
|
||||
import { UniqueConnectionReadModel } from '../features/unique-connection/unique-connection-read-model';
|
||||
import FakeFeatureLinkStore from '../features/feature-links/fake-feature-link-store';
|
||||
|
||||
export const createStores = (
|
||||
config: IUnleashConfig,
|
||||
@ -201,6 +202,7 @@ export const createStores = (
|
||||
releasePlanMilestoneStore: new ReleasePlanMilestoneStore(db, config),
|
||||
releasePlanMilestoneStrategyStore:
|
||||
new ReleasePlanMilestoneStrategyStore(db, config),
|
||||
featureLinkStore: new FakeFeatureLinkStore(),
|
||||
};
|
||||
};
|
||||
|
||||
|
17
src/lib/features/feature-links/createFeatureLinkService.ts
Normal file
17
src/lib/features/feature-links/createFeatureLinkService.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import type { IUnleashConfig } from '../../types';
|
||||
import FeatureLinkService from './feature-link-service';
|
||||
import FakeFeatureLinkStore from './fake-feature-link-store';
|
||||
import { createFakeEventsService } from '../events/createEventsService';
|
||||
|
||||
export const createFakeFeatureLinkService = (config: IUnleashConfig) => {
|
||||
const eventService = createFakeEventsService(config);
|
||||
const featureLinkStore = new FakeFeatureLinkStore();
|
||||
|
||||
const featureLinkService = new FeatureLinkService(
|
||||
{ featureLinkStore },
|
||||
config,
|
||||
eventService,
|
||||
);
|
||||
|
||||
return { featureLinkService, featureLinkStore };
|
||||
};
|
53
src/lib/features/feature-links/fake-feature-link-store.ts
Normal file
53
src/lib/features/feature-links/fake-feature-link-store.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { NotFoundError } from '../../error';
|
||||
import type {
|
||||
IFeatureLink,
|
||||
IFeatureLinkStore,
|
||||
} from './feature-link-store-type';
|
||||
|
||||
export default class FakeFeatureLinkStore implements IFeatureLinkStore {
|
||||
private links: IFeatureLink[] = [];
|
||||
|
||||
async create(link: Omit<IFeatureLink, 'id'>): Promise<IFeatureLink> {
|
||||
const newLink: IFeatureLink = {
|
||||
...link,
|
||||
id: String(Math.random()),
|
||||
};
|
||||
this.links.push(newLink);
|
||||
return newLink;
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
const index = this.links.findIndex((link) => link.id === id);
|
||||
if (index !== -1) {
|
||||
this.links.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAll(): Promise<void> {
|
||||
this.links = [];
|
||||
}
|
||||
|
||||
destroy(): void {}
|
||||
|
||||
async exists(id: string): Promise<boolean> {
|
||||
return this.links.some((link) => link.id === id);
|
||||
}
|
||||
|
||||
async get(id: string): Promise<IFeatureLink> {
|
||||
const link = this.links.find((link) => link.id === id);
|
||||
if (link) {
|
||||
return link;
|
||||
}
|
||||
throw new NotFoundError('Could not find feature link');
|
||||
}
|
||||
|
||||
async getAll(): Promise<IFeatureLink[]> {
|
||||
return this.links;
|
||||
}
|
||||
|
||||
async update(link: IFeatureLink): Promise<IFeatureLink> {
|
||||
await this.delete(link.id);
|
||||
this.links.push(link);
|
||||
return link;
|
||||
}
|
||||
}
|
158
src/lib/features/feature-links/feature-link-controller.ts
Normal file
158
src/lib/features/feature-links/feature-link-controller.ts
Normal file
@ -0,0 +1,158 @@
|
||||
import type { Response } from 'express';
|
||||
import Controller from '../../routes/controller';
|
||||
import type { IAuthRequest } from '../../routes/unleash-types';
|
||||
import type { IUnleashConfig } from '../../types/option';
|
||||
import type { WithTransactional } from '../../db/transaction';
|
||||
import type FeatureLinkService from './feature-link-service';
|
||||
import { UPDATE_FEATURE } from '../../types/permissions';
|
||||
import type { OpenApiService } from '../../services/openapi-service';
|
||||
import {
|
||||
emptyResponse,
|
||||
getStandardResponses,
|
||||
} from '../../openapi/util/standard-responses';
|
||||
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
||||
import type { IFeatureLink } from './feature-link-store-type';
|
||||
|
||||
interface FeatureLinkServices {
|
||||
transactionalFeatureLinkService: WithTransactional<FeatureLinkService>;
|
||||
openApiService: OpenApiService;
|
||||
}
|
||||
|
||||
const PATH = '/:projectId/features/:featureName/link';
|
||||
const PATH_LINK = '/:projectId/features/:featureName/link/:linkId';
|
||||
|
||||
export default class FeatureLinkController extends Controller {
|
||||
private transactionalFeatureLinkService: WithTransactional<FeatureLinkService>;
|
||||
private openApiService: OpenApiService;
|
||||
|
||||
constructor(
|
||||
config: IUnleashConfig,
|
||||
{
|
||||
transactionalFeatureLinkService,
|
||||
openApiService,
|
||||
}: FeatureLinkServices,
|
||||
) {
|
||||
super(config);
|
||||
this.transactionalFeatureLinkService = transactionalFeatureLinkService;
|
||||
this.openApiService = openApiService;
|
||||
|
||||
this.route({
|
||||
method: 'post',
|
||||
path: PATH,
|
||||
handler: this.createFeatureLink,
|
||||
permission: UPDATE_FEATURE,
|
||||
middleware: [
|
||||
openApiService.validPath({
|
||||
tags: ['Unstable'],
|
||||
operationId: 'createFeatureLink',
|
||||
summary: 'Create a feature link',
|
||||
description: 'Create a new link for a feature.',
|
||||
responses: {
|
||||
204: emptyResponse,
|
||||
...getStandardResponses(400, 401, 403, 415),
|
||||
},
|
||||
requestBody: createRequestSchema('featureLinkSchema'),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
this.route({
|
||||
method: 'put',
|
||||
path: PATH_LINK,
|
||||
handler: this.updateFeatureLink,
|
||||
permission: UPDATE_FEATURE,
|
||||
middleware: [
|
||||
openApiService.validPath({
|
||||
tags: ['Unstable'],
|
||||
operationId: 'updateFeatureLink',
|
||||
summary: 'Update a feature link',
|
||||
description: 'Update an existing feature link.',
|
||||
responses: {
|
||||
204: emptyResponse,
|
||||
...getStandardResponses(400, 401, 403, 404, 415),
|
||||
},
|
||||
requestBody: createRequestSchema('featureLinkSchema'),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
this.route({
|
||||
method: 'delete',
|
||||
path: PATH_LINK,
|
||||
handler: this.deleteFeatureLink,
|
||||
acceptAnyContentType: true,
|
||||
permission: UPDATE_FEATURE,
|
||||
middleware: [
|
||||
openApiService.validPath({
|
||||
tags: ['Unstable'],
|
||||
operationId: 'deleteFeatureLink',
|
||||
summary: 'Delete a feature link',
|
||||
description: 'Delete a feature link by id.',
|
||||
responses: {
|
||||
204: emptyResponse,
|
||||
...getStandardResponses(401, 403, 404),
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async createFeatureLink(
|
||||
req: IAuthRequest<
|
||||
{ projectId: string; featureName: string },
|
||||
unknown,
|
||||
Omit<IFeatureLink, 'id' | 'createdAt'>
|
||||
>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { projectId, featureName } = req.params;
|
||||
|
||||
await this.transactionalFeatureLinkService.transactional((service) =>
|
||||
service.createLink(
|
||||
projectId,
|
||||
{ ...req.body, featureName },
|
||||
req.audit,
|
||||
),
|
||||
);
|
||||
|
||||
res.status(204).end();
|
||||
}
|
||||
|
||||
async updateFeatureLink(
|
||||
req: IAuthRequest<
|
||||
{ projectId: string; linkId: string; featureName: string },
|
||||
unknown,
|
||||
Omit<IFeatureLink, 'id'>
|
||||
>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { projectId, linkId, featureName } = req.params;
|
||||
|
||||
await this.transactionalFeatureLinkService.transactional((service) =>
|
||||
service.updateLink(
|
||||
{ projectId, linkId },
|
||||
{ ...req.body, featureName },
|
||||
req.audit,
|
||||
),
|
||||
);
|
||||
|
||||
res.status(204).end();
|
||||
}
|
||||
|
||||
async deleteFeatureLink(
|
||||
req: IAuthRequest<
|
||||
{ projectId: string; linkId: string },
|
||||
unknown,
|
||||
Omit<IFeatureLink, 'id'>
|
||||
>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { projectId, linkId } = req.params;
|
||||
|
||||
await this.transactionalFeatureLinkService.transactional((service) =>
|
||||
service.deleteLink({ projectId, linkId }, req.audit),
|
||||
);
|
||||
|
||||
res.status(204).end();
|
||||
}
|
||||
}
|
60
src/lib/features/feature-links/feature-link-service.test.ts
Normal file
60
src/lib/features/feature-links/feature-link-service.test.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { createFakeFeatureLinkService } from './createFeatureLinkService';
|
||||
import type { IAuditUser, IUnleashConfig } from '../../types';
|
||||
import getLogger from '../../../test/fixtures/no-logger';
|
||||
import { NotFoundError } from '../../error';
|
||||
|
||||
test('create, update and delete feature link', async () => {
|
||||
const { featureLinkStore, featureLinkService } =
|
||||
createFakeFeatureLinkService({
|
||||
getLogger,
|
||||
} as unknown as IUnleashConfig);
|
||||
|
||||
const link = await featureLinkService.createLink(
|
||||
'default',
|
||||
{ featureName: 'feature', url: 'example.com', title: 'some title' },
|
||||
{} as IAuditUser,
|
||||
);
|
||||
expect(link).toMatchObject({
|
||||
featureName: 'feature',
|
||||
url: 'example.com',
|
||||
title: 'some title',
|
||||
});
|
||||
|
||||
const newLink = await featureLinkService.updateLink(
|
||||
{ projectId: 'default', linkId: link.id },
|
||||
{ title: 'new title', url: 'example1.com', featureName: 'feature' },
|
||||
{} as IAuditUser,
|
||||
);
|
||||
expect(newLink).toMatchObject({
|
||||
featureName: 'feature',
|
||||
url: 'example1.com',
|
||||
title: 'new title',
|
||||
});
|
||||
|
||||
await featureLinkService.deleteLink(
|
||||
{ projectId: 'default', linkId: link.id },
|
||||
{} as IAuditUser,
|
||||
);
|
||||
expect(await featureLinkStore.getAll()).toMatchObject([]);
|
||||
});
|
||||
|
||||
test('cannot delete/update non existent link', async () => {
|
||||
const { featureLinkStore, featureLinkService } =
|
||||
createFakeFeatureLinkService({
|
||||
getLogger,
|
||||
} as unknown as IUnleashConfig);
|
||||
|
||||
await expect(
|
||||
featureLinkService.updateLink(
|
||||
{ projectId: 'default', linkId: 'nonexitent' },
|
||||
{ title: 'new title', url: 'example1.com', featureName: 'feature' },
|
||||
{} as IAuditUser,
|
||||
),
|
||||
).rejects.toThrow(NotFoundError);
|
||||
await expect(
|
||||
featureLinkService.deleteLink(
|
||||
{ projectId: 'default', linkId: 'nonexitent' },
|
||||
{} as IAuditUser,
|
||||
),
|
||||
).rejects.toThrow(NotFoundError);
|
||||
});
|
108
src/lib/features/feature-links/feature-link-service.ts
Normal file
108
src/lib/features/feature-links/feature-link-service.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import type { Logger } from '../../logger';
|
||||
import {
|
||||
type IUnleashConfig,
|
||||
type IAuditUser,
|
||||
FeatureLinkAddedEvent,
|
||||
FeatureLinkUpdatedEvent,
|
||||
FeatureLinkRemovedEvent,
|
||||
} from '../../types';
|
||||
import type {
|
||||
IFeatureLink,
|
||||
IFeatureLinkStore,
|
||||
} from './feature-link-store-type';
|
||||
import type EventService from '../events/event-service';
|
||||
import { NotFoundError } from '../../error';
|
||||
|
||||
interface IFeatureLinkStoreObj {
|
||||
featureLinkStore: IFeatureLinkStore;
|
||||
}
|
||||
|
||||
export default class FeatureLinkService {
|
||||
private logger: Logger;
|
||||
private featureLinkStore: IFeatureLinkStore;
|
||||
private eventService: EventService;
|
||||
|
||||
constructor(
|
||||
stores: IFeatureLinkStoreObj,
|
||||
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
||||
eventService: EventService,
|
||||
) {
|
||||
this.logger = getLogger('feature-links/feature-link-service.ts');
|
||||
this.featureLinkStore = stores.featureLinkStore;
|
||||
this.eventService = eventService;
|
||||
}
|
||||
|
||||
async getAll(): Promise<IFeatureLink[]> {
|
||||
return this.featureLinkStore.getAll();
|
||||
}
|
||||
|
||||
async createLink(
|
||||
projectId: string,
|
||||
newLink: Omit<IFeatureLink, 'id'>,
|
||||
auditUser: IAuditUser,
|
||||
): Promise<IFeatureLink> {
|
||||
const link = await this.featureLinkStore.create(newLink);
|
||||
|
||||
await this.eventService.storeEvent(
|
||||
new FeatureLinkAddedEvent({
|
||||
featureName: newLink.featureName,
|
||||
project: projectId,
|
||||
data: { url: newLink.url, title: newLink.title },
|
||||
auditUser,
|
||||
}),
|
||||
);
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
async updateLink(
|
||||
{ projectId, linkId }: { projectId: string; linkId: string },
|
||||
updatedLink: Omit<IFeatureLink, 'id'>,
|
||||
auditUser: IAuditUser,
|
||||
): Promise<IFeatureLink> {
|
||||
const preData = await this.featureLinkStore.get(linkId);
|
||||
|
||||
if (!preData) {
|
||||
throw new NotFoundError(`Could not find link with id ${linkId}`);
|
||||
}
|
||||
|
||||
const link = await this.featureLinkStore.update({
|
||||
...updatedLink,
|
||||
id: linkId,
|
||||
});
|
||||
|
||||
await this.eventService.storeEvent(
|
||||
new FeatureLinkUpdatedEvent({
|
||||
featureName: updatedLink.featureName,
|
||||
project: projectId,
|
||||
data: { url: link.url, title: link.title },
|
||||
preData: { url: preData.url, title: preData.title },
|
||||
auditUser,
|
||||
}),
|
||||
);
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
async deleteLink(
|
||||
{ projectId, linkId }: { projectId: string; linkId: string },
|
||||
auditUser: IAuditUser,
|
||||
): Promise<void> {
|
||||
const link = await this.featureLinkStore.get(linkId);
|
||||
|
||||
if (!link) {
|
||||
throw new NotFoundError(`Could not find link with id ${linkId}`);
|
||||
}
|
||||
|
||||
await this.featureLinkStore.delete(linkId);
|
||||
|
||||
await this.eventService.storeEvent(
|
||||
new FeatureLinkRemovedEvent({
|
||||
featureName: link.featureName,
|
||||
project: projectId,
|
||||
preData: { url: link.url, title: link.title },
|
||||
auditUser,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
13
src/lib/features/feature-links/feature-link-store-type.ts
Normal file
13
src/lib/features/feature-links/feature-link-store-type.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import type { Store } from '../../types/stores/store';
|
||||
|
||||
export interface IFeatureLink {
|
||||
id: string;
|
||||
featureName: string;
|
||||
url: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface IFeatureLinkStore extends Store<IFeatureLink, string> {
|
||||
create(link: Omit<IFeatureLink, 'id'>): Promise<IFeatureLink>;
|
||||
update(link: IFeatureLink): Promise<IFeatureLink>;
|
||||
}
|
63
src/lib/features/feature-links/feature-link.e2e.test.ts
Normal file
63
src/lib/features/feature-links/feature-link.e2e.test.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import {
|
||||
type IUnleashTest,
|
||||
setupAppWithAuth,
|
||||
} from '../../../test/e2e/helpers/test-helper';
|
||||
import dbInit, { type ITestDb } from '../../../test/e2e/helpers/database-init';
|
||||
import type { IEventStore, IFeatureLinkStore } from '../../types';
|
||||
import getLogger from '../../../test/fixtures/no-logger';
|
||||
import type { FeatureLinkSchema } from '../../openapi/spec/feature-link-schema';
|
||||
|
||||
let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
let featureLinkStore: IFeatureLinkStore;
|
||||
let eventStore: IEventStore;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_link', getLogger, {
|
||||
dbInitMethod: 'legacy' as const,
|
||||
});
|
||||
app = await setupAppWithAuth(
|
||||
db.stores,
|
||||
{
|
||||
experimental: {
|
||||
flags: {},
|
||||
},
|
||||
},
|
||||
db.rawDatabase,
|
||||
);
|
||||
eventStore = db.stores.eventStore;
|
||||
featureLinkStore = db.stores.featureLinkStore;
|
||||
|
||||
await app.request
|
||||
.post(`/auth/demo/login`)
|
||||
.send({
|
||||
email: 'user@getunleash.io',
|
||||
})
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.destroy();
|
||||
await db.destroy();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await featureLinkStore.deleteAll();
|
||||
});
|
||||
|
||||
const addLink = async (
|
||||
featureName: string,
|
||||
link: FeatureLinkSchema,
|
||||
expectedCode = 204,
|
||||
) => {
|
||||
return app.request
|
||||
.post(`/api/admin/projects/default/features/${featureName}/link`)
|
||||
.send(link)
|
||||
.expect(expectedCode);
|
||||
};
|
||||
|
||||
test('should add feature links', async () => {
|
||||
await app.createFeature('my_feature');
|
||||
|
||||
await addLink('my_feature', { url: 'example.com', title: 'feature link' });
|
||||
});
|
@ -14,3 +14,4 @@ export * from './playground/createPlaygroundService';
|
||||
export * from './personal-dashboard/createPersonalDashboardService';
|
||||
export * from './user-subscriptions/createUserSubscriptionsService';
|
||||
export * from './context/createContextService';
|
||||
export * from './feature-links/createFeatureLinkService';
|
||||
|
@ -49,6 +49,7 @@ import {
|
||||
type ProjectFlagCreatorsSchema,
|
||||
} from '../../openapi/spec/project-flag-creators-schema';
|
||||
import ProjectStatusController from '../project-status/project-status-controller';
|
||||
import FeatureLinkController from '../feature-links/feature-link-controller';
|
||||
|
||||
export default class ProjectController extends Controller {
|
||||
private projectService: ProjectService;
|
||||
@ -248,6 +249,7 @@ export default class ProjectController extends Controller {
|
||||
this.use('/', new ProjectInsightsController(config, services).router);
|
||||
this.use('/', new ProjectStatusController(config, services).router);
|
||||
this.use('/', new FeatureLifecycleController(config, services).router);
|
||||
this.use('/', new FeatureLinkController(config, services).router);
|
||||
}
|
||||
|
||||
async getProjects(
|
||||
|
26
src/lib/openapi/spec/feature-link-schema.ts
Normal file
26
src/lib/openapi/spec/feature-link-schema.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import type { FromSchema } from 'json-schema-to-ts';
|
||||
|
||||
export const featureLinkSchema = {
|
||||
$id: '#/components/schemas/featureLinkSchema',
|
||||
type: 'object',
|
||||
required: ['url'],
|
||||
properties: {
|
||||
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 link to any URL related to the feature',
|
||||
components: {
|
||||
schemas: {},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type FeatureLinkSchema = FromSchema<typeof featureLinkSchema>;
|
@ -85,6 +85,7 @@ export * from './feature-events-schema';
|
||||
export * from './feature-lifecycle-completed-schema';
|
||||
export * from './feature-lifecycle-count-schema';
|
||||
export * from './feature-lifecycle-schema';
|
||||
export * from './feature-link-schema';
|
||||
export * from './feature-metrics-schema';
|
||||
export * from './feature-schema';
|
||||
export * from './feature-search-environment-schema';
|
||||
|
@ -158,6 +158,7 @@ import {
|
||||
createFakeContextService,
|
||||
} from '../features/context/createContextService';
|
||||
import { UniqueConnectionService } from '../features/unique-connection/unique-connection-service';
|
||||
import { createFakeFeatureLinkService } from '../features/feature-links/createFeatureLinkService';
|
||||
|
||||
export const createServices = (
|
||||
stores: IUnleashStores,
|
||||
@ -424,6 +425,10 @@ export const createServices = (
|
||||
? withTransactional(createUserSubscriptionsService(config), db)
|
||||
: withFakeTransactional(createFakeUserSubscriptionsService(config));
|
||||
|
||||
const transactionalFeatureLinkService = withFakeTransactional(
|
||||
createFakeFeatureLinkService(config).featureLinkService,
|
||||
);
|
||||
|
||||
return {
|
||||
transactionalAccessService,
|
||||
accessService,
|
||||
@ -493,6 +498,7 @@ export const createServices = (
|
||||
transactionalUserSubscriptionsService,
|
||||
uniqueConnectionService,
|
||||
featureLifecycleReadModel,
|
||||
transactionalFeatureLinkService,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -18,6 +18,9 @@ export const APPLICATION_CREATED = 'application-created' as const;
|
||||
export const FEATURE_CREATED = 'feature-created' as const;
|
||||
export const FEATURE_DELETED = 'feature-deleted' as const;
|
||||
export const FEATURE_UPDATED = 'feature-updated' as const;
|
||||
export const FEATURE_LINK_ADDED = 'feature-link-added' as const;
|
||||
export const FEATURE_LINK_REMOVED = 'feature-link-removed' as const;
|
||||
export const FEATURE_LINK_UPDATED = 'feature-link-updated' as const;
|
||||
export const FEATURE_DEPENDENCY_ADDED = 'feature-dependency-added' as const;
|
||||
export const FEATURE_DEPENDENCY_REMOVED = 'feature-dependency-removed' as const;
|
||||
export const FEATURE_DEPENDENCIES_REMOVED =
|
||||
@ -242,6 +245,9 @@ export const IEventTypes = [
|
||||
FEATURE_TYPE_UPDATED,
|
||||
FEATURE_COMPLETED,
|
||||
FEATURE_UNCOMPLETED,
|
||||
FEATURE_LINK_ADDED,
|
||||
FEATURE_LINK_REMOVED,
|
||||
FEATURE_LINK_UPDATED,
|
||||
STRATEGY_ORDER_CHANGED,
|
||||
DROP_FEATURE_TAGS,
|
||||
FEATURE_UNTAGGED,
|
||||
@ -1023,6 +1029,77 @@ export class FeatureMetadataUpdateEvent extends BaseEvent {
|
||||
}
|
||||
}
|
||||
|
||||
export class FeatureLinkAddedEvent extends BaseEvent {
|
||||
readonly project: string;
|
||||
|
||||
readonly featureName: string;
|
||||
|
||||
readonly data: { url: string; title?: string };
|
||||
readonly preData: null;
|
||||
|
||||
constructor(p: {
|
||||
featureName: string;
|
||||
project: string;
|
||||
data: { url: string; title?: string };
|
||||
auditUser: IAuditUser;
|
||||
}) {
|
||||
super(FEATURE_LINK_ADDED, p.auditUser);
|
||||
const { project, featureName, data } = p;
|
||||
this.project = project;
|
||||
this.featureName = featureName;
|
||||
this.data = data;
|
||||
this.preData = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class FeatureLinkUpdatedEvent extends BaseEvent {
|
||||
readonly project: string;
|
||||
|
||||
readonly featureName: string;
|
||||
|
||||
readonly data: { url: string; title?: string };
|
||||
|
||||
readonly preData: { url: string; title?: string };
|
||||
|
||||
constructor(p: {
|
||||
featureName: string;
|
||||
project: string;
|
||||
data: { url: string; title?: string };
|
||||
preData: { url: string; title?: string };
|
||||
auditUser: IAuditUser;
|
||||
}) {
|
||||
super(FEATURE_LINK_UPDATED, p.auditUser);
|
||||
const { project, featureName, data, preData } = p;
|
||||
this.project = project;
|
||||
this.featureName = featureName;
|
||||
this.data = data;
|
||||
this.preData = preData;
|
||||
}
|
||||
}
|
||||
|
||||
export class FeatureLinkRemovedEvent extends BaseEvent {
|
||||
readonly project: string;
|
||||
|
||||
readonly featureName: string;
|
||||
|
||||
readonly preData: { url: string; title?: string };
|
||||
readonly data: null;
|
||||
|
||||
constructor(p: {
|
||||
featureName: string;
|
||||
project: string;
|
||||
preData: { url: string; title?: string };
|
||||
auditUser: IAuditUser;
|
||||
}) {
|
||||
super(FEATURE_LINK_REMOVED, p.auditUser);
|
||||
const { project, featureName, preData } = p;
|
||||
this.project = project;
|
||||
this.featureName = featureName;
|
||||
this.data = null;
|
||||
this.preData = preData;
|
||||
}
|
||||
}
|
||||
|
||||
export class FeatureStrategyAddEvent extends BaseEvent {
|
||||
readonly project: string;
|
||||
|
||||
|
@ -61,6 +61,7 @@ import type { ProjectStatusService } from '../features/project-status/project-st
|
||||
import type { UserSubscriptionsService } from '../features/user-subscriptions/user-subscriptions-service';
|
||||
import type { UniqueConnectionService } from '../features/unique-connection/unique-connection-service';
|
||||
import type { IFeatureLifecycleReadModel } from '../features/feature-lifecycle/feature-lifecycle-read-model-type';
|
||||
import type FeatureLinkService from '../features/feature-links/feature-link-service';
|
||||
|
||||
export interface IUnleashServices {
|
||||
transactionalAccessService: WithTransactional<AccessService>;
|
||||
@ -133,4 +134,5 @@ export interface IUnleashServices {
|
||||
transactionalUserSubscriptionsService: WithTransactional<UserSubscriptionsService>;
|
||||
uniqueConnectionService: UniqueConnectionService;
|
||||
featureLifecycleReadModel: IFeatureLifecycleReadModel;
|
||||
transactionalFeatureLinkService: WithTransactional<FeatureLinkService>;
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ import { ReleasePlanStore } from '../features/release-plans/release-plan-store';
|
||||
import { ReleasePlanTemplateStore } from '../features/release-plans/release-plan-template-store';
|
||||
import { ReleasePlanMilestoneStore } from '../features/release-plans/release-plan-milestone-store';
|
||||
import { ReleasePlanMilestoneStrategyStore } from '../features/release-plans/release-plan-milestone-strategy-store';
|
||||
import type { IFeatureLinkStore } from '../features/feature-links/feature-link-store-type';
|
||||
|
||||
export interface IUnleashStores {
|
||||
accessStore: IAccessStore;
|
||||
@ -122,6 +123,7 @@ export interface IUnleashStores {
|
||||
releasePlanTemplateStore: ReleasePlanTemplateStore;
|
||||
releasePlanMilestoneStore: ReleasePlanMilestoneStore;
|
||||
releasePlanMilestoneStrategyStore: ReleasePlanMilestoneStrategyStore;
|
||||
featureLinkStore: IFeatureLinkStore;
|
||||
}
|
||||
|
||||
export {
|
||||
@ -183,4 +185,5 @@ export {
|
||||
ReleasePlanTemplateStore,
|
||||
ReleasePlanMilestoneStore,
|
||||
ReleasePlanMilestoneStrategyStore,
|
||||
type IFeatureLinkStore,
|
||||
};
|
||||
|
2
src/test/fixtures/store.ts
vendored
2
src/test/fixtures/store.ts
vendored
@ -62,6 +62,7 @@ import { FakeUserUnsubscribeStore } from '../../lib/features/user-subscriptions/
|
||||
import { FakeUserSubscriptionsReadModel } from '../../lib/features/user-subscriptions/fake-user-subscriptions-read-model';
|
||||
import { FakeUniqueConnectionStore } from '../../lib/features/unique-connection/fake-unique-connection-store';
|
||||
import { UniqueConnectionReadModel } from '../../lib/features/unique-connection/unique-connection-read-model';
|
||||
import FakeFeatureLinkStore from '../../lib/features/feature-links/fake-feature-link-store';
|
||||
|
||||
const db = {
|
||||
select: () => ({
|
||||
@ -138,6 +139,7 @@ const createStores: () => IUnleashStores = () => {
|
||||
releasePlanTemplateStore: {} as ReleasePlanTemplateStore,
|
||||
releasePlanMilestoneStrategyStore:
|
||||
{} as ReleasePlanMilestoneStrategyStore,
|
||||
featureLinkStore: new FakeFeatureLinkStore(),
|
||||
};
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user