Skip to content

Commit

Permalink
Reorganize modules, set build output
Browse files Browse the repository at this point in the history
  • Loading branch information
epiphone committed Dec 9, 2019
1 parent 43b541e commit 2510cd5
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 60 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,5 @@ typings/

# next.js build output
.next

build/
2 changes: 1 addition & 1 deletion PetStoreSchema.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OpenAPIObject } from './OpenAPI'
import { OpenAPIObject } from './src/OpenAPI'

export default interface OpenAPISchema extends OpenAPIObject {
openapi: '3.0.0'
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"name": "express-openapi-typer",
"version": "0.0.1",
"description": "Code-generation-free conversion of OpenAPI schema into typed Express request handlers",
"main": "index.js",
"main": "build/index.js",
"types": "build/index.d.ts",
"scripts": {
"build": "tsc -p tsconfig.json",
"lint": "tslint --project ."
Expand Down
3 changes: 1 addition & 2 deletions sample.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as express from 'express'
import { OpenAPIRouter } from '.'
import PetStoreSchema from './PetStoreSchema'
import { OpenAPIRouter } from './src'

const router = (express.Router() as unknown) as OpenAPIRouter<PetStoreSchema>

Expand All @@ -16,6 +16,5 @@ router.delete('/pets/{id}', (req, res, next) => {
})

router.post('/pets', (req, res, next) => {
// req.body autocompletes to `{ [x: string]: JSONValue; name: string; tag?: string | undefined; }`
res.send({ name: req.body.name, id: 1 })
})
27 changes: 27 additions & 0 deletions src/JSONSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as JSONSchemaTypeMapper from 'json-schema-type-mapper'
import { OpenAPIObject } from '.'
import { ValueOf } from './util'

// Digging up schemas by hand since json-schema-type-mapper expects to find them
// under `/definitions`, not in OpenAPI's `/components/schemas`:
export type Schemas<S extends OpenAPIObject> = S['components']['schemas']

export type SchemaIds<S extends OpenAPIObject> = Exclude<
ValueOf<Schemas<S>>['$id'],
undefined
>

export type SchemasById<S extends OpenAPIObject> = {
[Id in SchemaIds<S>]: ValueOf<
{
[P in keyof Schemas<S>]: Schemas<S>[P]['$id'] extends Id
? Schemas<S>[P]
: never
}
>
}

export type JSONSchema<
T,
S extends OpenAPIObject
> = JSONSchemaTypeMapper.Schema<T, SchemasById<S>>
File renamed without changes.
71 changes: 18 additions & 53 deletions index.ts → src/index.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,15 @@
import * as express from 'express-serve-static-core' // tslint:disable-line:no-implicit-dependencies
import * as JSONSchemaTypeMapper from 'json-schema-type-mapper'
import { JSONSchema } from './JSONSchema'
import * as oa from './OpenAPI'
import { Compute, UnionToIntersection, ValueOf } from './util'

// TODO: for now assuming schema.components.schemas is populated and doesn't contain `$ref` objects
type OpenAPIObject = oa.OpenAPIObject & {
export type OpenAPIObject = oa.OpenAPIObject & {
components: {
schemas: Record<string, oa.SchemaObject>
}
}

// Digging up schemas by hand since json-schema-type-mapper expects to find them
// under `/definitions`, not in OpenAPI's `/components/schemas`:

export type Schemas<S extends OpenAPIObject> = S['components']['schemas']

export type SchemaIds<S extends OpenAPIObject> = Exclude<
ValueOf<Schemas<S>>['$id'],
undefined
>

export type SchemasById<S extends OpenAPIObject> = {
[Id in SchemaIds<S>]: ValueOf<
{
[P in keyof Schemas<S>]: Schemas<S>[P]['$id'] extends Id
? Schemas<S>[P]
: never
}
>
}

export type JSONSchema<
T,
S extends OpenAPIObject
> = JSONSchemaTypeMapper.Schema<T, SchemasById<S>>

/**
* Force TS to load a type that has not been computed
* https://github.com/pirix-gh/ts-toolbelt
*/
export type Compute<A extends any> = A extends Function // tslint:disable-line:ban-types
? A
: { [K in keyof A]: A[K] } & {}

/**
* Courtesy of @jcalz at https://stackoverflow.com/a/50375286/1763012
*/
type UnionToIntersection<U> = (U extends any
? (k: U) => void
: never) extends (k: infer I) => void
? I
: never

type ValueOf<T extends object> = T[keyof T]

export type RequestBody<
S extends OpenAPIObject,
Path extends keyof S['paths'],
Expand All @@ -63,6 +20,19 @@ export type RequestBody<
| (O['requestBody']['required'] extends true ? never : undefined)
>

export type ResponseBody<
S extends OpenAPIObject,
Path extends keyof S['paths'],
Method extends keyof S['paths'][Path]
> = Compute<
JSONSchema<
ValueOf<
S['paths'][Path][Method]['responses']
>['content']['application/json']['schema'],
S
>
>

export type ParametersIn<
S extends OpenAPIObject,
Path extends keyof S['paths'],
Expand Down Expand Up @@ -98,16 +68,11 @@ export type Operation<S extends OpenAPIObject> = ValueOf<
params: Parameters<S, Path, Method, 'path'>
query: Parameters<S, Path, Method, 'query'>

// TODO handle different content types and `required`:
// TODO handle different content types:
requestBody: RequestBody<S, Path, Method>

// TODO handle different content types, headers and status codes:
responseBody: JSONSchema<
ValueOf<
S['paths'][Path][Method]['responses']
>['content']['application/json']['schema'],
S
>
responseBody: ResponseBody<S, Path, Method>
}
}
>
Expand Down
18 changes: 18 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Force TS to load a type that has not been computed
* https://github.com/pirix-gh/ts-toolbelt
*/
export type Compute<A extends any> = A extends Function // tslint:disable-line:ban-types
? A
: { [K in keyof A]: A[K] } & {}

/**
* Courtesy of @jcalz at https://stackoverflow.com/a/50375286/1763012
*/
export type UnionToIntersection<U> = (U extends any
? (k: U) => void
: never) extends (k: infer I) => void
? I
: never

export type ValueOf<T extends object> = T[keyof T]
10 changes: 7 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"declaration": true,
"moduleResolution": "node",
"noEmit": true,
"strict": true
}
"outDir": "build",
"strict": true,
},
"include": [
"./src/**/*"
]
}

0 comments on commit 2510cd5

Please sign in to comment.