1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-14 00:19:16 +01:00
unleash.unleash/src/lib/services/strategy-service.ts
Nuno Góis 87d9497be9
refactor: prefer eventService.storeEvent methods (#4830)
https://linear.app/unleash/issue/2-1403/consider-refactoring-the-way-tags-are-fetched-for-the-events

This adds 2 methods to `EventService`:
 - `storeEvent`;
 - `storeEvents`;

This allows us to run event-specific logic inside these methods. In the
case of this PR, this means fetching the feature tags in case the event
contains a `featureName` and there are no tags specified in the event.

This prevents us from having to remember to fetch the tags in order to
store feature-related events except for very specific cases, like the
deletion of a feature - You can't fetch tags for a feature that no
longer exists, so in that case we need to pre-fetch the tags before
deleting the feature.

This also allows us to do any event-specific post-processing to the
event before reaching the DB layer.
In general I think it's also nicer that we reference the event service
instead of the event store directly.

There's a lot of changes and a lot of files touched, but most of it is
boilerplate to inject the `eventService` where needed instead of using
the `eventStore` directly.

Hopefully this will be a better approach than
https://github.com/Unleash/unleash/pull/4729

---------

Co-authored-by: Gastón Fournier <gaston@getunleash.io>
2023-09-27 14:23:05 +01:00

156 lines
4.7 KiB
TypeScript

import { Logger } from '../logger';
import { IUnleashConfig } from '../types/option';
import { IUnleashStores } from '../types/stores';
import {
IMinimalStrategy,
IStrategy,
IStrategyStore,
} from '../types/stores/strategy-store';
import NotFoundError from '../error/notfound-error';
import EventService from './event-service';
const strategySchema = require('./strategy-schema');
const NameExistsError = require('../error/name-exists-error');
const {
STRATEGY_CREATED,
STRATEGY_DELETED,
STRATEGY_DEPRECATED,
STRATEGY_REACTIVATED,
STRATEGY_UPDATED,
} = require('../types/events');
class StrategyService {
private logger: Logger;
private strategyStore: IStrategyStore;
private eventService: EventService;
constructor(
{ strategyStore }: Pick<IUnleashStores, 'strategyStore'>,
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
eventService: EventService,
) {
this.strategyStore = strategyStore;
this.eventService = eventService;
this.logger = getLogger('services/strategy-service.js');
}
async getStrategies(): Promise<IStrategy[]> {
return this.strategyStore.getAll();
}
async getStrategy(name: string): Promise<IStrategy> {
return this.strategyStore.get(name);
}
async removeStrategy(
strategyName: string,
userName: string,
): Promise<void> {
const strategy = await this.strategyStore.get(strategyName);
await this._validateEditable(strategy);
await this.strategyStore.delete(strategyName);
await this.eventService.storeEvent({
type: STRATEGY_DELETED,
createdBy: userName,
data: {
name: strategyName,
},
});
}
async deprecateStrategy(
strategyName: string,
userName: string,
): Promise<void> {
if (await this.strategyStore.exists(strategyName)) {
// Check existence
await this.strategyStore.deprecateStrategy({ name: strategyName });
await this.eventService.storeEvent({
type: STRATEGY_DEPRECATED,
createdBy: userName,
data: {
name: strategyName,
},
});
} else {
throw new NotFoundError(
`Could not find strategy with name ${strategyName}`,
);
}
}
async reactivateStrategy(
strategyName: string,
userName: string,
): Promise<void> {
await this.strategyStore.get(strategyName); // Check existence
await this.strategyStore.reactivateStrategy({ name: strategyName });
await this.eventService.storeEvent({
type: STRATEGY_REACTIVATED,
createdBy: userName,
data: {
name: strategyName,
},
});
}
async createStrategy(
value: IMinimalStrategy,
userName: string,
): Promise<IStrategy> {
const strategy = await strategySchema.validateAsync(value);
strategy.deprecated = false;
await this._validateStrategyName(strategy);
await this.strategyStore.createStrategy(strategy);
await this.eventService.storeEvent({
type: STRATEGY_CREATED,
createdBy: userName,
data: strategy,
});
return this.strategyStore.get(strategy.name);
}
async updateStrategy(
input: IMinimalStrategy,
userName: string,
): Promise<void> {
const value = await strategySchema.validateAsync(input);
const strategy = await this.strategyStore.get(input.name);
await this._validateEditable(strategy);
await this.strategyStore.updateStrategy(value);
await this.eventService.storeEvent({
type: STRATEGY_UPDATED,
createdBy: userName,
data: value,
});
}
private _validateStrategyName(
data: Pick<IStrategy, 'name'>,
): Promise<Pick<IStrategy, 'name'>> {
return new Promise((resolve, reject) => {
this.strategyStore
.get(data.name)
.then(() =>
reject(
new NameExistsError(
`Strategy with name ${data.name} already exist!`,
),
),
)
.catch(() => resolve(data));
});
}
// This check belongs in the store.
_validateEditable(strategy: IStrategy): void {
if (strategy.editable === false) {
throw new Error(`Cannot edit strategy ${strategy.name}`);
}
}
}
export default StrategyService;
module.exports = StrategyService;