1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-26 13:48:33 +02:00

chore!: remove deprecated POST events search endpoint (#10030)

https://linear.app/unleash/issue/2-3368/remove-post-apiadmineventssearch-deprecated-in-610

Removes POST `/api/admin/events/search` which was deprecated in v6.1.
Also cleans up related code.
This commit is contained in:
Nuno Góis 2025-05-27 09:12:36 +01:00 committed by GitHub
parent 45434109a9
commit 290ef6ca40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 129 additions and 182 deletions

View File

@ -2,17 +2,21 @@ import dbInit, {
type ITestDb,
} from '../../../test/e2e/helpers/database-init.js';
import {
createUserWithRootRole,
type IUnleashTest,
setupAppWithCustomConfig,
setupAppWithAuth,
} from '../../../test/e2e/helpers/test-helper.js';
import getLogger from '../../../test/fixtures/no-logger.js';
import { RoleName } from '../../types/index.js';
let app: IUnleashTest;
let db: ITestDb;
const adminEmail = 'admin-user@getunleash.io';
beforeAll(async () => {
db = await dbInit('tag_types_api_serial', getLogger);
app = await setupAppWithCustomConfig(
app = await setupAppWithAuth(
db.stores,
{
experimental: {
@ -23,6 +27,17 @@ beforeAll(async () => {
},
db.rawDatabase,
);
await createUserWithRootRole({
app,
stores: db.stores,
email: adminEmail,
roleName: RoleName.ADMIN,
});
});
beforeEach(async () => {
await app.login({ email: adminEmail });
});
afterAll(async () => {

View File

@ -18,8 +18,6 @@ import {
type FeatureEventsSchema,
} from '../../../lib/openapi/spec/feature-events-schema.js';
import { getStandardResponses } from '../../../lib/openapi/util/standard-responses.js';
import { createRequestSchema } from '../../openapi/util/create-request-schema.js';
import type { DeprecatedSearchEventsSchema } from '../../openapi/spec/deprecated-search-events-schema.js';
import type { IFlagResolver } from '../../types/experimental.js';
import type { IAuthRequest } from '../unleash-types.js';
import {
@ -100,27 +98,6 @@ export default class EventController extends Controller {
],
});
this.route({
method: 'post',
path: '/events/search',
handler: this.deprecatedSearchEvents,
permission: NONE,
middleware: [
openApiService.validPath({
operationId: 'deprecatedSearchEvents',
tags: ['Events'],
deprecated: true,
summary: 'Search for events (deprecated)',
description:
'Allows searching for events matching the search criteria in the request body',
requestBody: createRequestSchema(
'deprecatedSearchEventsSchema',
),
responses: { 200: createResponseSchema('eventsSchema') },
}),
],
});
this.route({
method: 'get',
path: '/event-creators',
@ -201,28 +178,6 @@ export default class EventController extends Controller {
);
}
async deprecatedSearchEvents(
req: Request<unknown, unknown, DeprecatedSearchEventsSchema>,
res: Response<EventsSchema>,
): Promise<void> {
const eventList = await this.eventService.deprecatedSearchEvents(
req.body,
);
const response = {
version,
events: serializeDates(this.maybeAnonymiseEvents(eventList.events)),
totalEvents: eventList.totalEvents,
};
this.openApiService.respondWithValidation(
200,
res,
featureEventsSchema.$id,
response,
);
}
async getEventCreators(
req: IAuthRequest,
res: Response<ProjectFlagCreatorsSchema>,

View File

@ -6,66 +6,26 @@ import {
type IUnleashStores,
RoleName,
} from '../../../../lib/types/index.js';
import type {
AccessService,
EventService,
} from '../../../../lib/services/index.js';
import type { EventService } from '../../../../lib/services/index.js';
import getLogger from '../../../fixtures/no-logger.js';
import {
createUserWithRootRole,
type IUnleashTest,
setupAppWithAuth,
} from '../../helpers/test-helper.js';
import { createEventsService } from '../../../../lib/features/index.js';
import { createTestConfig } from '../../../config/test-config.js';
import type { IRole } from '../../../../lib/types/stores/access-store.js';
import { FEATURE_CREATED, USER_CREATED } from '../../../../lib/events/index.js';
let app: IUnleashTest;
let db: ITestDb;
let eventService: EventService;
const TEST_USER_ID = -9999;
const regularUserName = 'import-user';
const adminUserName = 'admin-user';
const regularEmail = 'import-user@getunleash.io';
const adminEmail = 'admin-user@getunleash.io';
const config: IUnleashConfig = createTestConfig();
let adminRole: IRole;
let stores: IUnleashStores;
let accessService: AccessService;
const loginRegularUser = () =>
app.request
.post(`/auth/demo/login`)
.send({
email: `${regularUserName}@getunleash.io`,
})
.expect(200);
const loginAdminUser = () =>
app.request
.post(`/auth/demo/login`)
.send({
email: `${adminUserName}@getunleash.io`,
})
.expect(200);
const createUserEditorAccess = async (name, email) => {
const { userStore } = stores;
const user = await userStore.insert({
name,
email,
});
return user;
};
const createUserAdminAccess = async (name, email) => {
const { userStore } = stores;
const user = await userStore.insert({
name,
email,
});
await accessService.addUserToRole(user.id, adminRole.id, 'default');
return user;
};
beforeAll(async () => {
db = await dbInit('event_search', getLogger);
@ -84,19 +44,18 @@ beforeAll(async () => {
eventService = createEventsService(db.rawDatabase, config);
accessService = app.services.accessService;
await createUserWithRootRole({
app,
stores,
email: regularEmail,
});
const roles = await accessService.getRootRoles();
adminRole = roles.find((role) => role.name === RoleName.ADMIN)!;
await createUserEditorAccess(
regularUserName,
`${regularUserName}@getunleash.io`,
);
await createUserAdminAccess(
adminUserName,
`${adminUserName}@getunleash.io`,
);
await createUserWithRootRole({
app,
stores,
email: adminEmail,
roleName: RoleName.ADMIN,
});
});
afterAll(async () => {
@ -105,7 +64,7 @@ afterAll(async () => {
});
beforeEach(async () => {
await loginAdminUser();
await app.login({ email: adminEmail });
await db.stores.featureToggleStore.deleteAll();
await db.stores.segmentStore.deleteAll();
await db.stores.eventStore.deleteAll();
@ -522,7 +481,7 @@ test('should filter events by project using IS_ANY_OF', async () => {
});
test('should not show user creation events for non-admins', async () => {
await loginRegularUser();
await app.login({ email: regularEmail });
await eventService.storeEvent({
type: USER_CREATED,
createdBy: 'test-user',

View File

@ -90,75 +90,6 @@ test('Can filter by project', async () => {
});
});
test('can search for events', async () => {
const events: IBaseEvent[] = [
{
type: FEATURE_CREATED,
project: randomId(),
data: { id: randomId() },
tags: [],
createdBy: randomId(),
createdByUserId: TEST_USER_ID,
ip: '127.0.0.1',
},
{
type: FEATURE_CREATED,
project: randomId(),
data: { id: randomId() },
preData: { id: randomId() },
tags: [{ type: 'simple', value: randomId() }],
createdBy: randomId(),
createdByUserId: TEST_USER_ID,
ip: '127.0.0.1',
},
];
await Promise.all(
events.map((event) => {
return eventService.storeEvent(event);
}),
);
await app.request
.post('/api/admin/events/search')
.send({})
.expect(200)
.expect((res) => {
expect(res.body.events).toHaveLength(2);
});
await app.request
.post('/api/admin/events/search')
.send({ limit: 1, offset: 1 })
.expect(200)
.expect((res) => {
expect(res.body.events).toHaveLength(1);
});
await app.request
.post('/api/admin/events/search')
.send({ query: events[1].data.id })
.expect(200)
.expect((res) => {
expect(res.body.events).toHaveLength(1);
expect(res.body.events[0].data.id).toEqual(events[1].data.id);
});
await app.request
.post('/api/admin/events/search')
.send({ query: events[1].preData.id })
.expect(200)
.expect((res) => {
expect(res.body.events).toHaveLength(1);
expect(res.body.events[0].preData.id).toEqual(events[1].preData.id);
});
await app.request
.post('/api/admin/events/search')
.send({ query: events[1].tags![0].value })
.expect(200)
.expect((res) => {
expect(res.body.events).toHaveLength(1);
expect(res.body.events[0].data.id).toEqual(events[1].data.id);
});
});
test('event creators - if system user, return system name, else should return name from database if user exists, else from events table', async () => {
const user = await db.stores.userStore.insert({ name: 'database-user' });
const events: IBaseEvent[] = [

View File

@ -1,18 +1,22 @@
import dbInit, { type ITestDb } from '../../helpers/database-init.js';
import getLogger from '../../../fixtures/no-logger.js';
import {
createUserWithRootRole,
type IUnleashTest,
setupAppWithCustomConfig,
setupAppWithAuth,
} from '../../helpers/test-helper.js';
import { validateSchema } from '../../../../lib/openapi/validate.js';
import { featureTypesSchema } from '../../../../lib/openapi/spec/feature-types-schema.js';
import { RoleName } from '../../../../lib/types/index.js';
let app: IUnleashTest;
let db: ITestDb;
const adminEmail = 'admin-user@getunleash.io';
beforeAll(async () => {
db = await dbInit('feature_type_api_serial', getLogger);
app = await setupAppWithCustomConfig(
app = await setupAppWithAuth(
db.stores,
{
experimental: {
@ -23,6 +27,17 @@ beforeAll(async () => {
},
db.rawDatabase,
);
await createUserWithRootRole({
app,
stores: db.stores,
email: adminEmail,
roleName: RoleName.ADMIN,
});
});
beforeEach(async () => {
await app.login({ email: adminEmail });
});
afterAll(async () => {

View File

@ -24,9 +24,23 @@ import type { Knex } from 'knex';
import type TestAgent from 'supertest/lib/agent.d.ts';
import type Test from 'supertest/lib/test.d.ts';
import type { Server } from 'node:http';
import { initialServiceSetup } from '../../../lib/server-impl.js';
import {
initialServiceSetup,
type IUser,
type RoleName,
} from '../../../lib/server-impl.js';
import type { EventSearchQueryParameters } from '../../../lib/openapi/spec/event-search-query-parameters.js';
process.env.NODE_ENV = 'test';
type DemoLoginArgs = {
email: string;
};
type SimpleLoginArgs = {
username: string;
password: string;
};
export interface IUnleashTest extends IUnleashHttpAPI {
request: TestAgent<Test>;
destroy: () => Promise<void>;
@ -115,7 +129,10 @@ export interface IUnleashHttpAPI {
expectedResponseCode?: number,
): supertest.Test;
getRecordedEvents(): supertest.Test;
getRecordedEvents(
queryParams?: EventSearchQueryParameters,
expectedResponseCode?: number,
): supertest.Test;
createSegment(postData: object, expectStatusCode?: number): supertest.Test;
deleteSegment(
@ -127,6 +144,8 @@ export interface IUnleashHttpAPI {
postData: object,
expectStatusCode?: number,
): supertest.Test;
login(args: DemoLoginArgs | SimpleLoginArgs): supertest.Test;
}
function httpApis(
@ -318,15 +337,32 @@ function httpApis(
.expect(expectStatusCode);
},
getRecordedEvents(
project: string | null = null,
queryParams: EventSearchQueryParameters = {},
expectedResponseCode: number = 200,
): supertest.Test {
const query = new URLSearchParams(queryParams as any).toString();
return request
.post('/api/admin/events/search')
.send({ project, query: '', limit: 50, offset: 0 })
.set('Content-Type', 'application/json')
.get(`/api/admin/search/events${query ? `?${query}` : ''}`)
.expect(expectedResponseCode);
},
login(args: DemoLoginArgs | SimpleLoginArgs): supertest.Test {
if ('email' in args) {
const { email } = args;
return request
.post(`${base}/auth/demo/login`)
.send({ email })
.expect(200);
}
const { username, password } = args;
return request
.post(`${base}/auth/simple/login`)
.send({
username,
password,
} as SimpleLoginArgs)
.expect(200);
},
};
}
@ -509,3 +545,39 @@ export const insertFeatureEnvironmentsLastSeen = async (
return date;
};
export const createUserWithRootRole = async ({
app,
stores,
email,
name = email,
roleName,
}: {
app: IUnleashTest;
stores: IUnleashStores;
name?: string;
email: string;
roleName?: RoleName;
}): Promise<IUser> => {
const createdUser = await stores.userStore.insert({
name,
email,
});
if (roleName) {
const roles = await app.services.accessService.getRootRoles();
const role = roles.find((role) => role.name === roleName);
if (!role) {
throw new Error(`Role ${roleName} not found`);
}
await app.services.accessService.addUserToRole(
createdUser.id,
role.id,
'default',
);
}
return createdUser;
};