Skip to content

Commit dd5b9d9

Browse files
keithradfordaomi
andauthored
feat: Timetable sharing backend (#252)
* feat: initialize backend code for share functionality * refactor: timetable backend to work * feat: preliminary data validation for timetables * feat(validation): max course amount for timetable * chore: add coverage * fix: ignore build folder * fix: make static function * chore: install supertest * test: add subjects tests * refactor: slug generation to include term * chore: add test command * fix: ignore functions folder tests * feat: add test stage to PR workflow * feat: initialize testing * test: fix basic test * feat(timetableService): add sad path tests * fix(lint) * fix: tests * chore: cleanup tests * refactor: sections are arrays * fix: make shit work on windows -_- * commit * feat: shorter slugs * chore: remove console log * refactor: course validation to use Promise.all * fix: backend ci test job * fix: tests not working with query commands * feat: additional assertion in test * fix(depdency): change cross-env to depdency from dev dependency * feat: generate fetchers for timetable endpoints * nothing to see here * nothing to see here again * fix: fetchers linting error ._. * refactor: use PUT for timetables * refactor: controller tests * fix: tests not passing for PUT * refactor: deprecated bodyparser to use express * chore: cleanup redundant data * fix: tests * fix: packages in wrong places * fix: run npm ci * fix: optional chaining for function configs * fix: clean up packages * chore: move data validation middleware to file * chore: cleanup conditions in validation Co-authored-by: Aomi <[email protected]>
1 parent 9b6bdb1 commit dd5b9d9

File tree

20 files changed

+3046
-1589
lines changed

20 files changed

+3046
-1589
lines changed

.firebaserc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"projects": {
33
"default": "staging-clockwork",
4-
"production": "courseup-vikelabs"
5-
}
4+
"production": "courseup-vikelabs",
5+
"demo": "courseup-demo-vikelabs"
6+
},
7+
"targets": {}
68
}

.github/workflows/backend.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ jobs:
2828
test:
2929
name: Test
3030
runs-on: ubuntu-latest
31+
defaults:
32+
run:
33+
working-directory: ./functions
3134
steps:
3235
- uses: actions/checkout@v2
3336
- name: Setup Node.js

functions/.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v12.21.0

functions/build/routes.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { SectionsController } from './../src/sections/Section.controller';
1010
import { SubjectsController } from './../src/subjects/Subject.controller';
1111
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
1212
import { TextbooksController } from './../src/textbooks/Textbook.controller';
13+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
14+
import { TimetablesController } from './../src/timetables/Timetable.controller';
1315
import * as express from 'express';
1416

1517
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
@@ -183,6 +185,40 @@ const models: TsoaRoute.Models = {
183185
"additionalProperties": false,
184186
},
185187
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
188+
"TimetableCourse": {
189+
"dataType": "refAlias",
190+
"type": {"dataType":"nestedObjectLiteral","nestedProperties":{"color":{"dataType":"string","required":true,"validators":{"pattern":{"value":"^#(?:[0-9a-fA-F]{3}){1,2}$"}}},"tutorial":{"dataType":"array","array":{"dataType":"string"},"validators":{"pattern":{"value":"^T\\d{2}$"}}},"lab":{"dataType":"array","array":{"dataType":"string"},"validators":{"pattern":{"value":"^B\\d{2}$"}}},"lecture":{"dataType":"array","array":{"dataType":"string"},"validators":{"pattern":{"value":"^A\\d{2}$"}}},"pid":{"dataType":"string","required":true},"code":{"dataType":"string","required":true},"subject":{"dataType":"string","required":true}},"validators":{}},
191+
},
192+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
193+
"Timetable": {
194+
"dataType": "refAlias",
195+
"type": {"dataType":"nestedObjectLiteral","nestedProperties":{"courses":{"dataType":"array","array":{"dataType":"refAlias","ref":"TimetableCourse"},"required":true},"term":{"ref":"Term","required":true}},"validators":{}},
196+
},
197+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
198+
"TimetableReturn": {
199+
"dataType": "refAlias",
200+
"type": {"dataType":"intersection","subSchemas":[{"dataType":"nestedObjectLiteral","nestedProperties":{"slug":{"dataType":"string","required":true}}},{"ref":"Timetable"}],"validators":{}},
201+
},
202+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
203+
"ValidateErrorJSON": {
204+
"dataType": "refObject",
205+
"properties": {
206+
"message": {"dataType":"enum","enums":["Validation failed"],"required":true},
207+
"details": {"dataType":"nestedObjectLiteral","nestedProperties":{},"additionalProperties":{"dataType":"any"},"required":true},
208+
},
209+
"additionalProperties": false,
210+
},
211+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
212+
"Pick_Timetable.courses-or-term_": {
213+
"dataType": "refAlias",
214+
"type": {"dataType":"nestedObjectLiteral","nestedProperties":{"courses":{"dataType":"array","array":{"dataType":"refAlias","ref":"TimetableCourse"},"required":true},"term":{"ref":"Term","required":true}},"validators":{}},
215+
},
216+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
217+
"TimetableParams": {
218+
"dataType": "refAlias",
219+
"type": {"ref":"Pick_Timetable.courses-or-term_","validators":{}},
220+
},
221+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
186222
};
187223
const validationService = new ValidationService(models);
188224

@@ -364,6 +400,52 @@ export function RegisterRoutes(app: express.Router) {
364400
promiseHandler(controller, promise, response, undefined, next);
365401
});
366402
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
403+
app.get('/timetables/:slug',
404+
405+
function TimetablesController_getTimetable(request: any, response: any, next: any) {
406+
const args = {
407+
slug: {"in":"path","name":"slug","required":true,"dataType":"string"},
408+
};
409+
410+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
411+
412+
let validatedArgs: any[] = [];
413+
try {
414+
validatedArgs = getValidatedArgs(args, request, response);
415+
} catch (err) {
416+
return next(err);
417+
}
418+
419+
const controller = new TimetablesController();
420+
421+
422+
const promise = controller.getTimetable.apply(controller, validatedArgs as any);
423+
promiseHandler(controller, promise, response, undefined, next);
424+
});
425+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
426+
app.put('/timetables',
427+
428+
function TimetablesController_createTimetable(request: any, response: any, next: any) {
429+
const args = {
430+
requestBody: {"in":"body","name":"requestBody","required":true,"ref":"TimetableParams"},
431+
};
432+
433+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
434+
435+
let validatedArgs: any[] = [];
436+
try {
437+
validatedArgs = getValidatedArgs(args, request, response);
438+
} catch (err) {
439+
return next(err);
440+
}
441+
442+
const controller = new TimetablesController();
443+
444+
445+
const promise = controller.createTimetable.apply(controller, validatedArgs as any);
446+
promiseHandler(controller, promise, response, undefined, next);
447+
});
448+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
367449

368450
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
369451

functions/build/swagger.json

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,145 @@
553553
],
554554
"type": "object",
555555
"additionalProperties": false
556+
},
557+
"TimetableCourse": {
558+
"properties": {
559+
"color": {
560+
"type": "string",
561+
"description": "The colour code displayed on the timetable",
562+
"example": "#123456",
563+
"pattern": "^#(?:[0-9a-fA-F]{3}){1,2}$"
564+
},
565+
"tutorial": {
566+
"items": {
567+
"type": "string"
568+
},
569+
"type": "array",
570+
"description": "The selected tutorial section of the course.",
571+
"example": "T03",
572+
"pattern": "^T\\d{2}$"
573+
},
574+
"lab": {
575+
"items": {
576+
"type": "string"
577+
},
578+
"type": "array",
579+
"description": "The selected lab section of the course.",
580+
"example": "B01",
581+
"pattern": "^B\\d{2}$"
582+
},
583+
"lecture": {
584+
"items": {
585+
"type": "string"
586+
},
587+
"type": "array",
588+
"description": "The selected lecture section of the course.",
589+
"example": "A02",
590+
"pattern": "^A\\d{2}$"
591+
},
592+
"pid": {
593+
"type": "string",
594+
"description": "The PID of the course.",
595+
"example": "ByS23Pp7E"
596+
},
597+
"code": {
598+
"type": "string",
599+
"description": "The code portion of the course.",
600+
"example": "260"
601+
},
602+
"subject": {
603+
"type": "string",
604+
"description": "Abbreviation of the subject of the course.",
605+
"example": "ECE"
606+
}
607+
},
608+
"required": [
609+
"color",
610+
"pid",
611+
"code",
612+
"subject"
613+
],
614+
"type": "object"
615+
},
616+
"Timetable": {
617+
"properties": {
618+
"courses": {
619+
"items": {
620+
"$ref": "#/components/schemas/TimetableCourse"
621+
},
622+
"type": "array"
623+
},
624+
"term": {
625+
"$ref": "#/components/schemas/Term"
626+
}
627+
},
628+
"required": [
629+
"courses",
630+
"term"
631+
],
632+
"type": "object"
633+
},
634+
"TimetableReturn": {
635+
"allOf": [
636+
{
637+
"properties": {
638+
"slug": {
639+
"type": "string"
640+
}
641+
},
642+
"required": [
643+
"slug"
644+
],
645+
"type": "object"
646+
},
647+
{
648+
"$ref": "#/components/schemas/Timetable"
649+
}
650+
]
651+
},
652+
"ValidateErrorJSON": {
653+
"properties": {
654+
"message": {
655+
"type": "string",
656+
"enum": [
657+
"Validation failed"
658+
],
659+
"nullable": false
660+
},
661+
"details": {
662+
"properties": {},
663+
"additionalProperties": {},
664+
"type": "object"
665+
}
666+
},
667+
"required": [
668+
"message",
669+
"details"
670+
],
671+
"type": "object",
672+
"additionalProperties": false
673+
},
674+
"Pick_Timetable.courses-or-term_": {
675+
"properties": {
676+
"courses": {
677+
"items": {
678+
"$ref": "#/components/schemas/TimetableCourse"
679+
},
680+
"type": "array"
681+
},
682+
"term": {
683+
"$ref": "#/components/schemas/Term"
684+
}
685+
},
686+
"required": [
687+
"courses",
688+
"term"
689+
],
690+
"type": "object",
691+
"description": "From T, pick a set of properties whose keys are in the union K"
692+
},
693+
"TimetableParams": {
694+
"$ref": "#/components/schemas/Pick_Timetable.courses-or-term_"
556695
}
557696
},
558697
"securitySchemes": {}
@@ -870,6 +1009,86 @@
8701009
}
8711010
]
8721011
}
1012+
},
1013+
"/timetables/{slug}": {
1014+
"get": {
1015+
"operationId": "GetTimetable",
1016+
"responses": {
1017+
"200": {
1018+
"description": "Ok",
1019+
"content": {
1020+
"application/json": {
1021+
"schema": {
1022+
"anyOf": [
1023+
{
1024+
"$ref": "#/components/schemas/Timetable"
1025+
},
1026+
{}
1027+
]
1028+
}
1029+
}
1030+
}
1031+
},
1032+
"404": {
1033+
"description": "Not found"
1034+
}
1035+
},
1036+
"security": [],
1037+
"parameters": [
1038+
{
1039+
"in": "path",
1040+
"name": "slug",
1041+
"required": true,
1042+
"schema": {
1043+
"type": "string"
1044+
}
1045+
}
1046+
]
1047+
}
1048+
},
1049+
"/timetables": {
1050+
"put": {
1051+
"operationId": "CreateTimetable",
1052+
"responses": {
1053+
"201": {
1054+
"description": "Created",
1055+
"content": {
1056+
"application/json": {
1057+
"schema": {
1058+
"anyOf": [
1059+
{
1060+
"$ref": "#/components/schemas/TimetableReturn"
1061+
},
1062+
{}
1063+
]
1064+
}
1065+
}
1066+
}
1067+
},
1068+
"422": {
1069+
"description": "Invalid data",
1070+
"content": {
1071+
"application/json": {
1072+
"schema": {
1073+
"$ref": "#/components/schemas/ValidateErrorJSON"
1074+
}
1075+
}
1076+
}
1077+
}
1078+
},
1079+
"security": [],
1080+
"parameters": [],
1081+
"requestBody": {
1082+
"required": true,
1083+
"content": {
1084+
"application/json": {
1085+
"schema": {
1086+
"$ref": "#/components/schemas/TimetableParams"
1087+
}
1088+
}
1089+
}
1090+
}
1091+
}
8731092
}
8741093
},
8751094
"servers": [

0 commit comments

Comments
 (0)