1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-23 00:22:19 +01:00

fix: return 404 if the project doesn't exist (#8362)

This change adds a check for whether the project exists in the
database before trying to fetch data for it. If it doesn't exist,
you'll get a 404.
This commit is contained in:
Thomas Heartman 2024-10-04 15:43:02 +02:00 committed by GitHub
parent 1875c9b6d1
commit 2ac9c701c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 54 additions and 30 deletions

View File

@ -19,9 +19,7 @@ export class FakeOnboardingReadModel implements IOnboardingReadModel {
return Promise.resolve([]); return Promise.resolve([]);
} }
getOnboardingStatusForProject( async getOnboardingStatusForProject(): Promise<OnboardingStatus | null> {
projectId: string, return null;
): Promise<OnboardingStatus> {
throw new Error('Method not implemented.');
} }
} }

View File

@ -26,5 +26,7 @@ export type ProjectOnboarding = {
export interface IOnboardingReadModel { export interface IOnboardingReadModel {
getInstanceOnboardingMetrics(): Promise<InstanceOnboarding>; getInstanceOnboardingMetrics(): Promise<InstanceOnboarding>;
getProjectsOnboardingMetrics(): Promise<Array<ProjectOnboarding>>; getProjectsOnboardingMetrics(): Promise<Array<ProjectOnboarding>>;
getOnboardingStatusForProject(projectId: string): Promise<OnboardingStatus>; getOnboardingStatusForProject(
projectId: string,
): Promise<OnboardingStatus | null>;
} }

View File

@ -82,7 +82,16 @@ export class OnboardingReadModel implements IOnboardingReadModel {
async getOnboardingStatusForProject( async getOnboardingStatusForProject(
projectId: string, projectId: string,
): Promise<OnboardingStatus> { ): Promise<OnboardingStatus | null> {
const projectExists = await this.db('projects')
.select(1)
.where('id', projectId)
.first();
if (!projectExists) {
return null;
}
const feature = await this.db('features') const feature = await this.db('features')
.select('name') .select('name')
.where('project', projectId) .where('project', projectId)

View File

@ -335,6 +335,14 @@ test('should return personal dashboard project details', async () => {
}); });
}); });
test("should return 404 if the project doesn't exist", async () => {
await loginUser('new_user@test.com');
await app.request
.get(`/api/admin/personal-dashboard/${randomId()}`)
.expect(404);
});
test('should return Unleash admins', async () => { test('should return Unleash admins', async () => {
await loginUser('new_user@test.com'); await loginUser('new_user@test.com');
const adminRoleId = 1; const adminRoleId = 1;

View File

@ -21,6 +21,7 @@ import type { FeatureEventFormatter } from '../../addons/feature-event-formatter
import { generateImageUrl } from '../../util'; import { generateImageUrl } from '../../util';
import type { PersonalDashboardProjectDetailsSchema } from '../../openapi'; import type { PersonalDashboardProjectDetailsSchema } from '../../openapi';
import type { IRoleWithProject } from '../../types/stores/access-store'; import type { IRoleWithProject } from '../../types/stores/access-store';
import { NotFoundError } from '../../error';
export class PersonalDashboardService { export class PersonalDashboardService {
private personalDashboardReadModel: IPersonalDashboardReadModel; private personalDashboardReadModel: IPersonalDashboardReadModel;
@ -105,6 +106,17 @@ export class PersonalDashboardService {
userId: number, userId: number,
projectId: string, projectId: string,
): Promise<PersonalDashboardProjectDetailsSchema> { ): Promise<PersonalDashboardProjectDetailsSchema> {
const onboardingStatus =
await this.onboardingReadModel.getOnboardingStatusForProject(
projectId,
);
if (!onboardingStatus) {
throw new NotFoundError(
`No project with id "${projectId}" exists.`,
);
}
const formatEvents = (recentEvents: IEvent[]) => const formatEvents = (recentEvents: IEvent[]) =>
recentEvents.map((event) => ({ recentEvents.map((event) => ({
summary: this.featureEventFormatter.format(event).text, summary: this.featureEventFormatter.format(event).text,
@ -122,8 +134,7 @@ export class PersonalDashboardService {
type: role.type as PersonalDashboardProjectDetailsSchema['roles'][number]['type'], type: role.type as PersonalDashboardProjectDetailsSchema['roles'][number]['type'],
})); }));
const [latestEvents, onboardingStatus, owners, roles, healthScores] = const [latestEvents, owners, roles, healthScores] = await Promise.all([
await Promise.all([
this.eventStore this.eventStore
.searchEvents({ limit: 4, offset: 0 }, [ .searchEvents({ limit: 4, offset: 0 }, [
{ {
@ -133,17 +144,11 @@ export class PersonalDashboardService {
}, },
]) ])
.then(formatEvents), .then(formatEvents),
this.onboardingReadModel.getOnboardingStatusForProject(
projectId,
),
this.projectOwnersReadModel.getProjectOwners(projectId), this.projectOwnersReadModel.getProjectOwners(projectId),
this.accessStore this.accessStore
.getAllProjectRolesForUser(userId, projectId) .getAllProjectRolesForUser(userId, projectId)
.then(filterRoles), .then(filterRoles),
this.personalDashboardReadModel.getLatestHealthScores( this.personalDashboardReadModel.getLatestHealthScores(projectId, 8),
projectId,
8,
),
]); ]);
let avgHealthCurrentWindow: number | null = null; let avgHealthCurrentWindow: number | null = null;

View File

@ -1546,7 +1546,9 @@ export default class ProjectService {
updatedAt: project.updatedAt, updatedAt: project.updatedAt,
archivedAt: project.archivedAt, archivedAt: project.archivedAt,
createdAt: project.createdAt, createdAt: project.createdAt,
onboardingStatus, onboardingStatus: onboardingStatus ?? {
status: 'onboarding-started',
},
environments, environments,
featureTypeCounts, featureTypeCounts,
members, members,