1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: feature type updated audit log (#5415)

This commit is contained in:
Mateusz Kwasniewski 2023-11-24 14:24:31 +01:00 committed by GitHub
parent d680e50055
commit 2e96ace14e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 5 deletions

View File

@ -41,7 +41,7 @@ class FeatureTypeStore implements IFeatureTypeStore {
async get(id: string): Promise<IFeatureType> {
const row = await this.db(TABLE).where({ id }).first();
return this.rowToFeatureType(row);
return row ? this.rowToFeatureType(row) : row;
}
async getByName(name: string): Promise<IFeatureType> {

View File

@ -116,6 +116,7 @@ When a feature toggle type's expected lifetime is changed, this will also cause
const result = await this.featureTypeService.updateLifetime(
req.params.id.toLowerCase(),
req.body.lifetimeDays,
req.user,
);
this.openApiService.respondWithValidation(

View File

@ -6,18 +6,25 @@ import {
IFeatureTypeStore,
} from '../types/stores/feature-type-store';
import NotFoundError from '../error/notfound-error';
import EventService from './event-service';
import { FEATURE_FAVORITED, FEATURE_TYPE_UPDATED, IUser } from '../types';
import { extractUsernameFromUser } from '../util';
export default class FeatureTypeService {
private featureTypeStore: IFeatureTypeStore;
private eventService: EventService;
private logger: Logger;
constructor(
{ featureTypeStore }: Pick<IUnleashStores, 'featureTypeStore'>,
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
eventService: EventService,
) {
this.featureTypeStore = featureTypeStore;
this.logger = getLogger('services/feature-type-service.ts');
this.eventService = eventService;
}
async getAll(): Promise<IFeatureType[]> {
@ -27,23 +34,33 @@ export default class FeatureTypeService {
async updateLifetime(
id: string,
newLifetimeDays: number | null,
user: IUser,
): Promise<IFeatureType> {
// because our OpenAPI library does type coercion, any `null` values you
// pass in get converted to `0`.
const translatedLifetime =
newLifetimeDays === 0 ? null : newLifetimeDays;
const featureType = await this.featureTypeStore.get(id);
const result = await this.featureTypeStore.updateLifetime(
id,
translatedLifetime,
);
if (!result) {
if (!featureType || !result) {
throw new NotFoundError(
`The feature type you tried to update ("${id}") does not exist.`,
);
}
await this.eventService.storeEvent({
type: FEATURE_TYPE_UPDATED,
createdBy: extractUsernameFromUser(user),
data: { ...featureType, lifetimeDays: translatedLifetime },
preData: featureType,
});
return result;
}
}

View File

@ -135,7 +135,11 @@ export const createServices = (
privateProjectChecker,
);
const emailService = new EmailService(config.email, config.getLogger);
const featureTypeService = new FeatureTypeService(stores, config);
const featureTypeService = new FeatureTypeService(
stores,
config,
eventService,
);
const resetTokenService = new ResetTokenService(stores, config);
const stateService = new StateService(stores, config, eventService);
const strategyService = new StrategyService(stores, config, eventService);

View File

@ -47,6 +47,7 @@ export const CONTEXT_FIELD_CREATED = 'context-field-created' as const;
export const CONTEXT_FIELD_UPDATED = 'context-field-updated' as const;
export const CONTEXT_FIELD_DELETED = 'context-field-deleted' as const;
export const PROJECT_ACCESS_ADDED = 'project-access-added' as const;
export const FEATURE_TYPE_UPDATED = 'feature-type-updated' as const;
export const PROJECT_ACCESS_USER_ROLES_UPDATED =
'project-access-user-roles-updated';
@ -178,6 +179,7 @@ export const IEventTypes = [
FEATURE_STRATEGY_UPDATE,
FEATURE_STRATEGY_ADD,
FEATURE_STRATEGY_REMOVE,
FEATURE_TYPE_UPDATED,
STRATEGY_ORDER_CHANGED,
DROP_FEATURE_TAGS,
FEATURE_UNTAGGED,

View File

@ -1,10 +1,13 @@
import dbInit from '../../helpers/database-init';
import getLogger from '../../../fixtures/no-logger';
import { setupAppWithCustomConfig } from '../../helpers/test-helper';
import {
IUnleashTest,
setupAppWithCustomConfig,
} from '../../helpers/test-helper';
import { validateSchema } from '../../../../lib/openapi/validate';
import { featureTypesSchema } from '../../../../lib/openapi/spec/feature-types-schema';
let app;
let app: IUnleashTest;
let db;
beforeAll(async () => {
@ -68,6 +71,11 @@ describe('updating lifetimes', () => {
};
expect(await setLifetime(0)).toMatchObject(await setLifetime(null));
const { body } = await app.getRecordedEvents();
expect(body.events[0]).toMatchObject({
data: { id: 'release', lifetimeDays: null },
});
});
test('the :id parameter is not case sensitive', async () => {
const lifetimeDays = 45;

View File

@ -97,6 +97,8 @@ export interface IUnleashHttpAPI {
tag: { type: string; value: string },
expectedResponseCode?: number,
): supertest.Test;
getRecordedEvents(): supertest.Test;
}
function httpApis(
@ -258,6 +260,17 @@ function httpApis(
.set('Content-Type', 'application/json')
.expect(expectedResponseCode);
},
getRecordedEvents(
project: string | null = null,
expectedResponseCode: number = 200,
): supertest.Test {
return request
.post('/api/admin/events/search')
.send({ project, query: '', limit: 50, offset: 0 })
.set('Content-Type', 'application/json')
.expect(expectedResponseCode);
},
};
}