Skip to content

Commit

Permalink
feat: implement responses for locked profile
Browse files Browse the repository at this point in the history
  • Loading branch information
ojeytonwilliams committed May 9, 2024
1 parent ffb77ad commit 9bc4936
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 23 deletions.
52 changes: 46 additions & 6 deletions api/src/routes/user.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1096,11 +1096,31 @@ Thanks and regards,
});

describe('/api/users/get-public-profile', () => {
const profilelessUser = 'profileless-user';
const lockedUser = 'locked-user';
const lockedUserProfileUI = {
isLocked: true,
showAbout: true,
showPortfolio: false
};
const users = [profilelessUser, lockedUser];
beforeAll(async () => {
await fastifyTestInstance.prisma.user.create({
data: { ...minimalUserData, username: profilelessUser }
});
await fastifyTestInstance.prisma.user.create({
data: {
...minimalUserData,
username: 'testuser'
username: lockedUser,
profileUI: lockedUserProfileUI
}
});
});

afterAll(async () => {
await fastifyTestInstance.prisma.user.deleteMany({
where: {
OR: users.map(username => ({ username }))
}
});
});
Expand Down Expand Up @@ -1131,23 +1151,43 @@ Thanks and regards,

test('returns 200 status code with a locked profile if the profile is private', async () => {
const response = await superGet(
'/api/users/get-public-profile?username=testuser'
`/api/users/get-public-profile?username=${lockedUser}`
);

expect(response.body).toStrictEqual({
entities: {
user: {
'private-user': {
[lockedUser]: {
isLocked: true,
profileUI: lockedProfileUI,
username: 'private-user'
profileUI: lockedUserProfileUI,
username: lockedUser
}
}
},
result: 'private-user'
result: lockedUser
});
expect(response.statusCode).toBe(200);
});

test('returns 200 status code locked profile if the profile is missing', async () => {
const response = await superGet(
`/api/users/get-public-profile?username=${profilelessUser}`
);

expect(response.body).toStrictEqual({
entities: {
user: {
[profilelessUser]: {
isLocked: true,
profileUI: lockedProfileUI,
username: profilelessUser
}
}
},
result: profilelessUser
});
expect(response.statusCode).toBe(200);
});
test.todo('returns 200 status code with public user object');
});
});
Expand Down
22 changes: 22 additions & 0 deletions api/src/routes/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,28 @@ export const userPublicGetRoutes: FastifyPluginCallbackTypebox = (
void reply.code(404);
return reply.send({});
}

// The old API would crash if it tried to handle users without profileUI,
// instead of crashing we can handle it gracefully.
const profileUI = normalizeProfileUI(user.profileUI);

if (profileUI.isLocked) {
void reply.code(200);
return reply.send({
// TODO(Post-MVP): just return isLocked and an empty profileUI. No
// need for entities or result.
entities: {
user: {
[user.username]: {
isLocked: true,
profileUI,
username: user.username
}
}
},
result: user.username
});
}
}
);

Expand Down
21 changes: 19 additions & 2 deletions api/src/schemas/api/users/get-public-profile.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
import { Type } from '@fastify/type-provider-typebox';
import { profileUI } from '../../types';

export const getPublicProfile = {
querystring: Type.Object({
username: Type.String({ minLength: 1 })
}),
response: {
400: Type.Object({}),
404: Type.Object({})
200: Type.Object({
entities: Type.Object({
user: Type.Record(
Type.String(),
Type.Object({
isLocked: Type.Boolean(),
profileUI,
username: Type.String()
})
)
}),
result: Type.String()
}),
// We can't simply have Type.Object({}), even though that's correct, because
// TypeScript will then accept all responses (since every object can be
// assigned to {})
400: Type.Object({ entities: Type.Optional(Type.Never()) }),
404: Type.Object({ entities: Type.Optional(Type.Never()) })
}
};
13 changes: 13 additions & 0 deletions api/src/schemas/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,16 @@ export const examResults = Type.Object({
export const surveyTitles = Type.Union([
Type.Literal('Foundational C# with Microsoft Survey')
]);

export const profileUI = Type.Object({
isLocked: Type.Optional(Type.Boolean()),
showAbout: Type.Optional(Type.Boolean()),
showCerts: Type.Optional(Type.Boolean()),
showDonation: Type.Optional(Type.Boolean()),
showHeatMap: Type.Optional(Type.Boolean()),
showLocation: Type.Optional(Type.Boolean()),
showName: Type.Optional(Type.Boolean()),
showPoints: Type.Optional(Type.Boolean()),
showPortfolio: Type.Optional(Type.Boolean()),
showTimeLine: Type.Optional(Type.Boolean())
});
17 changes: 2 additions & 15 deletions api/src/schemas/user/get-session-user.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Type } from '@fastify/type-provider-typebox';
import { examResults, saveChallengeBody } from '../types';
import { examResults, saveChallengeBody, profileUI } from '../types';

export const getSessionUser = {
response: {
Expand Down Expand Up @@ -89,20 +89,7 @@ export const getSessionUser = {
url: Type.String()
})
),
profileUI: Type.Optional(
Type.Object({
isLocked: Type.Optional(Type.Boolean()),
showAbout: Type.Optional(Type.Boolean()),
showCerts: Type.Optional(Type.Boolean()),
showDonation: Type.Optional(Type.Boolean()),
showHeatMap: Type.Optional(Type.Boolean()),
showLocation: Type.Optional(Type.Boolean()),
showName: Type.Optional(Type.Boolean()),
showPoints: Type.Optional(Type.Boolean()),
showPortfolio: Type.Optional(Type.Boolean()),
showTimeLine: Type.Optional(Type.Boolean())
})
),
profileUI: Type.Optional(profileUI),
sendQuincyEmail: Type.Boolean(),
theme: Type.Optional(Type.String()),
twitter: Type.Optional(Type.String()),
Expand Down

0 comments on commit 9bc4936

Please sign in to comment.