mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-14 00:19:16 +01:00
feat: support private projects for event search (#7884)
Adds private projects support for event search. Unit test should cover the usecases.
This commit is contained in:
parent
a89f05181d
commit
183a9fc737
@ -19,7 +19,7 @@ import { normalizeQueryParams } from '../../features/feature-search/search-utils
|
||||
import Controller from '../../routes/controller';
|
||||
import type { IAuthRequest } from '../../server-impl';
|
||||
import type { IEvent } from '../../types';
|
||||
import { anonymiseKeys } from '../../util';
|
||||
import { anonymiseKeys, extractUserIdFromUser } from '../../util';
|
||||
|
||||
const ANON_KEYS = ['email', 'username', 'createdBy'];
|
||||
const version = 1 as const;
|
||||
@ -67,6 +67,7 @@ export default class EventSearchController extends Controller {
|
||||
req: IAuthRequest<any, any, any, EventSearchQueryParameters>,
|
||||
res: Response<EventSearchResponseSchema>,
|
||||
): Promise<void> {
|
||||
const { user } = req;
|
||||
const { normalizedLimit, normalizedOffset } = normalizeQueryParams(
|
||||
req.query,
|
||||
{
|
||||
@ -75,11 +76,14 @@ export default class EventSearchController extends Controller {
|
||||
},
|
||||
);
|
||||
|
||||
const { events, totalEvents } = await this.eventService.searchEvents({
|
||||
...req.query,
|
||||
offset: normalizedOffset,
|
||||
limit: normalizedLimit,
|
||||
});
|
||||
const { events, totalEvents } = await this.eventService.searchEvents(
|
||||
{
|
||||
...req.query,
|
||||
offset: normalizedOffset,
|
||||
limit: normalizedLimit,
|
||||
},
|
||||
extractUserIdFromUser(user),
|
||||
);
|
||||
|
||||
this.openApiService.respondWithValidation(
|
||||
200,
|
||||
|
93
src/lib/features/events/event-service.test.ts
Normal file
93
src/lib/features/events/event-service.test.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import type { ProjectAccess } from '../private-project/privateProjectStore';
|
||||
import { filterAccessibleProjects } from './event-service';
|
||||
|
||||
describe('filterPrivateProjectsFromParams', () => {
|
||||
it('should return IS_ANY_OF with allowed projects when projectParam is undefined and mode is limited', () => {
|
||||
const projectAccess: ProjectAccess = {
|
||||
mode: 'limited',
|
||||
projects: ['project1', 'project2'],
|
||||
};
|
||||
|
||||
const projectParam = undefined;
|
||||
|
||||
const result = filterAccessibleProjects(projectParam, projectAccess);
|
||||
|
||||
expect(result).toBe('IS_ANY_OF:project1,project2');
|
||||
});
|
||||
|
||||
it('should return the original projectParam when mode is all', () => {
|
||||
const projectAccess: ProjectAccess = {
|
||||
mode: 'all',
|
||||
};
|
||||
|
||||
const projectParam = 'IS:project3';
|
||||
|
||||
const result = filterAccessibleProjects(projectParam, projectAccess);
|
||||
|
||||
expect(result).toBe(projectParam);
|
||||
});
|
||||
|
||||
it('should filter out projects not in allowedProjects when mode is limited', () => {
|
||||
const projectAccess: ProjectAccess = {
|
||||
mode: 'limited',
|
||||
projects: ['project1', 'project2'],
|
||||
};
|
||||
|
||||
const projectParam = 'IS_ANY_OF:project1,project3';
|
||||
|
||||
const result = filterAccessibleProjects(projectParam, projectAccess);
|
||||
|
||||
expect(result).toBe('IS_ANY_OF:project1');
|
||||
});
|
||||
|
||||
it('should return a single project if only one is allowed', () => {
|
||||
const projectAccess: ProjectAccess = {
|
||||
mode: 'limited',
|
||||
projects: ['project1'],
|
||||
};
|
||||
|
||||
const projectParam = 'IS_ANY_OF:project1,project2';
|
||||
|
||||
const result = filterAccessibleProjects(projectParam, projectAccess);
|
||||
|
||||
expect(result).toBe('IS_ANY_OF:project1');
|
||||
});
|
||||
|
||||
it('should return undefined if projectParam is undefined and projectAccess mode is all', () => {
|
||||
const projectAccess: ProjectAccess = {
|
||||
mode: 'all',
|
||||
};
|
||||
|
||||
const projectParam = undefined;
|
||||
|
||||
const result = filterAccessibleProjects(projectParam, projectAccess);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return the original projectParam if all projects are allowed when mode is limited', () => {
|
||||
const projectAccess: ProjectAccess = {
|
||||
mode: 'limited',
|
||||
projects: ['project1', 'project2', 'project3'],
|
||||
};
|
||||
|
||||
const projectParam = 'IS_ANY_OF:project1,project2';
|
||||
|
||||
const result = filterAccessibleProjects(projectParam, projectAccess);
|
||||
|
||||
expect(result).toBe('IS_ANY_OF:project1,project2');
|
||||
});
|
||||
|
||||
it('should throw an error if no projects match', () => {
|
||||
const projectAccess: ProjectAccess = {
|
||||
mode: 'limited',
|
||||
projects: ['project1', 'project2'],
|
||||
};
|
||||
|
||||
const projectParam = 'IS_ANY_OF:project3,project4';
|
||||
|
||||
expect(() =>
|
||||
filterAccessibleProjects(projectParam, projectAccess),
|
||||
).toThrow('No accessible projects in the search parameters');
|
||||
});
|
||||
});
|
@ -15,6 +15,7 @@ import type { IQueryParam } from '../feature-toggle/types/feature-toggle-strateg
|
||||
import { parseSearchOperatorValue } from '../feature-search/search-utils';
|
||||
import { endOfDay, formatISO } from 'date-fns';
|
||||
import type { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType';
|
||||
import type { ProjectAccess } from '../private-project/privateProjectStore';
|
||||
|
||||
export default class EventService {
|
||||
private logger: Logger;
|
||||
@ -63,8 +64,20 @@ export default class EventService {
|
||||
};
|
||||
}
|
||||
|
||||
async searchEvents(search: IEventSearchParams): Promise<IEventList> {
|
||||
async searchEvents(
|
||||
search: IEventSearchParams,
|
||||
userId: number,
|
||||
): Promise<IEventList> {
|
||||
const projectAccess =
|
||||
await this.privateProjectChecker.getUserAccessibleProjects(userId);
|
||||
|
||||
search.project = filterAccessibleProjects(
|
||||
search.project,
|
||||
projectAccess,
|
||||
);
|
||||
|
||||
const queryParams = this.convertToDbParams(search);
|
||||
|
||||
const totalEvents = await this.eventStore.searchEventsCount(
|
||||
{
|
||||
limit: search.limit,
|
||||
@ -210,3 +223,35 @@ export default class EventService {
|
||||
return this.eventStore.getEventCreators();
|
||||
}
|
||||
}
|
||||
|
||||
export const filterAccessibleProjects = (
|
||||
projectParam: string | undefined,
|
||||
projectAccess: ProjectAccess,
|
||||
): string | undefined => {
|
||||
if (projectAccess.mode !== 'all') {
|
||||
const allowedProjects = projectAccess.projects;
|
||||
|
||||
if (!projectParam) {
|
||||
return `IS_ANY_OF:${allowedProjects.join(',')}`;
|
||||
} else {
|
||||
const searchProjectList = projectParam.split(',');
|
||||
const filteredProjects = searchProjectList
|
||||
.filter((proj) =>
|
||||
allowedProjects.includes(
|
||||
proj.replace(/^(IS|IS_ANY_OF):/, ''),
|
||||
),
|
||||
)
|
||||
.join(',');
|
||||
|
||||
if (!filteredProjects) {
|
||||
throw new Error(
|
||||
'No accessible projects in the search parameters',
|
||||
);
|
||||
}
|
||||
|
||||
return filteredProjects;
|
||||
}
|
||||
}
|
||||
|
||||
return projectParam;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user