From 8c9cd856bf8fd71655527021764a17fff49be619 Mon Sep 17 00:00:00 2001 From: "Xunnamius (Romulus)" Date: Mon, 1 Jun 2020 18:23:21 -0500 Subject: [PATCH] documentation updates --- lib/relative-random-time/index.ts | 24 ++++++++++++++++- lib/test-api-endpoint.ts | 18 +++++++++++++ next.config.js | 14 +--------- package.json | 2 +- src/__test__/db.ts | 1 - src/backend/db.ts | 16 +++++++----- src/backend/index.ts | 8 +++--- src/backend/middleware.ts | 4 +-- src/pages/_app.tsx | 2 +- src/pages/index.tsx | 2 +- types/_shared.ts | 21 --------------- types/global.ts | 43 ++++++++++++++++++++++++++----- 12 files changed, 96 insertions(+), 59 deletions(-) diff --git a/lib/relative-random-time/index.ts b/lib/relative-random-time/index.ts index dd9bef7..99e75a5 100644 --- a/lib/relative-random-time/index.ts +++ b/lib/relative-random-time/index.ts @@ -10,7 +10,10 @@ const NEAR_SMALLEST_ABS = 10**3; const dateTLS = (time: number): string => (!isFinite(time) && time.toString()) || (new Date(time)).toLocaleString(); -// TODO: document that bounds are inclusive +/** + * Returns a number between bounds[0] and bounds[1] (inclusive) that is higher + * than `before` but lower than `after`. + */ export function fromTimespan({ bounds, before, after }: TimespanParams): number { // ? Ensure sorting happens in ascending order bounds.sort((a, b) => a - b); @@ -27,22 +30,41 @@ export function fromTimespan({ bounds, before, after }: TimespanParams): number return randomInt(ceiling, floor); } +/** + * Returns a number that is higher than `before` but lower than `after` + * representing a time in the distant past (months to decades). + */ export function farPast({ before, after }: TimeParams = {}): number { return fromTimespan({ bounds: [-FAR_SMALLEST_ABS, -FAR_LARGEST_ABS], before, after }); } +/** + * Returns a number that is higher than `before` but lower than `after` + * representing a time in the near past (seconds to minutes). + */ export function nearPast({ before, after }: TimeParams = {}): number { return fromTimespan({ bounds: [-NEAR_SMALLEST_ABS, -NEAR_LARGEST_ABS], before, after }); } +/** + * Returns Date.now() + */ export function present(): number { return Date.now(); } +/** + * Returns a number that is higher than `before` but lower than `after` + * representing a time in the distant future (months to decades). + */ export function nearFuture({ before, after }: TimeParams = {}): number { return fromTimespan({ bounds: [NEAR_SMALLEST_ABS, NEAR_LARGEST_ABS], before, after }); } +/** + * Returns a number that is higher than `before` but lower than `after` + * representing a time in the near future (seconds to minutes). + */ export function farFuture({ before, after }: TimeParams = {}): number { return fromTimespan({ bounds: [FAR_SMALLEST_ABS, FAR_LARGEST_ABS], before, after }); } diff --git a/lib/test-api-endpoint.ts b/lib/test-api-endpoint.ts index 64e885d..18172ae 100644 --- a/lib/test-api-endpoint.ts +++ b/lib/test-api-endpoint.ts @@ -14,6 +14,24 @@ export type TesApiEndParams = { next: any; }; +/** + * Uses Next's internal `apiResolver` to execute endpoints in a Next-like + * testing environment. Useful for unit testing Next /api endpoints. + * + * `test` should be a function that returns a promise (or async) where Jest + * tests and the like can be run. + * + * `params` are passed directly to the api handler and represent processed + * dynamic routes, i.e. testing `/api/user/:id` would need `params: { id: ... }` + * + * `requestPatcher` and `responsePatcher` are functions that receive an + * IncomingMessage and ServerResponse instance respectively. Use these functions + * to edit the request and response before they're injected into the api + * handler. + * + * `next` is the actual api handler under test. It should be an async function + * that accepts a NextApiRequest and NextApiResult as its two parameters. + */ export async function testApiEndpoint({ test, params, requestPatcher, responsePatcher, next }: TesApiEndParams) { let server = null; diff --git a/next.config.js b/next.config.js index c8bde4d..a98ee3c 100644 --- a/next.config.js +++ b/next.config.js @@ -36,18 +36,6 @@ module.exports = () => { experimental: { async rewrites() { return [{ - source: '/api', - destination: '/404' - }, { - source: '/api/v1', - destination: '/404' - }, { - source: '/api/v1/election', - destination: '/404' - }, { - source: '/api/v2', - destination: '/404' - }, { source: '/v1/meta', destination: '/api/v1/meta' }, { @@ -65,4 +53,4 @@ module.exports = () => { } }); }; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbmZpZy9uZXh0LmNvbmZpZy50cyJdLCJuYW1lcyI6WyJyZXF1aXJlIiwicG9wdWxhdGVFbnYiLCJwYXRocyIsInVuaXZlcnNlIiwiX19kaXJuYW1lIiwibXVsdGl2ZXJzZSIsIm1vZHVsZSIsImV4cG9ydHMiLCJlbmFibGVkIiwicHJvY2VzcyIsImVudiIsIkFOQUxZWkUiLCJkaXN0RGlyIiwid2VicGFjayIsImNvbmZpZyIsInJlc29sdmUiLCJhbGlhcyIsIk1BWF9MSU1JVCIsIklHTk9SRV9SQVRFX0xJTUlUUyIsIkxPQ0tPVVRfQUxMX0tFWVMiLCJESVNBTExPV0VEX01FVEhPRFMiLCJSRVFVRVNUU19QRVJfQ09OVFJJVkVEX0VSUk9SIiwiTUFYX09QVElPTlNfUEVSX0VMRUNUSU9OIiwiTUFYX1JBTktJTkdTX1BFUl9FTEVDVElPTiIsIk1BWF9DT05URU5UX0xFTkdUSF9CWVRFUyIsImV4cGVyaW1lbnRhbCIsInJld3JpdGVzIiwic291cmNlIiwiZGVzdGluYXRpb24iXSwibWFwcGluZ3MiOiI7O0FBQUE7Ozs7QUFPQUEsT0FBTyxDQUFDLGlCQUFELENBQVAsQ0FBMkJDLFdBQTNCOztBQUVBLE1BQU1DLEtBQUssR0FBRztBQUNWQyxFQUFBQSxRQUFRLEVBQUcsR0FBRUMsU0FBVSxPQURiO0FBRVZDLEVBQUFBLFVBQVUsRUFBRyxHQUFFRCxTQUFVO0FBRmYsQ0FBZDs7QUFLQUUsTUFBTSxDQUFDQyxPQUFQLEdBQWlCLE1BQWM7QUFDM0IsU0FBTyw2QkFBbUI7QUFDdEJDLElBQUFBLE9BQU8sRUFBRUMsT0FBTyxDQUFDQyxHQUFSLENBQVlDLE9BQVosS0FBd0I7QUFEWCxHQUFuQixFQUVKO0FBRUNDLElBQUFBLE9BQU8sRUFBRSxPQUZWO0FBT0NDLElBQUFBLE9BQU8sRUFBR0MsTUFBRCxJQUEyQjtBQUloQ0EsTUFBQUEsTUFBTSxDQUFDQyxPQUFQLEtBQW1CRCxNQUFNLENBQUNDLE9BQVAsQ0FBZUMsS0FBZixHQUF1QixFQUN0QyxHQUFHRixNQUFNLENBQUNDLE9BQVAsQ0FBZUMsS0FEb0I7QUFFdENiLFFBQUFBLFFBQVEsRUFBRUQsS0FBSyxDQUFDQyxRQUZzQjtBQUd0Q0UsUUFBQUEsVUFBVSxFQUFFSCxLQUFLLENBQUNHO0FBSG9CLE9BQTFDO0FBTUEsYUFBT1MsTUFBUDtBQUNILEtBbEJGO0FBdUJDSixJQUFBQSxHQUFHLEVBQUU7QUFDRE8sTUFBQUEsU0FBUyxFQUFFUixPQUFPLENBQUNDLEdBQVIsQ0FBWU8sU0FEdEI7QUFFREMsTUFBQUEsa0JBQWtCLEVBQUVULE9BQU8sQ0FBQ0MsR0FBUixDQUFZUSxrQkFGL0I7QUFHREMsTUFBQUEsZ0JBQWdCLEVBQUVWLE9BQU8sQ0FBQ0MsR0FBUixDQUFZUyxnQkFIN0I7QUFJREMsTUFBQUEsa0JBQWtCLEVBQUVYLE9BQU8sQ0FBQ0MsR0FBUixDQUFZVSxrQkFKL0I7QUFLREMsTUFBQUEsNEJBQTRCLEVBQUVaLE9BQU8sQ0FBQ0MsR0FBUixDQUFZVyw0QkFMekM7QUFNREMsTUFBQUEsd0JBQXdCLEVBQUViLE9BQU8sQ0FBQ0MsR0FBUixDQUFZWSx3QkFOckM7QUFPREMsTUFBQUEseUJBQXlCLEVBQUVkLE9BQU8sQ0FBQ0MsR0FBUixDQUFZYSx5QkFQdEM7QUFRREMsTUFBQUEsd0JBQXdCLEVBQUVmLE9BQU8sQ0FBQ0MsR0FBUixDQUFZYztBQVJyQyxLQXZCTjtBQW9DQ0MsSUFBQUEsWUFBWSxFQUFFO0FBQ1YsWUFBTUMsUUFBTixHQUFpQjtBQUNiLGVBQU8sQ0FDSDtBQUNJQyxVQUFBQSxNQUFNLEVBQUUsTUFEWjtBQUVJQyxVQUFBQSxXQUFXLEVBQUU7QUFGakIsU0FERyxFQUtIO0FBQ0lELFVBQUFBLE1BQU0sRUFBRSxTQURaO0FBRUlDLFVBQUFBLFdBQVcsRUFBRTtBQUZqQixTQUxHLEVBU0g7QUFDSUQsVUFBQUEsTUFBTSxFQUFFLGtCQURaO0FBRUlDLFVBQUFBLFdBQVcsRUFBRTtBQUZqQixTQVRHLEVBYUg7QUFDSUQsVUFBQUEsTUFBTSxFQUFFLFNBRFo7QUFFSUMsVUFBQUEsV0FBVyxFQUFFO0FBRmpCLFNBYkcsRUFpQkg7QUFDSUQsVUFBQUEsTUFBTSxFQUFFLFVBRFo7QUFFSUMsVUFBQUEsV0FBVyxFQUFFO0FBRmpCLFNBakJHLEVBcUJIO0FBQ0lELFVBQUFBLE1BQU0sRUFBRSxlQURaO0FBRUlDLFVBQUFBLFdBQVcsRUFBRTtBQUZqQixTQXJCRyxFQXlCSDtBQUNJRCxVQUFBQSxNQUFNLEVBQUUsa0JBRFo7QUFFSUMsVUFBQUEsV0FBVyxFQUFFO0FBRmpCLFNBekJHLEVBNkJIO0FBQ0lELFVBQUFBLE1BQU0sRUFBRSx5QkFEWjtBQUVJQyxVQUFBQSxXQUFXLEVBQUU7QUFGakIsU0E3QkcsQ0FBUDtBQWtDSDs7QUFwQ1M7QUFwQ2YsR0FGSSxDQUFQO0FBNkVILENBOUVEIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHdpdGhCdW5kbGVBbmFseXplciBmcm9tICdAbmV4dC9idW5kbGUtYW5hbHl6ZXInXG5cbmltcG9ydCB0eXBlIHsgQ29uZmlndXJhdGlvbiB9IGZyb20gJ3dlYnBhY2snXG5cbi8vID8gTm90IHVzaW5nIEVTNi9UUyBpbXBvcnQgc3ludGF4IGhlcmUgYmVjYXVzZSBkZXYtdXRpbHMgaGFzIHNwZWNpYWxcbi8vID8gY2lyY3Vtc3RhbmNlc1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby11bnJlc29sdmVkLCBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdmFyLXJlcXVpcmVzXG5yZXF1aXJlKCcuL3NyYy9kZXYtdXRpbHMnKS5wb3B1bGF0ZUVudigpO1xuXG5jb25zdCBwYXRocyA9IHtcbiAgICB1bml2ZXJzZTogYCR7X19kaXJuYW1lfS9zcmMvYCxcbiAgICBtdWx0aXZlcnNlOiBgJHtfX2Rpcm5hbWV9L2xpYi9gLFxufTtcblxubW9kdWxlLmV4cG9ydHMgPSAoKTogb2JqZWN0ID0+IHtcbiAgICByZXR1cm4gd2l0aEJ1bmRsZUFuYWx5emVyKHtcbiAgICAgICAgZW5hYmxlZDogcHJvY2Vzcy5lbnYuQU5BTFlaRSA9PT0gJ3RydWUnXG4gICAgfSkoe1xuICAgICAgICAvLyA/IFJlbmFtZXMgdGhlIGJ1aWxkIGRpciBcImJ1aWxkXCIgaW5zdGVhZCBvZiBcIi5uZXh0XCJcbiAgICAgICAgZGlzdERpcjogJ2J1aWxkJyxcblxuICAgICAgICAvLyA/IFdlYnBhY2sgY29uZmlndXJhdGlvblxuICAgICAgICAvLyAhIE5vdGUgdGhhdCB0aGUgd2VicGFjayBjb25maWd1cmF0aW9uIGlzIGV4ZWN1dGVkIHR3aWNlOiBvbmNlXG4gICAgICAgIC8vICEgc2VydmVyLXNpZGUgYW5kIG9uY2UgY2xpZW50LXNpZGUhXG4gICAgICAgIHdlYnBhY2s6IChjb25maWc6IENvbmZpZ3VyYXRpb24pID0+IHtcbiAgICAgICAgICAgIC8vID8gVGhlc2UgYXJlIGFsaWFzZXMgdGhhdCBjYW4gYmUgdXNlZCBkdXJpbmcgSlMgaW1wb3J0IGNhbGxzXG4gICAgICAgICAgICAvLyAhIE5vdGUgdGhhdCB5b3UgbXVzdCBhbHNvIGNoYW5nZSB0aGVzZSBzYW1lIGFsaWFzZXMgaW4gdHNjb25maWcuanNvblxuICAgICAgICAgICAgLy8gISBOb3RlIHRoYXQgeW91IG11c3QgYWxzbyBjaGFuZ2UgdGhlc2Ugc2FtZSBhbGlhc2VzIGluIHBhY2thZ2UuanNvbiAoamVzdClcbiAgICAgICAgICAgIGNvbmZpZy5yZXNvbHZlICYmIChjb25maWcucmVzb2x2ZS5hbGlhcyA9IHtcbiAgICAgICAgICAgICAgICAuLi5jb25maWcucmVzb2x2ZS5hbGlhcyxcbiAgICAgICAgICAgICAgICB1bml2ZXJzZTogcGF0aHMudW5pdmVyc2UsXG4gICAgICAgICAgICAgICAgbXVsdGl2ZXJzZTogcGF0aHMubXVsdGl2ZXJzZSxcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gY29uZmlnO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8vID8gU2VsZWN0IHNvbWUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGRlZmluZWQgaW4gLmVudiB0byBwdXNoIHRvIHRoZVxuICAgICAgICAvLyA/IGNsaWVudC5cbiAgICAgICAgLy8gISEgRE8gTk9UIFBVVCBBTlkgU0VDUkVUIEVOVklST05NRU5UIFZBUklBQkxFUyBIRVJFICEhXG4gICAgICAgIGVudjoge1xuICAgICAgICAgICAgTUFYX0xJTUlUOiBwcm9jZXNzLmVudi5NQVhfTElNSVQsXG4gICAgICAgICAgICBJR05PUkVfUkFURV9MSU1JVFM6IHByb2Nlc3MuZW52LklHTk9SRV9SQVRFX0xJTUlUUyxcbiAgICAgICAgICAgIExPQ0tPVVRfQUxMX0tFWVM6IHByb2Nlc3MuZW52LkxPQ0tPVVRfQUxMX0tFWVMsXG4gICAgICAgICAgICBESVNBTExPV0VEX01FVEhPRFM6IHByb2Nlc3MuZW52LkRJU0FMTE9XRURfTUVUSE9EUyxcbiAgICAgICAgICAgIFJFUVVFU1RTX1BFUl9DT05UUklWRURfRVJST1I6IHByb2Nlc3MuZW52LlJFUVVFU1RTX1BFUl9DT05UUklWRURfRVJST1IsXG4gICAgICAgICAgICBNQVhfT1BUSU9OU19QRVJfRUxFQ1RJT046IHByb2Nlc3MuZW52Lk1BWF9PUFRJT05TX1BFUl9FTEVDVElPTixcbiAgICAgICAgICAgIE1BWF9SQU5LSU5HU19QRVJfRUxFQ1RJT046IHByb2Nlc3MuZW52Lk1BWF9SQU5LSU5HU19QRVJfRUxFQ1RJT04sXG4gICAgICAgICAgICBNQVhfQ09OVEVOVF9MRU5HVEhfQllURVM6IHByb2Nlc3MuZW52Lk1BWF9DT05URU5UX0xFTkdUSF9CWVRFUyxcbiAgICAgICAgfSxcblxuICAgICAgICAvLyBUT0RPOiBtb3ZlIHRoZXNlIG91dCBvZiBleHBlcmltZW50YWwgd2hlbiB0aGV5J3JlIG5vdCBleHBlcmltZW50YWxcbiAgICAgICAgLy8gVE9ETzogYW55bW9yZSFcbiAgICAgICAgZXhwZXJpbWVudGFsOiB7XG4gICAgICAgICAgICBhc3luYyByZXdyaXRlcygpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2U6ICcvYXBpJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRlc3RpbmF0aW9uOiAnLzQwNCcsXG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZTogJy9hcGkvdjEnLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGVzdGluYXRpb246ICcvNDA0JyxcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlOiAnL2FwaS92MS9lbGVjdGlvbicsXG4gICAgICAgICAgICAgICAgICAgICAgICBkZXN0aW5hdGlvbjogJy80MDQnLFxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2U6ICcvYXBpL3YyJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRlc3RpbmF0aW9uOiAnLzQwNCcsXG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZTogJy92MS9tZXRhJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRlc3RpbmF0aW9uOiAnL2FwaS92MS9tZXRhJ1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2U6ICcvdjEvZWxlY3Rpb25zJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRlc3RpbmF0aW9uOiAnL2FwaS92MS9lbGVjdGlvbnMnXG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZTogJy92MS9lbGVjdGlvbi86aWQnLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGVzdGluYXRpb246ICcvYXBpL3YxL2VsZWN0aW9uLzppZCdcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlOiAnL3YxL2VsZWN0aW9uLzppZC92b3RlcnMnLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGVzdGluYXRpb246ICcvYXBpL3YxL2VsZWN0aW9uLzppZC92b3RlcnMnXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSk7XG59O1xuIl19 \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbmZpZy9uZXh0LmNvbmZpZy50cyJdLCJuYW1lcyI6WyJyZXF1aXJlIiwicG9wdWxhdGVFbnYiLCJwYXRocyIsInVuaXZlcnNlIiwiX19kaXJuYW1lIiwibXVsdGl2ZXJzZSIsIm1vZHVsZSIsImV4cG9ydHMiLCJlbmFibGVkIiwicHJvY2VzcyIsImVudiIsIkFOQUxZWkUiLCJkaXN0RGlyIiwid2VicGFjayIsImNvbmZpZyIsInJlc29sdmUiLCJhbGlhcyIsIk1BWF9MSU1JVCIsIklHTk9SRV9SQVRFX0xJTUlUUyIsIkxPQ0tPVVRfQUxMX0tFWVMiLCJESVNBTExPV0VEX01FVEhPRFMiLCJSRVFVRVNUU19QRVJfQ09OVFJJVkVEX0VSUk9SIiwiTUFYX09QVElPTlNfUEVSX0VMRUNUSU9OIiwiTUFYX1JBTktJTkdTX1BFUl9FTEVDVElPTiIsIk1BWF9DT05URU5UX0xFTkdUSF9CWVRFUyIsImV4cGVyaW1lbnRhbCIsInJld3JpdGVzIiwic291cmNlIiwiZGVzdGluYXRpb24iXSwibWFwcGluZ3MiOiI7O0FBQUE7Ozs7QUFPQUEsT0FBTyxDQUFDLGlCQUFELENBQVAsQ0FBMkJDLFdBQTNCOztBQUVBLE1BQU1DLEtBQUssR0FBRztBQUNWQyxFQUFBQSxRQUFRLEVBQUcsR0FBRUMsU0FBVSxPQURiO0FBRVZDLEVBQUFBLFVBQVUsRUFBRyxHQUFFRCxTQUFVO0FBRmYsQ0FBZDs7QUFLQUUsTUFBTSxDQUFDQyxPQUFQLEdBQWlCLE1BQWM7QUFDM0IsU0FBTyw2QkFBbUI7QUFDdEJDLElBQUFBLE9BQU8sRUFBRUMsT0FBTyxDQUFDQyxHQUFSLENBQVlDLE9BQVosS0FBd0I7QUFEWCxHQUFuQixFQUVKO0FBRUNDLElBQUFBLE9BQU8sRUFBRSxPQUZWO0FBT0NDLElBQUFBLE9BQU8sRUFBR0MsTUFBRCxJQUEyQjtBQUloQ0EsTUFBQUEsTUFBTSxDQUFDQyxPQUFQLEtBQW1CRCxNQUFNLENBQUNDLE9BQVAsQ0FBZUMsS0FBZixHQUF1QixFQUN0QyxHQUFHRixNQUFNLENBQUNDLE9BQVAsQ0FBZUMsS0FEb0I7QUFFdENiLFFBQUFBLFFBQVEsRUFBRUQsS0FBSyxDQUFDQyxRQUZzQjtBQUd0Q0UsUUFBQUEsVUFBVSxFQUFFSCxLQUFLLENBQUNHO0FBSG9CLE9BQTFDO0FBTUEsYUFBT1MsTUFBUDtBQUNILEtBbEJGO0FBdUJDSixJQUFBQSxHQUFHLEVBQUU7QUFDRE8sTUFBQUEsU0FBUyxFQUFFUixPQUFPLENBQUNDLEdBQVIsQ0FBWU8sU0FEdEI7QUFFREMsTUFBQUEsa0JBQWtCLEVBQUVULE9BQU8sQ0FBQ0MsR0FBUixDQUFZUSxrQkFGL0I7QUFHREMsTUFBQUEsZ0JBQWdCLEVBQUVWLE9BQU8sQ0FBQ0MsR0FBUixDQUFZUyxnQkFIN0I7QUFJREMsTUFBQUEsa0JBQWtCLEVBQUVYLE9BQU8sQ0FBQ0MsR0FBUixDQUFZVSxrQkFKL0I7QUFLREMsTUFBQUEsNEJBQTRCLEVBQUVaLE9BQU8sQ0FBQ0MsR0FBUixDQUFZVyw0QkFMekM7QUFNREMsTUFBQUEsd0JBQXdCLEVBQUViLE9BQU8sQ0FBQ0MsR0FBUixDQUFZWSx3QkFOckM7QUFPREMsTUFBQUEseUJBQXlCLEVBQUVkLE9BQU8sQ0FBQ0MsR0FBUixDQUFZYSx5QkFQdEM7QUFRREMsTUFBQUEsd0JBQXdCLEVBQUVmLE9BQU8sQ0FBQ0MsR0FBUixDQUFZYztBQVJyQyxLQXZCTjtBQW9DQ0MsSUFBQUEsWUFBWSxFQUFFO0FBQ1YsWUFBTUMsUUFBTixHQUFpQjtBQUNiLGVBQU8sQ0FDSDtBQUNJQyxVQUFBQSxNQUFNLEVBQUUsVUFEWjtBQUVJQyxVQUFBQSxXQUFXLEVBQUU7QUFGakIsU0FERyxFQUtIO0FBQ0lELFVBQUFBLE1BQU0sRUFBRSxlQURaO0FBRUlDLFVBQUFBLFdBQVcsRUFBRTtBQUZqQixTQUxHLEVBU0g7QUFDSUQsVUFBQUEsTUFBTSxFQUFFLGtCQURaO0FBRUlDLFVBQUFBLFdBQVcsRUFBRTtBQUZqQixTQVRHLEVBYUg7QUFDSUQsVUFBQUEsTUFBTSxFQUFFLHlCQURaO0FBRUlDLFVBQUFBLFdBQVcsRUFBRTtBQUZqQixTQWJHLENBQVA7QUFrQkg7O0FBcEJTO0FBcENmLEdBRkksQ0FBUDtBQTZESCxDQTlERCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB3aXRoQnVuZGxlQW5hbHl6ZXIgZnJvbSAnQG5leHQvYnVuZGxlLWFuYWx5emVyJ1xuXG5pbXBvcnQgdHlwZSB7IENvbmZpZ3VyYXRpb24gfSBmcm9tICd3ZWJwYWNrJ1xuXG4vLyA/IE5vdCB1c2luZyBFUzYvVFMgaW1wb3J0IHN5bnRheCBoZXJlIGJlY2F1c2UgZGV2LXV0aWxzIGhhcyBzcGVjaWFsXG4vLyA/IGNpcmN1bXN0YW5jZXNcbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tdW5yZXNvbHZlZCwgQHR5cGVzY3JpcHQtZXNsaW50L25vLXZhci1yZXF1aXJlc1xucmVxdWlyZSgnLi9zcmMvZGV2LXV0aWxzJykucG9wdWxhdGVFbnYoKTtcblxuY29uc3QgcGF0aHMgPSB7XG4gICAgdW5pdmVyc2U6IGAke19fZGlybmFtZX0vc3JjL2AsXG4gICAgbXVsdGl2ZXJzZTogYCR7X19kaXJuYW1lfS9saWIvYCxcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gKCk6IG9iamVjdCA9PiB7XG4gICAgcmV0dXJuIHdpdGhCdW5kbGVBbmFseXplcih7XG4gICAgICAgIGVuYWJsZWQ6IHByb2Nlc3MuZW52LkFOQUxZWkUgPT09ICd0cnVlJ1xuICAgIH0pKHtcbiAgICAgICAgLy8gPyBSZW5hbWVzIHRoZSBidWlsZCBkaXIgXCJidWlsZFwiIGluc3RlYWQgb2YgXCIubmV4dFwiXG4gICAgICAgIGRpc3REaXI6ICdidWlsZCcsXG5cbiAgICAgICAgLy8gPyBXZWJwYWNrIGNvbmZpZ3VyYXRpb25cbiAgICAgICAgLy8gISBOb3RlIHRoYXQgdGhlIHdlYnBhY2sgY29uZmlndXJhdGlvbiBpcyBleGVjdXRlZCB0d2ljZTogb25jZVxuICAgICAgICAvLyAhIHNlcnZlci1zaWRlIGFuZCBvbmNlIGNsaWVudC1zaWRlIVxuICAgICAgICB3ZWJwYWNrOiAoY29uZmlnOiBDb25maWd1cmF0aW9uKSA9PiB7XG4gICAgICAgICAgICAvLyA/IFRoZXNlIGFyZSBhbGlhc2VzIHRoYXQgY2FuIGJlIHVzZWQgZHVyaW5nIEpTIGltcG9ydCBjYWxsc1xuICAgICAgICAgICAgLy8gISBOb3RlIHRoYXQgeW91IG11c3QgYWxzbyBjaGFuZ2UgdGhlc2Ugc2FtZSBhbGlhc2VzIGluIHRzY29uZmlnLmpzb25cbiAgICAgICAgICAgIC8vICEgTm90ZSB0aGF0IHlvdSBtdXN0IGFsc28gY2hhbmdlIHRoZXNlIHNhbWUgYWxpYXNlcyBpbiBwYWNrYWdlLmpzb24gKGplc3QpXG4gICAgICAgICAgICBjb25maWcucmVzb2x2ZSAmJiAoY29uZmlnLnJlc29sdmUuYWxpYXMgPSB7XG4gICAgICAgICAgICAgICAgLi4uY29uZmlnLnJlc29sdmUuYWxpYXMsXG4gICAgICAgICAgICAgICAgdW5pdmVyc2U6IHBhdGhzLnVuaXZlcnNlLFxuICAgICAgICAgICAgICAgIG11bHRpdmVyc2U6IHBhdGhzLm11bHRpdmVyc2UsXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgcmV0dXJuIGNvbmZpZztcbiAgICAgICAgfSxcblxuICAgICAgICAvLyA/IFNlbGVjdCBzb21lIGVudmlyb25tZW50IHZhcmlhYmxlcyBkZWZpbmVkIGluIC5lbnYgdG8gcHVzaCB0byB0aGVcbiAgICAgICAgLy8gPyBjbGllbnQuXG4gICAgICAgIC8vICEhIERPIE5PVCBQVVQgQU5ZIFNFQ1JFVCBFTlZJUk9OTUVOVCBWQVJJQUJMRVMgSEVSRSAhIVxuICAgICAgICBlbnY6IHtcbiAgICAgICAgICAgIE1BWF9MSU1JVDogcHJvY2Vzcy5lbnYuTUFYX0xJTUlULFxuICAgICAgICAgICAgSUdOT1JFX1JBVEVfTElNSVRTOiBwcm9jZXNzLmVudi5JR05PUkVfUkFURV9MSU1JVFMsXG4gICAgICAgICAgICBMT0NLT1VUX0FMTF9LRVlTOiBwcm9jZXNzLmVudi5MT0NLT1VUX0FMTF9LRVlTLFxuICAgICAgICAgICAgRElTQUxMT1dFRF9NRVRIT0RTOiBwcm9jZXNzLmVudi5ESVNBTExPV0VEX01FVEhPRFMsXG4gICAgICAgICAgICBSRVFVRVNUU19QRVJfQ09OVFJJVkVEX0VSUk9SOiBwcm9jZXNzLmVudi5SRVFVRVNUU19QRVJfQ09OVFJJVkVEX0VSUk9SLFxuICAgICAgICAgICAgTUFYX09QVElPTlNfUEVSX0VMRUNUSU9OOiBwcm9jZXNzLmVudi5NQVhfT1BUSU9OU19QRVJfRUxFQ1RJT04sXG4gICAgICAgICAgICBNQVhfUkFOS0lOR1NfUEVSX0VMRUNUSU9OOiBwcm9jZXNzLmVudi5NQVhfUkFOS0lOR1NfUEVSX0VMRUNUSU9OLFxuICAgICAgICAgICAgTUFYX0NPTlRFTlRfTEVOR1RIX0JZVEVTOiBwcm9jZXNzLmVudi5NQVhfQ09OVEVOVF9MRU5HVEhfQllURVMsXG4gICAgICAgIH0sXG5cbiAgICAgICAgLy8gVE9ETzogbW92ZSB0aGVzZSBvdXQgb2YgZXhwZXJpbWVudGFsIHdoZW4gdGhleSdyZSBub3QgZXhwZXJpbWVudGFsXG4gICAgICAgIC8vIFRPRE86IGFueW1vcmUhXG4gICAgICAgIGV4cGVyaW1lbnRhbDoge1xuICAgICAgICAgICAgYXN5bmMgcmV3cml0ZXMoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlOiAnL3YxL21ldGEnLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGVzdGluYXRpb246ICcvYXBpL3YxL21ldGEnXG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZTogJy92MS9lbGVjdGlvbnMnLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGVzdGluYXRpb246ICcvYXBpL3YxL2VsZWN0aW9ucydcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlOiAnL3YxL2VsZWN0aW9uLzppZCcsXG4gICAgICAgICAgICAgICAgICAgICAgICBkZXN0aW5hdGlvbjogJy9hcGkvdjEvZWxlY3Rpb24vOmlkJ1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2U6ICcvdjEvZWxlY3Rpb24vOmlkL3ZvdGVycycsXG4gICAgICAgICAgICAgICAgICAgICAgICBkZXN0aW5hdGlvbjogJy9hcGkvdjEvZWxlY3Rpb24vOmlkL3ZvdGVycydcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIF07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9KTtcbn07XG4iXX0= \ No newline at end of file diff --git a/package.json b/package.json index ecbcefb..319e09d 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "cors": "^2.8.5", "dotenv": "^8.2.0", "dotenv-webpack": "^1.8.0", - "eslint-import-resolver-alias": "^1.1.2", "fast-shuffle": "^3.0.0", "isomorphic-unfetch": "^3.0.0", "mongodb": "^3.5.7", @@ -111,6 +110,7 @@ "eslint-plugin-import": "^2.20.2", "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-react": "^7.20.0", + "eslint-import-resolver-alias": "^1.1.2", "fancy-log": "^1.3.3", "gulp": "^4.0.2", "gulp-tap": "^2.0.0", diff --git a/src/__test__/db.ts b/src/__test__/db.ts index 01795d2..b72ece7 100644 --- a/src/__test__/db.ts +++ b/src/__test__/db.ts @@ -148,7 +148,6 @@ export const unhydratedDummyDbData: DummyDbData = { ].flat() }; -// TODO: not idempotent; elections will be duplicated if called twice export async function hydrateDb(db: Db, data: DummyDbData): Promise { const newData = { ...data }; diff --git a/src/backend/db.ts b/src/backend/db.ts index 443b248..a3f4cf9 100644 --- a/src/backend/db.ts +++ b/src/backend/db.ts @@ -17,7 +17,10 @@ export async function getDb(): Promise { */ export function setDb(newDB: Db): void { db = newDB; } -// TODO: document +/** + * Destroys all collections in the database. Can be called multiple times + * safely. + */ export async function destroyDb(db: Db) { await Promise.allSettled([ db.dropCollection('keys'), @@ -28,13 +31,12 @@ export async function destroyDb(db: Db) { ]); } -// TODO: document that this function is idempotent and can be called on -// TODO: conformant databases that already have the appropriate structure -// TODO: without worry of data loss +/** + * This function is idempotent and can be called without worry of data loss. + */ export async function initializeDb(db: Db): Promise { - // TODO: add validation rules during createCollection phase - // TODO: (including special 0 root key not accepted in keys or in API) - // TODO: also make an index over key in keys (if not exists) + // TODO: Add validation rules during createCollection phase + // TODO: Make an index over key in keys (if not exists) await Promise.all([ db.createCollection('keys'), diff --git a/src/backend/index.ts b/src/backend/index.ts index 5072e5d..9ea8439 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -222,8 +222,6 @@ export async function replaceRankings({ electionId, rankings }: EleVotRanParams) throw new GuruMeditationError(); } -// TODO: document that either 1) NewElection or 2) PatchElection, electionId, -// TODO: key are the two required sets of parameters (overloaded) export async function upsertElection(opts: UpsNewEleParams): Promise export async function upsertElection(opts: UpsPatEleParams): Promise export async function upsertElection(opts: UpsNewEleParams | UpsPatEleParams): Promise { @@ -372,8 +370,10 @@ export async function deleteElection(electionId: ObjectId): Promise { ); } -// TODO: document that it's okay not to await this function, it's fire and -// TODO: forget +/** + * Note that this async function does not have to be awaited. It's fire and + * forget! + */ export async function addToRequestLog({ req, res }: NextParamsRR): Promise { const logEntry: RequestLogEntry = { ip: getClientIp(req), diff --git a/src/backend/middleware.ts b/src/backend/middleware.ts index 1dc73cc..9f307a7 100644 --- a/src/backend/middleware.ts +++ b/src/backend/middleware.ts @@ -32,9 +32,7 @@ import type { NextParamsRR } from 'types/global' export type GenHanParams = NextParamsRR & { methods: string[] }; export type AsyncHanCallback = (params: NextParamsRR) => Promise; -const cors = Cors({ - methods: ['GET', 'POST', 'PUT', 'DELETE'], -}); +const cors = Cors({ methods: ['GET', 'POST', 'PUT', 'DELETE'] }); /* eslint-disable @typescript-eslint/no-explicit-any */ const runCorsMiddleware = (req: any, res: any) => { diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 6a90812..af388fa 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -5,7 +5,7 @@ import Head from 'next/head' import type { AppProps } from 'next/app' -export default function App({ Component, pageProps }: AppProps): JSX.Element { +export default function App({ Component, pageProps }: AppProps) { return ( diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 927565f..b302a45 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -35,7 +35,7 @@ export async function getServerSideProps() { return { props }; } -export default function Index({ previouslyHydratedDb, shouldHydrateDb, isInProduction, nodeEnv }: Props): JSX.Element { +export default function Index({ previouslyHydratedDb, shouldHydrateDb, isInProduction, nodeEnv }: Props) { let status = (unchanged); if(previouslyHydratedDb) diff --git a/types/_shared.ts b/types/_shared.ts index 51f8045..52252ab 100644 --- a/types/_shared.ts +++ b/types/_shared.ts @@ -3,19 +3,6 @@ import type { NextApiRequest, NextApiResponse } from 'next' - -// TODO: jsdoc comment/document all of this stuff like the below: -/** - * @desc Type representing - * [`Primitive`](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) - * types. - * - * @example - * type Various = number | string | object; - * - * /// Expect: object - * type Cleaned = Exclude - */ export type Primitive = | string | number @@ -25,14 +12,6 @@ export type Primitive = | null | undefined; -/** - * @desc Type representing falsy values. - * @example - * type Various = 'a' | 'b' | undefined | false; - * - * /// Expect: "a" | "b" - * Exclude; - */ export type Falsy = false | '' | 0 | null | undefined; export type SuccessJsonResponse = { success: true }; diff --git a/types/global.ts b/types/global.ts index 352a33f..f023750 100644 --- a/types/global.ts +++ b/types/global.ts @@ -1,11 +1,11 @@ -import type { HttpJsonResponse5xx } from './_shared' import type { ObjectId, WithId } from 'mongodb' // ? Access types shared between projects from `types/global` too export * from './_shared'; -// * Project-specific Types * \\ - +/** + * Base election type. + */ export type PrimitiveElection = { election_id: ObjectId; title: string; @@ -17,6 +17,9 @@ export type PrimitiveElection = { deleted: boolean; }; +/** + * The shape of the request body when creating a new election. + */ export type NewElection = { title: string; description?: string; @@ -25,6 +28,9 @@ export type NewElection = { closes: number; }; +/** + * The shape of the request body when modifying an election. + */ export type PatchElection = { title?: string; description?: string; @@ -34,27 +40,48 @@ export type PatchElection = { deleted?: boolean; }; +/** + * The shape of an election in NextJS. + */ export type InternalElection = PrimitiveElection & { owner: string }; + +/** + * The shape of the response body when sending election data. + */ export type PublicElection = PrimitiveElection & { owned: boolean }; -// TODO: document the differences between election types +/** + * The shape of an election in MongoDB. + */ export type InDbElection = WithId>; +/** + * The shape of an API key. + */ export type ApiKey = { owner: string; key: string; } +/** + * The shape of a single voter's rankings. + */ export type VoterRanking = { voter_id: string; ranking: string[]; }; +/** + * The shape of a single election's voters and their rankings. + */ export type ElectionRankings = { election_id: ObjectId; rankings: VoterRanking[]; }; +/** + * The shape of a request log entry. + */ export type RequestLogEntry = { ip: string | null; key: string | null; @@ -64,16 +91,20 @@ export type RequestLogEntry = { time: number; }; +/** + * The shape of a limited log entry. + */ export type LimitedLogEntry = { until: number; ip: string | null; key: string | null; }; +/** + * The shape of system metadata. + */ export type Metadata = { upcomingElections: number; openElections: number; closedElections: number; }; - -export type HTTP555 = HttpJsonResponse5xx & { contrived: true };