chore(api): refactor schemas into individual files (#54350)

pull/54369/head
Shaun Hamilton 2024-04-11 14:45:56 +02:00 committed by GitHub
parent 22277435e0
commit 133f07becd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 1271 additions and 1145 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,129 @@
import { Type } from '@fastify/type-provider-typebox';
import { Certification } from '../../../../shared/config/certification-settings';
export const certSlug = {
params: Type.Object({
certSlug: Type.String(),
username: Type.String()
}),
response: {
// TODO(POST_MVP): Most of these should not be 200s
200: Type.Union([
Type.Object({
messages: Type.Array(
Type.Object({
type: Type.Literal('info'),
message: Type.Literal('flash.username-not-found'),
variables: Type.Object({
username: Type.String()
})
})
)
}),
Type.Object({
messages: Type.Array(
Type.Object({
type: Type.Literal('info'),
message: Type.Literal('flash.not-eligible')
})
)
}),
Type.Object({
messages: Type.Array(
Type.Object({
type: Type.Literal('info'),
message: Type.Literal('flash.not-honest'),
variables: Type.Object({
username: Type.String()
})
})
)
}),
Type.Object({
messages: Type.Array(
Type.Object({
type: Type.Literal('info'),
message: Type.Literal('flash.profile-private'),
variables: Type.Object({
username: Type.String()
})
})
)
}),
Type.Object({
messages: Type.Array(
Type.Object({
type: Type.Literal('info'),
message: Type.Literal('flash.add-name')
})
)
}),
Type.Object({
messages: Type.Array(
Type.Object({
type: Type.Literal('info'),
message: Type.Literal('flash.certs-private'),
variables: Type.Object({
username: Type.String()
})
})
)
}),
Type.Object({
messages: Type.Array(
Type.Object({
type: Type.Literal('info'),
message: Type.Literal('flash.timeline-private'),
variables: Type.Object({
username: Type.String()
})
})
)
}),
Type.Object({
certSlug: Type.Enum(Certification),
certTitle: Type.String(),
username: Type.String(),
date: Type.Number(),
completionTime: Type.Number()
}),
Type.Object({
certSlug: Type.Enum(Certification),
certTitle: Type.String(),
username: Type.String(),
name: Type.String(),
date: Type.Number(),
completionTime: Type.Number()
}),
Type.Object({
messages: Type.Array(
Type.Object({
type: Type.Literal('info'),
message: Type.Literal('flash.user-not-certified'),
variables: Type.Object({
username: Type.String(),
cert: Type.String()
})
})
)
})
]),
400: Type.Object({
type: Type.Literal('error'),
message: Type.String()
}),
404: Type.Object({
message: Type.Literal('flash.cert-not-found'),
type: Type.Literal('info'),
variables: Type.Object({
certSlug: Type.String()
})
}),
500: Type.Object({
type: Type.Literal('danger'),
message: Type.Literal(
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
)
})
}
};

View File

@ -0,0 +1,129 @@
import { Type } from '@fastify/type-provider-typebox';
import { isCertMap } from '../types';
export const certificateVerify = {
// TODO(POST_MVP): Remove partial validation from route for schema validation
body: Type.Object({
certSlug: Type.String({ maxLength: 1024 })
}),
response: {
200: Type.Object({
response: Type.Union([
Type.Object({
type: Type.Literal('info'),
message: Type.Union([Type.Literal('flash.already-claimed')]),
variables: Type.Object({
name: Type.String()
})
}),
Type.Object({
type: Type.Literal('success'),
message: Type.Literal('flash.cert-claim-success'),
variables: Type.Object({
username: Type.String(),
name: Type.String()
})
})
]),
isCertMap,
completedChallenges: Type.Array(
Type.Object({
id: Type.String(),
completedDate: Type.Number(),
solution: Type.Optional(Type.String()),
githubLink: Type.Optional(Type.String()),
challengeType: Type.Optional(Type.Number()),
// Technically, files is optional, but the db default was [] and
// the client treats null, undefined and [] equivalently.
// TODO(Post-MVP): make this optional.
files: Type.Array(
Type.Object({
contents: Type.String(),
key: Type.String(),
ext: Type.String(),
name: Type.String(),
path: Type.Optional(Type.String())
})
),
isManuallyApproved: Type.Optional(Type.Boolean())
})
)
}),
400: Type.Union([
Type.Object({
response: Type.Object({
type: Type.Literal('info'),
message: Type.Union([Type.Literal('flash.incomplete-steps')]),
variables: Type.Object({
name: Type.String()
})
}),
isCertMap,
completedChallenges: Type.Array(
Type.Object({
id: Type.String(),
completedDate: Type.Number(),
solution: Type.Optional(Type.String()),
githubLink: Type.Optional(Type.String()),
challengeType: Type.Optional(Type.Number()),
// Technically, files is optional, but the db default was [] and
// the client treats null, undefined and [] equivalently.
// TODO(Post-MVP): make this optional.
files: Type.Array(
Type.Object({
contents: Type.String(),
key: Type.String(),
ext: Type.String(),
name: Type.String(),
path: Type.Optional(Type.String())
})
),
isManuallyApproved: Type.Optional(Type.Boolean())
})
)
}),
Type.Object({
response: Type.Object({
type: Type.Literal('danger'),
message: Type.Union([Type.Literal('flash.wrong-name')]),
variables: Type.Object({
name: Type.String()
})
})
}),
Type.Object({
response: Type.Object({
type: Type.Literal('info'),
message: Type.Union([Type.Literal('flash.name-needed')])
}),
isCertMap,
completedChallenges: Type.Array(
Type.Object({
id: Type.String(),
completedDate: Type.Number(),
solution: Type.Optional(Type.String()),
githubLink: Type.Optional(Type.String()),
challengeType: Type.Optional(Type.Number()),
// Technically, files is optional, but the db default was [] and
// the client treats null, undefined and [] equivalently.
// TODO(Post-MVP): make this optional.
files: Type.Array(
Type.Object({
contents: Type.String(),
key: Type.String(),
ext: Type.String(),
name: Type.String(),
path: Type.Optional(Type.String())
})
),
isManuallyApproved: Type.Optional(Type.Boolean())
})
)
})
]),
500: Type.Object({
type: Type.Literal('danger'),
message: Type.Literal('flash.went-wrong')
})
}
};

View File

@ -0,0 +1,26 @@
import { Type } from '@fastify/type-provider-typebox';
export const backendChallengeCompleted = {
body: Type.Object({
id: Type.String({ format: 'objectid', maxLength: 24, minLength: 24 })
}),
response: {
200: Type.Object({
completedDate: Type.Number(),
points: Type.Number(),
alreadyCompleted: Type.Boolean()
}),
400: Type.Object({
type: Type.Literal('error'),
message: Type.Literal(
'That does not appear to be a valid challenge submission.'
)
}),
500: Type.Object({
type: Type.Literal('danger'),
message: Type.Literal(
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
)
})
}
};

View File

@ -0,0 +1,22 @@
import { Type } from '@fastify/type-provider-typebox';
export const coderoadChallengeCompleted = {
body: Type.Object({
tutorialId: Type.String()
}),
headers: Type.Object({ 'coderoad-user-token': Type.String() }),
response: {
200: Type.Object({
type: Type.Literal('success'),
msg: Type.String()
}),
400: Type.Object({
type: Type.Literal('error'),
msg: Type.String()
}),
500: Type.Object({
type: Type.Literal('danger'),
msg: Type.String()
})
}
};

View File

@ -0,0 +1,40 @@
import { Type } from '@fastify/type-provider-typebox';
import { examResults } from '../types';
export const examChallengeCompleted = {
body: Type.Object({
id: Type.String({ format: 'objectid', maxLength: 24, minLength: 24 }),
challengeType: Type.Number(),
userCompletedExam: Type.Object({
examTimeInSeconds: Type.Number(),
userExamQuestions: Type.Array(
Type.Object({
id: Type.String(),
question: Type.String(),
answer: Type.Object({
id: Type.String(),
answer: Type.String()
})
}),
{ minItems: 1 }
)
})
}),
response: {
200: Type.Object({
completedDate: Type.Number(),
points: Type.Number(),
alreadyCompleted: Type.Boolean(),
examResults
}),
400: Type.Object({
error: Type.String()
}),
403: Type.Object({
error: Type.String()
}),
500: Type.Object({
error: Type.String()
})
}
};

View File

@ -0,0 +1,37 @@
import { Type } from '@fastify/type-provider-typebox';
export const exam = {
params: Type.Object({
id: Type.String({
format: 'objectid',
maxLength: 24,
minLength: 24
})
}),
response: {
200: Type.Object({
generatedExam: Type.Array(
Type.Object({
id: Type.String(),
question: Type.String(),
answers: Type.Array(
Type.Object({
id: Type.String(),
answer: Type.String()
})
)
})
)
}),
// TODO: Standardize error responses - e.g. { type, message }
400: Type.Object({
error: Type.String()
}),
403: Type.Object({
error: Type.String()
}),
500: Type.Object({
error: Type.String()
})
}
};

View File

@ -0,0 +1,45 @@
import { Type } from '@fastify/type-provider-typebox';
import { saveChallengeBody } from '../types';
export const modernChallengeCompleted = {
body: Type.Object({
id: Type.String({ format: 'objectid', maxLength: 24, minLength: 24 }),
challengeType: Type.Number(),
files: Type.Optional(
Type.Array(
Type.Object({
contents: Type.String(),
key: Type.String(),
ext: Type.String(),
name: Type.String(),
history: Type.Array(Type.String())
})
)
)
}),
response: {
200: Type.Object({
completedDate: Type.Number(),
points: Type.Number(),
alreadyCompleted: Type.Boolean(),
savedChallenges: Type.Array(
Type.Intersect([
saveChallengeBody,
Type.Object({ lastSavedDate: Type.Number() })
])
)
}),
400: Type.Object({
type: Type.Literal('error'),
message: Type.Literal(
'That does not appear to be a valid challenge submission.'
)
}),
500: Type.Object({
type: Type.Literal('danger'),
message: Type.Literal(
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
)
})
}
};

View File

@ -0,0 +1,50 @@
import { Type } from '@fastify/type-provider-typebox';
export const msTrophyChallengeCompleted = {
body: Type.Object({
id: Type.String({ format: 'objectid', maxLength: 24, minLength: 24 })
}),
response: {
200: Type.Object({
completedDate: Type.Number(),
points: Type.Number(),
alreadyCompleted: Type.Boolean()
}),
400: Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.trophy.err-2')
}),
403: Type.Union([
Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.trophy.err-1')
}),
Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.trophy.err-3')
}),
Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.trophy.err-4'),
variables: Type.Object({
msUsername: Type.String()
})
}),
Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.trophy.err-6')
}),
Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.profile.err'),
variables: Type.Object({
msUsername: Type.String()
})
})
]),
500: Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.trophy.err-5')
})
}
};

View File

@ -0,0 +1,46 @@
import { Type } from '@fastify/type-provider-typebox';
export const projectCompleted = {
body: Type.Object({
id: Type.String({ format: 'objectid', maxLength: 24, minLength: 24 }),
challengeType: Type.Optional(Type.Number()),
// The solution must be a valid URL only if it is a `backEndProject`.
solution: Type.String({ maxLength: 1024 }),
githubLink: Type.Optional(Type.String())
}),
response: {
200: Type.Object({
// TODO(Post-MVP): delete completedDate and alreadyCompleted? As far as
// I can tell, they are not used anywhere
completedDate: Type.Number(),
points: Type.Number(),
alreadyCompleted: Type.Boolean()
}),
400: Type.Object({
type: Type.Literal('error'),
message: Type.Union([
Type.Literal(
'That does not appear to be a valid challenge submission.'
),
Type.Literal(
'You have not provided the valid links for us to inspect your work.'
)
])
}),
403: Type.Object({
type: Type.Literal('error'),
message: Type.Union([
Type.Literal(
'You have to complete the project before you can submit a URL.'
),
Type.Literal('That does not appear to be a valid challenge submission.')
])
}),
500: Type.Object({
message: Type.Literal(
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,23 @@
import { Type } from '@fastify/type-provider-typebox';
import { saveChallengeBody } from '../types';
export const saveChallenge = {
body: saveChallengeBody,
response: {
200: Type.Object({
savedChallenges: Type.Array(
Type.Intersect([
saveChallengeBody,
Type.Object({ lastSavedDate: Type.Number() })
])
)
}),
403: Type.Literal('That challenge type is not saveable.'),
500: Type.Object({
type: Type.Literal('danger'),
message: Type.Literal(
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
)
})
}
};

View File

@ -0,0 +1,14 @@
import { Type } from '@fastify/type-provider-typebox';
export const deprecatedEndpoints = {
response: {
410: Type.Object({
message: Type.Object({
type: Type.Literal('info'),
message: Type.Literal(
'Please reload the app, this feature is no longer available.'
)
})
})
}
};

View File

@ -0,0 +1,29 @@
import { Type } from '@fastify/type-provider-typebox';
export const chargeStripeCard = {
body: Type.Object({
paymentMethodId: Type.String(),
amount: Type.Number(),
duration: Type.Literal('month')
}),
response: {
200: Type.Object({
isDonating: Type.Boolean(),
type: Type.Literal('success')
}),
400: Type.Object({
message: Type.String(),
type: Type.Literal('info')
}),
402: Type.Object({
message: Type.String(),
type: Type.String(),
// eslint-disable-next-line @typescript-eslint/naming-convention
client_secret: Type.Optional(Type.String())
}),
500: Type.Object({
message: Type.String(),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,21 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyAbout = {
body: Type.Object({
// TODO(Post-MVP): make these required
about: Type.Optional(Type.String()),
name: Type.Optional(Type.String()),
picture: Type.Optional(Type.String()),
location: Type.Optional(Type.String())
}),
response: {
200: Type.Object({
message: Type.Literal('flash.updated-about-me'),
type: Type.Literal('success')
}),
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,17 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyClassroomMode = {
body: Type.Object({
isClassroomAccount: Type.Boolean()
}),
response: {
200: Type.Object({
message: Type.Literal('flash.classroom-mode-updated'),
type: Type.Literal('success')
}),
403: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,21 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyEmail = {
body: Type.Object({
email: Type.String({ format: 'email', maxLength: 1024 })
}),
response: {
200: Type.Object({
message: Type.Literal('flash.email-valid'),
type: Type.Literal('success')
}),
'4xx': Type.Object({
message: Type.String(),
type: Type.Union([Type.Literal('danger'), Type.Literal('info')])
}),
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,21 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyHonesty = {
body: Type.Object({
isHonest: Type.Literal(true)
}),
response: {
200: Type.Object({
message: Type.Literal('buttons.accepted-honesty'),
type: Type.Literal('success')
}),
400: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
}),
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,21 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyKeyboardShortcuts = {
body: Type.Object({
keyboardShortcuts: Type.Boolean()
}),
response: {
200: Type.Object({
message: Type.Literal('flash.keyboard-shortcut-updated'),
type: Type.Literal('success')
}),
400: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
}),
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,32 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyPortfolio = {
body: Type.Object({
portfolio: Type.Array(
Type.Object({
description: Type.Optional(Type.String()),
id: Type.Optional(Type.String()),
image: Type.Optional(Type.String()),
title: Type.Optional(Type.String()),
url: Type.Optional(Type.String())
})
)
}),
response: {
200: Type.Object({
message: Type.Literal('flash.portfolio-item-updated'),
type: Type.Literal('success')
}),
// TODO(Post-MVP): give more detailed response (i.e. which item is
// missing)
400: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
}),
// TODO(Post-MVP): differentiate with more than just the status
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,21 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyPrivacyTerms = {
body: Type.Object({
quincyEmails: Type.Boolean()
}),
response: {
200: Type.Object({
message: Type.Literal('flash.privacy-updated'),
type: Type.Literal('success')
}),
400: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
}),
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,32 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyProfileUI = {
body: Type.Object({
profileUI: Type.Object({
isLocked: Type.Boolean(),
showAbout: Type.Boolean(),
showCerts: Type.Boolean(),
showDonation: Type.Boolean(),
showHeatMap: Type.Boolean(),
showLocation: Type.Boolean(),
showName: Type.Boolean(),
showPoints: Type.Boolean(),
showPortfolio: Type.Boolean(),
showTimeLine: Type.Boolean()
})
}),
response: {
200: Type.Object({
message: Type.Literal('flash.privacy-updated'),
type: Type.Literal('success')
}),
400: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
}),
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,21 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyQuincyEmail = {
body: Type.Object({
sendQuincyEmail: Type.Boolean()
}),
response: {
200: Type.Object({
message: Type.Literal('flash.subscribe-to-quincy-updated'),
type: Type.Literal('success')
}),
400: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
}),
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,26 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMySocials = {
body: Type.Object({
website: Type.Optional(Type.String({ format: 'url', maxLength: 1024 })),
twitter: Type.Optional(Type.String({ format: 'url', maxLength: 1024 })),
githubProfile: Type.Optional(
Type.String({ format: 'url', maxLength: 1024 })
),
linkedin: Type.Optional(Type.String({ format: 'url', maxLength: 1024 }))
}),
response: {
200: Type.Object({
message: Type.Literal('flash.updated-socials'),
type: Type.Literal('success')
}),
400: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
}),
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,21 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyTheme = {
body: Type.Object({
theme: Type.Union([Type.Literal('default'), Type.Literal('night')])
}),
response: {
200: Type.Object({
message: Type.Literal('flash.updated-themes'),
type: Type.Literal('success')
}),
400: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
}),
500: Type.Object({
message: Type.Literal('flash.wrong-updating'),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,22 @@
import { Type } from '@fastify/type-provider-typebox';
export const updateMyUsername = {
body: Type.Object({
username: Type.String({ minLength: 3, maxLength: 1000 })
}),
response: {
200: Type.Object({
message: Type.String(),
type: Type.Literal('success'),
username: Type.String()
}),
400: Type.Object({
message: Type.Optional(Type.String()),
type: Type.Literal('info')
}),
500: Type.Object({
message: Type.String(),
type: Type.Literal('danger')
})
}
};

59
api/src/schemas/types.ts Normal file
View File

@ -0,0 +1,59 @@
import { Type } from '@fastify/type-provider-typebox';
export const generic500 = Type.Object({
message: Type.Literal(
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
),
type: Type.Literal('danger')
});
export const isCertMap = Type.Object({
isRespWebDesignCert: Type.Boolean(),
isJsAlgoDataStructCert: Type.Boolean(),
isFrontEndLibsCert: Type.Boolean(),
is2018DataVisCert: Type.Boolean(),
isApisMicroservicesCert: Type.Boolean(),
isInfosecQaCert: Type.Boolean(),
isQaCertV7: Type.Boolean(),
isInfosecCertV7: Type.Boolean(),
isFrontEndCert: Type.Boolean(),
isBackEndCert: Type.Boolean(),
isDataVisCert: Type.Boolean(),
isFullStackCert: Type.Boolean(),
isSciCompPyCertV7: Type.Boolean(),
isDataAnalysisPyCertV7: Type.Boolean(),
isMachineLearningPyCertV7: Type.Boolean(),
isRelationalDatabaseCertV8: Type.Boolean(),
isCollegeAlgebraPyCertV8: Type.Boolean(),
isFoundationalCSharpCertV8: Type.Boolean()
});
export const file = Type.Object({
contents: Type.String(),
key: Type.String(),
ext: Type.String(),
name: Type.String(),
history: Type.Array(Type.String())
});
export const saveChallengeBody = Type.Object({
id: Type.String({
format: 'objectid',
maxLength: 24,
minLength: 24
}),
files: Type.Array(file)
});
export const examResults = Type.Object({
numberOfCorrectAnswers: Type.Number(),
numberOfQuestionsInExam: Type.Number(),
percentCorrect: Type.Number(),
passingPercent: Type.Number(),
passed: Type.Boolean(),
examTimeInSeconds: Type.Number()
});
export const surveyTitles = Type.Union([
Type.Literal('Foundational C# with Microsoft Survey')
]);

View File

@ -0,0 +1,11 @@
import { Type } from '@fastify/type-provider-typebox';
export const deleteMsUsername = {
response: {
200: Type.Object({ msUsername: Type.Null() }),
500: Type.Object({
message: Type.Literal('flash.ms.transcript.unlink-err'),
type: Type.Literal('error')
})
}
};

View File

@ -0,0 +1,9 @@
import { Type } from '@fastify/type-provider-typebox';
import { generic500 } from '../types';
export const deleteMyAccount = {
response: {
200: Type.Object({}),
500: generic500
}
};

View File

@ -0,0 +1,19 @@
import { Type } from '@fastify/type-provider-typebox';
export const deleteUserToken = {
response: {
200: Type.Object({
userToken: Type.Null()
}),
404: Type.Object({
message: Type.Literal('userToken not found'),
type: Type.Literal('info')
}),
500: Type.Object({
message: Type.Literal(
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
),
type: Type.Literal('danger')
})
}
};

View File

@ -0,0 +1,141 @@
import { Type } from '@fastify/type-provider-typebox';
import { examResults, saveChallengeBody } from '../types';
export const getSessionUser = {
response: {
200: Type.Object({
user: Type.Record(
Type.String(),
Type.Object({
about: Type.String(),
acceptedPrivacyTerms: Type.Boolean(),
calendar: Type.Record(Type.Number(), Type.Literal(1)),
completedChallenges: Type.Array(
Type.Object({
id: Type.String(),
completedDate: Type.Number(),
solution: Type.Optional(Type.String()),
githubLink: Type.Optional(Type.String()),
challengeType: Type.Optional(Type.Number()),
// Technically, files is optional, but the db default was [] and
// the client treats null, undefined and [] equivalently.
// TODO(Post-MVP): make this optional.
files: Type.Array(
Type.Object({
contents: Type.String(),
key: Type.String(),
ext: Type.String(),
name: Type.String(),
path: Type.Optional(Type.String())
})
),
isManuallyApproved: Type.Optional(Type.Boolean())
})
),
completedExams: Type.Array(
Type.Object({
id: Type.String(),
completedDate: Type.Number(),
challengeType: Type.Optional(Type.Number()),
examResults
})
),
completedChallengeCount: Type.Number(),
currentChallengeId: Type.Optional(Type.String()),
email: Type.String(),
emailVerified: Type.Boolean(),
githubProfile: Type.Optional(Type.String()),
id: Type.String(),
isApisMicroservicesCert: Type.Optional(Type.Boolean()),
isBackEndCert: Type.Optional(Type.Boolean()),
isCheater: Type.Optional(Type.Boolean()),
isDonating: Type.Boolean(),
is2018DataVisCert: Type.Optional(Type.Boolean()),
isDataVisCert: Type.Optional(Type.Boolean()),
isFrontEndCert: Type.Optional(Type.Boolean()),
isFullStackCert: Type.Optional(Type.Boolean()),
isFrontEndLibsCert: Type.Optional(Type.Boolean()),
isHonest: Type.Optional(Type.Boolean()),
isInfosecCertV7: Type.Optional(Type.Boolean()),
isInfosecQaCert: Type.Optional(Type.Boolean()),
isQaCertV7: Type.Optional(Type.Boolean()),
isJsAlgoDataStructCert: Type.Optional(Type.Boolean()),
isRelationalDatabaseCertV8: Type.Optional(Type.Boolean()),
isRespWebDesignCert: Type.Optional(Type.Boolean()),
isSciCompPyCertV7: Type.Optional(Type.Boolean()),
isDataAnalysisPyCertV7: Type.Optional(Type.Boolean()),
isMachineLearningPyCertV7: Type.Optional(Type.Boolean()),
isCollegeAlgebraPyCertV8: Type.Optional(Type.Boolean()),
keyboardShortcuts: Type.Optional(Type.Boolean()),
linkedin: Type.Optional(Type.String()),
location: Type.Optional(Type.String()),
name: Type.Optional(Type.String()),
partiallyCompletedChallenges: Type.Optional(
Type.Array(
Type.Object({ id: Type.String(), completedDate: Type.Number() })
)
),
picture: Type.String(), // TODO(Post-MVP): format as url/uri?
points: Type.Number(),
portfolio: Type.Array(
Type.Object({
description: Type.String(),
id: Type.String(),
image: Type.String(),
title: Type.String(),
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())
})
),
sendQuincyEmail: Type.Boolean(),
theme: Type.Optional(Type.String()),
twitter: Type.Optional(Type.String()),
website: Type.Optional(Type.String()),
yearsTopContributor: Type.Array(Type.String()), // TODO(Post-MVP): convert to number?
isEmailVerified: Type.Boolean(),
joinDate: Type.String(),
savedChallenges: Type.Optional(
Type.Array(
Type.Intersect([
saveChallengeBody,
Type.Object({ lastSavedDate: Type.Number() })
])
)
),
username: Type.String(),
userToken: Type.Optional(Type.String()),
completedSurveys: Type.Array(
Type.Object({
title: Type.String(),
responses: Type.Array(
Type.Object({
question: Type.String(),
response: Type.String()
})
)
})
),
msUsername: Type.Optional(Type.String())
})
),
result: Type.String()
}),
500: Type.Object({
user: Type.Object({}),
result: Type.Literal('')
})
}
};

View File

@ -0,0 +1,34 @@
import { Type } from '@fastify/type-provider-typebox';
export const postMsUsername = {
body: Type.Object({
msTranscriptUrl: Type.String({ maxLength: 1000 })
}),
response: {
200: Type.Object({
msUsername: Type.String()
}),
400: Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.transcript.link-err-1')
}),
404: Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.transcript.link-err-2')
}),
403: Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.transcript.link-err-4')
}),
500: Type.Union([
Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.transcript.link-err-6')
}),
Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.ms.transcript.link-err-3')
})
])
}
};

View File

@ -0,0 +1,23 @@
import { Type } from '@fastify/type-provider-typebox';
import { generic500 } from '../types';
export const reportUser = {
body: Type.Object({
username: Type.String(),
reportDescription: Type.String()
}),
response: {
200: Type.Object({
type: Type.Literal('info'),
message: Type.Literal('flash.report-sent'),
variables: Type.Object({
email: Type.String()
})
}),
400: Type.Object({
type: Type.Literal('danger'),
message: Type.Literal('flash.provide-username')
}),
500: generic500
}
};

View File

@ -0,0 +1,9 @@
import { Type } from '@fastify/type-provider-typebox';
import { generic500 } from '../types';
export const resetMyProgress = {
response: {
200: Type.Object({}),
500: generic500
}
};

View File

@ -0,0 +1,36 @@
import { Type } from '@fastify/type-provider-typebox';
import { surveyTitles } from '../types';
export const submitSurvey = {
body: Type.Object({
surveyResults: Type.Object({
title: surveyTitles,
responses: Type.Array(
Type.Object({
question: Type.String(),
response: Type.String()
})
)
})
}),
response: {
200: Type.Object({
type: Type.Literal('success'),
message: Type.Literal('flash.survey.success')
}),
400: Type.Union([
Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.survey.err-1')
}),
Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.survey.err-2')
})
]),
500: Type.Object({
type: Type.Literal('error'),
message: Type.Literal('flash.survey.err-3')
})
}
};