From 01e93da241aefee2986d18e0e9367db93ea02503 Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 25 Jan 2022 20:03:53 +0000 Subject: [PATCH 1/3] Add .npmignore for files that are not in dist --- .npmignore | 3 + __tests__/compiler.test.ts | 3 +- __tests__/integration/fixtures/index.ts | 2 +- lib/compiler.ts | 73 ------------------------- package.json | 4 +- tsconfig.json | 7 ++- 6 files changed, 14 insertions(+), 78 deletions(-) create mode 100644 .npmignore delete mode 100644 lib/compiler.ts diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..fe5f9d2 --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +__tests__ +.* +dist diff --git a/__tests__/compiler.test.ts b/__tests__/compiler.test.ts index 934d82d..640b018 100644 --- a/__tests__/compiler.test.ts +++ b/__tests__/compiler.test.ts @@ -1,5 +1,4 @@ -import { OpenAPIObject, SchemaObject } from 'openapi3-ts'; -import { APICompiler } from './../lib/compiler'; +import { APICompiler } from '../src'; const spec = { info: { diff --git a/__tests__/integration/fixtures/index.ts b/__tests__/integration/fixtures/index.ts index d181a2d..25847c1 100644 --- a/__tests__/integration/fixtures/index.ts +++ b/__tests__/integration/fixtures/index.ts @@ -1,5 +1,5 @@ import { OpenAPISpecification } from './openapi/spec'; -import { APICompiler } from '../../../lib/compiler'; +import { APICompiler } from '../../../src'; const API = APICompiler(OpenAPISpecification); const { response, request } = API('/pets', 'post'); diff --git a/lib/compiler.ts b/lib/compiler.ts deleted file mode 100644 index 2faa48f..0000000 --- a/lib/compiler.ts +++ /dev/null @@ -1,73 +0,0 @@ -import Ajv, { JSONSchemaType } from 'ajv'; -import { FromSchema } from 'json-schema-to-ts'; -import { OpenAPIObject } from 'openapi3-ts'; - -/** - * The open API Compiler will take in an OpenAPI specification and return type- - * safe utilities for handling requests, and responses. - * - * It does this by requiring the OpenAPI schema to be composed of Typescript - * classes that define the Components and Schema's of the paths. - * - * @param {OpenAPIObject} spec - The OpenAPI specification document - * @param {string} contentType - The content type of requests and responses - * @default 'application/json' - * @returns - */ -export const APICompiler = < - T extends OpenAPIObject, - U extends string = 'application/json' ->( - spec: T, - contentType = 'application/json' -) => { - return < - P extends keyof T['paths'], - M extends keyof T['paths'][P], - S extends T['paths'][P][M]['responses'] - >( - route: P, - method: M - ) => { - const path = route as string; - - const response = < - R extends keyof S, - SC extends T['paths'][P][M]['responses'][R]['content'][U]['schema'] - >( - statusCode: R, - body: FromSchema - ) => ({ - statusCode, - body: JSON.stringify(body), - }); - - const validateRequestBody = < - SC extends T['paths'][P][M]['requestBody']['content'][U]['schema'] - >() => { - const { requestBody } = spec.paths[path][method]; - - const ajv = new Ajv({ - allErrors: true, - }); - - if (requestBody) { - const schema = requestBody.content[contentType] - .schema as JSONSchemaType>; - - return ajv.compile(schema); - } else { - return ajv.compile({}); - } - }; - - const validator = validateRequestBody(); - - return { - response, - request: { - validator, - }, - }; - }; -}; diff --git a/package.json b/package.json index 8a30e16..37980e5 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,11 @@ "name": "combined", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "dist/index.js", "scripts": { "test": "jest", + "build": "tsc --build", + "clean": "tsc --build --clean", "prepare": "husky install" }, "keywords": [], diff --git a/tsconfig.json b/tsconfig.json index 2d7b0b3..bb26ef9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,8 @@ { - "extends": "@tsconfig/node14" + "extends": "@tsconfig/node14", + "include": ["src/**/*.ts"], + "compilerOptions": { + "outDir": "./dist", + "declaration": true + } } From fea760837fbfcf1aa27cc921d6ac73bcd8693e42 Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 25 Jan 2022 20:15:55 +0000 Subject: [PATCH 2/3] Setup standard pattern for imports and exports as would be expected in es modules --- .gitignore | 4 ++++ .npmignore | 3 --- README.md | 8 ++++---- __tests__/compiler.test.ts | 4 ++-- __tests__/integration/fixtures/index.ts | 4 ++-- package.json | 9 +++++++-- src/index.ts | 1 + 7 files changed, 20 insertions(+), 13 deletions(-) delete mode 100644 .npmignore create mode 100644 src/index.ts diff --git a/.gitignore b/.gitignore index 3c3629e..f9e4ec2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ node_modules + +dist + +*.tgz diff --git a/.npmignore b/.npmignore deleted file mode 100644 index fe5f9d2..0000000 --- a/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -__tests__ -.* -dist diff --git a/README.md b/README.md index 83dace9..259e4f9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# Compel +# Compeller A strong typescript binding for your OpenAPI Schema that doesn't need generation. -- [Compel](#compel) +- [Compeller](#compeller) - [About](#about) - [Shoulders](#shoulders) ## About -Compel tries to infer your OpenAPI validations and responses, from a typed OpenAPI specification. +Compeller tries to infer your OpenAPI validations and responses, from a typed OpenAPI specification. Say you had the following specification: @@ -52,7 +52,7 @@ With compel you can compile this into a typed request and response handler like: ```ts import {openAPISpec} from './spec'; -const stuff = APICompiler(spec); +const stuff = compeller(spec); const { response } = stuff('/test', 'get'); diff --git a/__tests__/compiler.test.ts b/__tests__/compiler.test.ts index 640b018..d25a4e0 100644 --- a/__tests__/compiler.test.ts +++ b/__tests__/compiler.test.ts @@ -1,4 +1,4 @@ -import { APICompiler } from '../src'; +import { compeller } from '../src'; const spec = { info: { @@ -36,7 +36,7 @@ const spec = { describe('API Compiler tests', () => { describe('get requests', () => { it('requires a valid API document', () => { - const stuff = APICompiler(spec); + const stuff = compeller(spec); const { response } = stuff('/test', 'get'); diff --git a/__tests__/integration/fixtures/index.ts b/__tests__/integration/fixtures/index.ts index 25847c1..ca512db 100644 --- a/__tests__/integration/fixtures/index.ts +++ b/__tests__/integration/fixtures/index.ts @@ -1,7 +1,7 @@ import { OpenAPISpecification } from './openapi/spec'; -import { APICompiler } from '../../../src'; +import { compeller } from '../../../src'; -const API = APICompiler(OpenAPISpecification); +const API = compeller(OpenAPISpecification); const { response, request } = API('/pets', 'post'); export const handler = (data: Record) => { diff --git a/package.json b/package.json index 37980e5..c3904a7 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "combined", + "name": "compeller", "version": "1.0.0", "description": "", "main": "dist/index.js", @@ -7,10 +7,15 @@ "test": "jest", "build": "tsc --build", "clean": "tsc --build --clean", - "prepare": "husky install" + "prepare": "husky install", + "prepack": "yarn clean && yarn build" }, "keywords": [], "author": "simon", + "files": [ + "dist/**/*", + "README.md" + ], "license": "ISC", "dependencies": { "ajv": "^8.9.0", diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..cd7893a --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export { compeller } from './compeller'; From b1bb08cfeb3764283951e9c7dcc5cd2793c3fb7c Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 25 Jan 2022 20:16:47 +0000 Subject: [PATCH 3/3] Create domain named files for es module --- src/compeller.ts | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/compeller.ts diff --git a/src/compeller.ts b/src/compeller.ts new file mode 100644 index 0000000..bdc646e --- /dev/null +++ b/src/compeller.ts @@ -0,0 +1,76 @@ +import Ajv, { JSONSchemaType } from 'ajv'; +import { FromSchema } from 'json-schema-to-ts'; +import { OpenAPIObject } from 'openapi3-ts'; + +/** + * The open API Compiler will take in an OpenAPI specification and return type- + * safe utilities for handling requests, and responses. + * + * It does this by requiring the OpenAPI schema to be composed of Typescript + * classes that define the Components and Schema's of the paths. + * + * @param {OpenAPIObject} spec - The OpenAPI specification document + * @param {string} contentType - The content type of requests and responses + * @default 'application/json' + * @returns + */ +export const compeller = < + T extends OpenAPIObject, + U extends string = 'application/json' +>( + spec: T, + contentType = 'application/json' +) => { + return < + P extends keyof T['paths'], + M extends keyof T['paths'][P], + S extends T['paths'][P][M]['responses'] + >( + route: P, + method: M + ) => { + const path = route as string; + + const response = < + R extends keyof S, + SC extends T['paths'][P][M]['responses'][R]['content'][U]['schema'] + >( + statusCode: R, + body: FromSchema + ) => ({ + statusCode, + body: JSON.stringify(body), + }); + + const validateRequestBody = < + SC extends T['paths'][P][M]['requestBody']['content'][U]['schema'] + >() => { + const { + requestBody: { + content: { [contentType]: { schema = undefined } = {} } = {}, + } = {}, + } = spec.paths[path][method]; + + const unsafeSchema = schema as JSONSchemaType>; + + const ajv = new Ajv({ + allErrors: true, + }); + + if (unsafeSchema) { + return ajv.compile(unsafeSchema); + } else { + return ajv.compile({}); + } + }; + + const validator = validateRequestBody(); + + return { + response, + request: { + validator, + }, + }; + }; +};