Skip to content

Commit

Permalink
Merge pull request #44 from lifeomic/new-koa-router
Browse files Browse the repository at this point in the history
feat: test against @koa/router and clean up typing
  • Loading branch information
swain authored Jul 28, 2022
2 parents 595f631 + 4b7ddc0 commit d62cb31
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 49 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ If you're building a Koa app, you can use these generated types with the `implem
```typescript
// app.ts
import Koa from 'koa';
import Router from 'koa-router';
import Router from '@koa/router';
import { implementSchema } from '@lifeomic/one-schema';
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@
"yargs": "^17.5.1"
},
"devDependencies": {
"@koa/router": "^12.0.0",
"@lifeomic/eslint-config-standards": "^2.1.1",
"@lifeomic/typescript-config": "^1.0.3",
"@types/axios": "^0.14.0",
"@types/jest": "^27.5.1",
"@types/js-yaml": "^4.0.5",
"@types/koa": "^2.13.4",
"@types/koa-bodyparser": "^4.3.7",
"@types/koa-router": "^7.4.4",
"@types/koa__router": "^8.0.11",
"@types/node": "^14.0.0",
"@types/tmp": "^0.2.3",
"@types/yargs": "^17.0.10",
Expand All @@ -45,7 +46,6 @@
"jest": "^28.1.0",
"koa": "^2.13.4",
"koa-bodyparser": "^4.3.0",
"koa-router": "^10.1.1",
"openapi-schema-validator": "^11.0.1",
"prettier": "^2.6.2",
"semantic-release": "^19.0.3",
Expand Down
2 changes: 1 addition & 1 deletion src/integration.koa.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios, { AxiosInstance } from 'axios';
import Koa = require('koa');
import Router = require('koa-router');
import Router = require('@koa/router');
import bodyparser = require('koa-bodyparser');
import { ImplementationConfig, implementSchema, OneSchemaDefinition } from '.';
import { withAssumptions } from './meta-schema';
Expand Down
2 changes: 1 addition & 1 deletion src/koa.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from 'axios';
import Koa = require('koa');
import bodyParser = require('koa-bodyparser');
import Router = require('koa-router');
import Router = require('@koa/router');
import { implementSchema } from '.';
import { withAssumptions } from './meta-schema';

Expand Down
51 changes: 29 additions & 22 deletions src/koa.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { JSONSchema4 } from 'json-schema';
import type { ParameterizedContext } from 'koa';
import type Router from 'koa-router';
import type { ExtendableContext, ParameterizedContext } from 'koa';
import type Router from '@koa/router';
import type { EndpointsOf, IntrospectionResponse, OneSchema } from './types';

/**
* We use this type to very cleanly remove these fields from the Koa context, so
* that we can replace the fields with our strict types from the generated schema.
*
* - `params`
* - `request.body`
* - `request.query`
*
Expand All @@ -24,29 +23,37 @@ import type { EndpointsOf, IntrospectionResponse, OneSchema } from './types';
* By explicitly removing the fields from the context, then re-adding them, we can be
* sure they are typed correctly.
*/
type WithTypedFieldsRemoved<T> =
// Omit params and request
Omit<T, 'params' | 'request'> & {
type ExtendableContextWithRequestFieldsRemoved =
// Omit request entirely
Omit<ExtendableContext, 'request'> & {
// Re-add request, but without the "body" or "query" fields.
request: Omit<T, 'body' | 'query'>;
request: Omit<ExtendableContext['request'], 'body' | 'query'>;
};

export type ImplementationOf<Schema extends OneSchema<any>, State, Context> = {
[Name in keyof EndpointsOf<Schema>]: (
// It's important that we remove our "typed" fields from the root `ParameterizedContext`,
// and not from the "inner" `Context` type.
//
// If we remove from the "inner" `Context` type, the `ParameterizedContext` will
// effectively just "re-add" the fields we removed.
//
// Basically, the "inner" Context can only be used for _extending_ the context,
// but not for _restricting_ it.
context: WithTypedFieldsRemoved<ParameterizedContext<State, Context>> & {
params: EndpointsOf<Schema>[Name]['PathParams'];
request: Name extends `${'GET' | 'DELETE'} ${string}`
? { query: EndpointsOf<Schema>[Name]['Request'] }
: { body: EndpointsOf<Schema>[Name]['Request'] };
},
// prettier-ignore
context:
// 1. Start with a context that has request.body and request.query removed.
// This context also importantly does _not_ have the `params` property included,
// since it comes from a core `koa` type, rather than from `@koa/router`.
& ExtendableContextWithRequestFieldsRemoved
// 2. Now, we add the generated + well-typed `params`, `request.body`, and
// `request.query` properties.
& {
params: EndpointsOf<Schema>[Name]['PathParams'];
request: Name extends `${'GET' | 'DELETE'} ${string}`
? { query: EndpointsOf<Schema>[Name]['Request'] }
: { body: EndpointsOf<Schema>[Name]['Request'] };
}
// 3. Now, add the `state` property and merge in the arbitrary custom context, to
// essentially mimic the behavior of koa's `ParameterizedContext`.
//
// Why not just use ParameterizedContext: When we tried to use ParameterizedContext
// directly, it was incompatible with Omit (omitting a single property resulted in
// a fully empty object).
& { state: State; }
& Context,
) =>
| EndpointsOf<Schema>[Name]['Response']
| Promise<EndpointsOf<Schema>[Name]['Response']>;
Expand Down Expand Up @@ -142,7 +149,7 @@ export const implementSchema = <State, Context, Schema extends OneSchema<any>>(
const [method, path] = endpoint.split(' ');

/** A shared route handler. */
const handler: Router.IMiddleware<State, Context> = async (ctx, next) => {
const handler: Router.Middleware<State, Context> = async (ctx, next) => {
// 1. Validate the input data.
const requestSchema = schema.Endpoints[endpoint].Request;
if (requestSchema) {
Expand Down
43 changes: 21 additions & 22 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,16 @@
resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==

"@koa/router@^12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@koa/router/-/router-12.0.0.tgz#2ae7937093fd392761c0e5833c368379d4a35737"
integrity sha512-cnnxeKHXlt7XARJptflGURdJaO+ITpNkOHmQu7NHmCoRinPbyvFzce/EG/E8Zy81yQ1W9MoSdtklc3nyaDReUw==
dependencies:
http-errors "^2.0.0"
koa-compose "^4.1.0"
methods "^1.1.2"
path-to-regexp "^6.2.1"

"@lifeomic/alpha@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@lifeomic/alpha/-/alpha-4.0.1.tgz#e7b5c8eddf454affbdc380a973d10407be11c5d8"
Expand Down Expand Up @@ -1248,13 +1258,6 @@
dependencies:
"@types/koa" "*"

"@types/koa-router@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@types/koa-router/-/koa-router-7.4.4.tgz#db72bde3616365d74f00178d5f243c4fce7da572"
integrity sha512-3dHlZ6CkhgcWeF6wafEUvyyqjWYfKmev3vy1PtOmr0mBc3wpXPU5E8fBBd4YQo5bRpHPfmwC5yDaX7s4jhIN6A==
dependencies:
"@types/koa" "*"

"@types/koa@*", "@types/koa@^2.13.4":
version "2.13.4"
resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.4.tgz#10620b3f24a8027ef5cbae88b393d1b31205726b"
Expand All @@ -1269,6 +1272,13 @@
"@types/koa-compose" "*"
"@types/node" "*"

"@types/koa__router@^8.0.11":
version "8.0.11"
resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-8.0.11.tgz#d7b37e6db934fc072ea1baa2ab92bc8ac4564f3e"
integrity sha512-WXgKWpBsbS14kzmzD9LeFapOIa678h7zvUHxDwXwSx4ETKXhXLVUAToX6jZ/U7EihM7qwyD9W/BZvB0MRu7MTQ==
dependencies:
"@types/koa" "*"

"@types/lodash@^4.14.168", "@types/lodash@^4.14.181":
version "4.14.182"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2"
Expand Down Expand Up @@ -3199,7 +3209,7 @@ http-cache-semantics@^4.1.0:
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==

[email protected]:
[email protected], http-errors@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
Expand All @@ -3210,7 +3220,7 @@ [email protected]:
statuses "2.0.1"
toidentifier "1.0.1"

http-errors@^1.6.3, http-errors@^1.7.3, http-errors@~1.8.0:
http-errors@^1.6.3, http-errors@~1.8.0:
version "1.8.1"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c"
integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==
Expand Down Expand Up @@ -4105,17 +4115,6 @@ koa-convert@^2.0.0:
co "^4.6.0"
koa-compose "^4.1.0"

koa-router@^10.1.1:
version "10.1.1"
resolved "https://registry.yarnpkg.com/koa-router/-/koa-router-10.1.1.tgz#20809f82648518b84726cd445037813cd99f17ff"
integrity sha512-z/OzxVjf5NyuNO3t9nJpx7e1oR3FSBAauiwXtMQu4ppcnuNZzTaQ4p21P8A6r2Es8uJJM339oc4oVW+qX7SqnQ==
dependencies:
debug "^4.1.1"
http-errors "^1.7.3"
koa-compose "^4.1.0"
methods "^1.1.2"
path-to-regexp "^6.1.0"

koa@^2.13.4:
version "2.13.4"
resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.4.tgz#ee5b0cb39e0b8069c38d115139c774833d32462e"
Expand Down Expand Up @@ -4502,7 +4501,7 @@ merge2@^1.3.0, merge2@^1.4.1:
methods@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==

micromatch@^4.0.2, micromatch@^4.0.4:
version "4.0.5"
Expand Down Expand Up @@ -5239,7 +5238,7 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==

path-to-regexp@^6.1.0:
path-to-regexp@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5"
integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==
Expand Down

0 comments on commit d62cb31

Please sign in to comment.