Skip to content

Commit

Permalink
fix: return 404 if the project doesn't exist (#8362)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
thomasheartman authored Oct 4, 2024
1 parent 1875c9b commit 2ac9c70
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 30 deletions.
6 changes: 2 additions & 4 deletions src/lib/features/onboarding/fake-onboarding-read-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ export class FakeOnboardingReadModel implements IOnboardingReadModel {
return Promise.resolve([]);
}

getOnboardingStatusForProject(
projectId: string,
): Promise<OnboardingStatus> {
throw new Error('Method not implemented.');
async getOnboardingStatusForProject(): Promise<OnboardingStatus | null> {
return null;
}
}
4 changes: 3 additions & 1 deletion src/lib/features/onboarding/onboarding-read-model-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@ export type ProjectOnboarding = {
export interface IOnboardingReadModel {
getInstanceOnboardingMetrics(): Promise<InstanceOnboarding>;
getProjectsOnboardingMetrics(): Promise<Array<ProjectOnboarding>>;
getOnboardingStatusForProject(projectId: string): Promise<OnboardingStatus>;
getOnboardingStatusForProject(
projectId: string,
): Promise<OnboardingStatus | null>;
}
11 changes: 10 additions & 1 deletion src/lib/features/onboarding/onboarding-read-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,16 @@ export class OnboardingReadModel implements IOnboardingReadModel {

async getOnboardingStatusForProject(
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')
.select('name')
.where('project', projectId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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('[email protected]');

await app.request
.get(`/api/admin/personal-dashboard/${randomId()}`)
.expect(404);
});

test('should return Unleash admins', async () => {
await loginUser('[email protected]');
const adminRoleId = 1;
Expand Down
51 changes: 28 additions & 23 deletions src/lib/features/personal-dashboard/personal-dashboard-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type { FeatureEventFormatter } from '../../addons/feature-event-formatter
import { generateImageUrl } from '../../util';
import type { PersonalDashboardProjectDetailsSchema } from '../../openapi';
import type { IRoleWithProject } from '../../types/stores/access-store';
import { NotFoundError } from '../../error';

export class PersonalDashboardService {
private personalDashboardReadModel: IPersonalDashboardReadModel;
Expand Down Expand Up @@ -105,6 +106,17 @@ export class PersonalDashboardService {
userId: number,
projectId: string,
): Promise<PersonalDashboardProjectDetailsSchema> {
const onboardingStatus =
await this.onboardingReadModel.getOnboardingStatusForProject(
projectId,
);

if (!onboardingStatus) {
throw new NotFoundError(
`No project with id "${projectId}" exists.`,
);
}

const formatEvents = (recentEvents: IEvent[]) =>
recentEvents.map((event) => ({
summary: this.featureEventFormatter.format(event).text,
Expand All @@ -122,29 +134,22 @@ export class PersonalDashboardService {
type: role.type as PersonalDashboardProjectDetailsSchema['roles'][number]['type'],
}));

const [latestEvents, onboardingStatus, owners, roles, healthScores] =
await Promise.all([
this.eventStore
.searchEvents({ limit: 4, offset: 0 }, [
{
field: 'project',
operator: 'IS',
values: [projectId],
},
])
.then(formatEvents),
this.onboardingReadModel.getOnboardingStatusForProject(
projectId,
),
this.projectOwnersReadModel.getProjectOwners(projectId),
this.accessStore
.getAllProjectRolesForUser(userId, projectId)
.then(filterRoles),
this.personalDashboardReadModel.getLatestHealthScores(
projectId,
8,
),
]);
const [latestEvents, owners, roles, healthScores] = await Promise.all([
this.eventStore
.searchEvents({ limit: 4, offset: 0 }, [
{
field: 'project',
operator: 'IS',
values: [projectId],
},
])
.then(formatEvents),
this.projectOwnersReadModel.getProjectOwners(projectId),
this.accessStore
.getAllProjectRolesForUser(userId, projectId)
.then(filterRoles),
this.personalDashboardReadModel.getLatestHealthScores(projectId, 8),
]);

let avgHealthCurrentWindow: number | null = null;
let avgHealthPastWindow: number | null = null;
Expand Down
4 changes: 3 additions & 1 deletion src/lib/features/project/project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,9 @@ export default class ProjectService {
updatedAt: project.updatedAt,
archivedAt: project.archivedAt,
createdAt: project.createdAt,
onboardingStatus,
onboardingStatus: onboardingStatus ?? {
status: 'onboarding-started',
},
environments,
featureTypeCounts,
members,
Expand Down

0 comments on commit 2ac9c70

Please sign in to comment.