openapi-to-typescript
Advanced tools
Comparing version 3.1.0 to 3.2.0
@@ -13,3 +13,3 @@ "use strict"; | ||
const { typeStore, clientStore } = await _1.GenerateTypings(JSON.parse(fs_1.default.readFileSync(`${argv.input}`).toString()), { | ||
operationFormatters: [formatters_1.FetchClientFormatter] | ||
operationFormatters: [new formatters_1.FetchClientFormatter] | ||
}); | ||
@@ -38,3 +38,2 @@ const outputFileWithoutExtension = `${argv.output === true ? argv.input : argv.output}`.replace(/(\.d)?\.ts$/, ''); | ||
describe: 'File to read from', | ||
default: false | ||
}) | ||
@@ -41,0 +40,0 @@ .option('output', { |
@@ -6,9 +6,2 @@ "use strict"; | ||
exports.Formatter = Formatter; | ||
class OperationFormatter extends Formatter { | ||
constructor(operation) { | ||
super(); | ||
this.operation = operation; | ||
} | ||
} | ||
exports.OperationFormatter = OperationFormatter; | ||
//# sourceMappingURL=formatter.js.map |
@@ -5,44 +5,48 @@ "use strict"; | ||
const formatter_1 = require("../formatter"); | ||
class FetchClientFormatter extends formatter_1.OperationFormatter { | ||
static async renderBoilerplate(apiSchema) { | ||
return [ | ||
`const fetch = require('node-fetch')`, | ||
`const pick = (obj:any, keys:string[]) => keys.reduce((picked, key) => obj[key] !== undefined ? Object.assign(picked, {[key]: obj[key]}) : picked, {})`, | ||
`const encodeQuery = (obj:any, keys:string[]) => require('querystring').encode(pick(obj, keys))`, | ||
`const API_URL = ${JSON.stringify(lodash_1.get(apiSchema, 'servers[0].url'))}`, | ||
].join('\n'); | ||
const withoutIndentation = (s) => s.replace(/(\s*[\n]+)\s*/g, '\n').trim(); | ||
const withoutEmptyLines = (s) => s.replace(/\n+/g, '\n'); | ||
class FetchClientFormatter extends formatter_1.Formatter { | ||
constructor({ url } = {}) { | ||
super(); | ||
this.url = url; | ||
} | ||
async render() { | ||
const { operationTypeName } = this.names(); | ||
return { | ||
[operationTypeName]: this.renderActionType(), | ||
}; | ||
async renderBoilerplate(apiSchema) { | ||
const url = this.url || lodash_1.get(apiSchema, 'servers[0].url'); | ||
return withoutIndentation(` | ||
const fetch = require('node-fetch') | ||
const pick = (obj:any, keys:string[]) => keys.reduce((picked, key) => obj[key] !== undefined ? Object.assign(picked, {[key]: obj[key]}) : picked, {}) | ||
const substitutePath = (pathName:string, body:any) => pathName.split('/').map(dir => dir.startsWith('{') ? body[dir.slice(1,-1)] : dir).join('/') | ||
const encodeQuery = (obj:any, keys:string[]) => require('querystring').encode(pick(obj, keys)) | ||
const API_URL = ${JSON.stringify(url)} | ||
`); | ||
} | ||
renderActionType() { | ||
const { operationName, requestTypeName, responseTypeName } = this.names(); | ||
return `export type ${operationName} = (payload: ${requestTypeName}) => Promise<${responseTypeName}>;`; | ||
async render(operation) { | ||
const { operationTypeName, operationName, requestTypeName, responseTypeName } = this.names(operation); | ||
const typedef = `export type ${operationName} = (payload: ${requestTypeName}) => Promise<${responseTypeName}>;`; | ||
return { [operationTypeName]: typedef }; | ||
} | ||
async renderAction() { | ||
const { operationName, requestTypeName, responseTypeName } = this.names(); | ||
const queryParameterNames = this.operation.route.parameters | ||
.filter(param => param.in === 'query') | ||
.map(param => param.name); | ||
const urlCode = queryParameterNames.length ? | ||
`[API_URL, encodeQuery(body, ${JSON.stringify(queryParameterNames)})].filter(x=>x).join('?')` : | ||
'API_URL'; | ||
const fetchWrapper = `const ${operationName} = | ||
async renderAction(operation) { | ||
const { operationName, requestTypeName, responseTypeName } = this.names(operation); | ||
const fetchWrapper = withoutEmptyLines(`const ${operationName} = | ||
async (body:${requestTypeName}, options:any):Promise<${responseTypeName}> => { | ||
return fetch(${urlCode}, { | ||
return fetch(${this.urlSnippet(operation)}, { | ||
...options, | ||
method: ${JSON.stringify(this.operation.method)}, | ||
body: JSON.stringify(body) | ||
method: ${JSON.stringify(operation.method)}, | ||
${operation.route.requestBody ? 'body: JSON.stringify(body),' : ''} | ||
}).then((res:any) => res.json()) | ||
}`; | ||
return { | ||
[operationName]: fetchWrapper | ||
}; | ||
}`); | ||
return { [operationName]: fetchWrapper }; | ||
} | ||
names() { | ||
const operationName = this.operation.name; | ||
const operationTypeName = this.operation.name + 'OperationType'; | ||
urlSnippet(operation) { | ||
const queryParameterNames = operation.parameterNamesIn('query'); | ||
const endpointURL = operation.hasAnyParametersIn('path') ? | ||
`API_URL + substitutePath(${JSON.stringify(operation.pathName)}, body)` : | ||
`API_URL`; | ||
return queryParameterNames.length ? | ||
`[${endpointURL}, encodeQuery(body, ${JSON.stringify(queryParameterNames)})].filter(x=>x).join('?')` : | ||
endpointURL; | ||
} | ||
names(operation) { | ||
const operationName = operation.name; | ||
const operationTypeName = operation.name + 'OperationType'; | ||
const requestTypeName = operationName + 'Request'; | ||
@@ -49,0 +53,0 @@ const responseTypeName = operationName + 'Result'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const request_1 = require("./request"); | ||
const response_1 = require("./response"); | ||
var request_1 = require("./request"); | ||
exports.RequestTypeFormatter = request_1.RequestTypeFormatter; | ||
var response_1 = require("./response"); | ||
exports.ResultTypeFormatter = response_1.ResultTypeFormatter; | ||
var schema_1 = require("./schema"); | ||
exports.SchemaFormatter = schema_1.SchemaFormatter; | ||
exports.defaultOperationFormatters = [ | ||
request_1.RequestTypeFormatter, | ||
response_1.ResultTypeFormatter | ||
]; | ||
var fetch_1 = require("./fetch"); | ||
exports.FetchClientFormatter = fetch_1.FetchClientFormatter; | ||
//# sourceMappingURL=index.js.map |
@@ -6,12 +6,12 @@ "use strict"; | ||
const compile_1 = require("../compile"); | ||
class RequestTypeFormatter extends formatter_1.OperationFormatter { | ||
async render() { | ||
const typeName = `${this.operation.name}Request`; | ||
const parameters = this.operation.route.parameters || []; | ||
class RequestTypeFormatter extends formatter_1.Formatter { | ||
async render(operation) { | ||
const typeName = `${operation.name}Request`; | ||
const parameters = operation.route.parameters || []; | ||
return { | ||
[typeName]: await this.toTypescriptInterface(typeName, parameters) | ||
[typeName]: await this.toTypescriptInterface(operation, typeName, parameters) | ||
}; | ||
} | ||
async toTypescriptInterface(typeName, parameters) { | ||
const requestSchema = lodash_1.get(this.operation.route, 'requestBody.content["application/json"].schema'); | ||
async toTypescriptInterface(operation, typeName, parameters) { | ||
const requestSchema = lodash_1.get(operation.route, 'requestBody.content["application/json"].schema'); | ||
const aliasedType = requestSchema && requestSchema.$ref && compile_1.getSchemaNameByRef(requestSchema.$ref); | ||
@@ -18,0 +18,0 @@ if (aliasedType) |
@@ -6,3 +6,3 @@ "use strict"; | ||
const compile_1 = require("../compile"); | ||
class ResultTypeFormatter extends formatter_1.OperationFormatter { | ||
class ResultTypeFormatter extends formatter_1.Formatter { | ||
constructor() { | ||
@@ -12,4 +12,4 @@ super(...arguments); | ||
} | ||
async render() { | ||
const definitions = this.getResponseSchemaDefinitions(); | ||
async render(operation) { | ||
const definitions = this.getResponseSchemaDefinitions(operation); | ||
const compilations = await Promise.all(definitions.map(({ schema, typeName }) => this.compileDefinition(schema, typeName))); | ||
@@ -25,9 +25,9 @@ return definitions.reduce((typeDefs, definition, index) => Object.assign(typeDefs, { | ||
} | ||
getResponseSchemaDefinitions() { | ||
const responsesByStatusCode = lodash_1.get(this.operation.route, 'responses', {}); | ||
getResponseSchemaDefinitions(operation) { | ||
const responsesByStatusCode = lodash_1.get(operation.route, 'responses', {}); | ||
const statusCodes = this.getStatusCodes(responsesByStatusCode); | ||
return statusCodes.map(((statusCode, index) => { | ||
const key = `responses['${statusCode}'].content[${this.contentType}].schema`; | ||
const typeName = this.typeNameFor(index === 0 ? 'Result' : statusCode); | ||
const schema = lodash_1.get(this.operation.route, key); | ||
const typeName = this.typeNameFor(operation, index === 0 ? 'Result' : statusCode); | ||
const schema = lodash_1.get(operation.route, key); | ||
return { schema, statusCode, typeName }; | ||
@@ -43,5 +43,5 @@ })); | ||
} | ||
typeNameFor(suffix) { | ||
typeNameFor(operation, suffix) { | ||
return lodash_1.upperFirst(lodash_1.camelCase(` | ||
${this.operation.name} | ||
${operation.name} | ||
${suffix === 'default' ? 'fallback' : suffix.toString()} | ||
@@ -48,0 +48,0 @@ `)); |
@@ -6,10 +6,9 @@ "use strict"; | ||
class SchemaFormatter extends formatter_1.Formatter { | ||
constructor(schema, name) { | ||
constructor(name) { | ||
super(); | ||
this.schema = schema; | ||
this.name = name; | ||
} | ||
async render() { | ||
async render(schema) { | ||
return { | ||
[this.name]: await compile_1.compileSchema(this.schema, this.name) | ||
[this.name]: await compile_1.compileSchema(schema, this.name) | ||
}; | ||
@@ -16,0 +15,0 @@ } |
@@ -14,31 +14,30 @@ "use strict"; | ||
const clientStore = new store_1.Store(); | ||
const stores = { typeStore, clientStore }; | ||
new refs_1.InternalRefRewriter().rewrite(schemas); | ||
new refs_1.InternalRefRewriter().rewrite(paths); | ||
for (const schemaName of Object.keys(schemas)) { | ||
typeStore.assign(await new formatters_1.SchemaFormatter(schemas[schemaName], schemaName).render()); | ||
typeStore.assign(await new formatters_1.SchemaFormatter(schemaName).render(schemas[schemaName])); | ||
} | ||
const formatters = [...formatters_1.defaultOperationFormatters, ...operationFormatters]; | ||
for (let formatter of formatters) { | ||
if (typeof formatter.renderBoilerplate === 'function') { | ||
clientStore.assign({ | ||
[formatters.indexOf(formatter)]: await formatter.renderBoilerplate(apiSchema) | ||
}); | ||
} | ||
const formatters = [ | ||
new formatters_1.RequestTypeFormatter, | ||
new formatters_1.ResultTypeFormatter, | ||
...operationFormatters | ||
]; | ||
const hasBoilerplate = (formatter) => typeof formatter.renderBoilerplate === 'function'; | ||
for (let formatter of formatters.filter(hasBoilerplate)) | ||
clientStore.assign({ | ||
[formatters.indexOf(formatter)]: await formatter.renderBoilerplate(apiSchema) | ||
}); | ||
for (const operation of operation_1.eachOperation(paths)) { | ||
await applyFormatters(operation, formatters, stores); | ||
} | ||
for (const pathName of Object.keys(paths)) { | ||
for (const method of Object.keys(paths[pathName])) { | ||
const operation = new operation_1.Operation(paths[pathName][method], { pathName, method }); | ||
for (const OperationFormatter of formatters) { | ||
const formatter = new OperationFormatter(operation); | ||
typeStore.assign(await formatter.render()); | ||
if (typeof formatter.renderAction === 'function') | ||
clientStore.assign(await formatter.renderAction()); | ||
} | ||
} | ||
return stores; | ||
}; | ||
async function applyFormatters(operation, formatters, { typeStore, clientStore }) { | ||
for (const formatter of formatters) { | ||
typeStore.assign(await formatter.render(operation)); | ||
if (typeof formatter.renderAction === 'function') | ||
clientStore.assign(await formatter.renderAction(operation)); | ||
} | ||
return { | ||
typeStore, | ||
clientStore, | ||
}; | ||
}; | ||
} | ||
//# sourceMappingURL=index.js.map |
@@ -10,5 +10,27 @@ "use strict"; | ||
this.method = method; | ||
this.pathName = pathName; | ||
} | ||
hasAnyParametersIn(parameterLocation) { | ||
return !!(this.route.parameters || []).find((param) => param.in === parameterLocation); | ||
} | ||
parametersIn(parameterLocation) { | ||
return (this.route.parameters || []).filter(param => param.in === parameterLocation); | ||
} | ||
parameterNamesIn(parameterLocation) { | ||
return this.parametersIn(parameterLocation).map(param => param.name); | ||
} | ||
} | ||
exports.Operation = Operation; | ||
function eachOperation(paths) { | ||
return { | ||
*[Symbol.iterator]() { | ||
for (const pathName of Object.keys(paths)) { | ||
for (const method of Object.keys(paths[pathName])) { | ||
yield new Operation(paths[pathName][method], { pathName, method }); | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
exports.eachOperation = eachOperation; | ||
//# sourceMappingURL=operation.js.map |
{ | ||
"name": "openapi-to-typescript", | ||
"version": "3.1.0", | ||
"version": "3.2.0", | ||
"description": "Generate TypeScript typings based on an OpenAPI schema object.", | ||
@@ -16,3 +16,5 @@ "main": "dist/index.js", | ||
"build": "rimraf dist && tsc", | ||
"cli": "ts-node ./lib/cli", | ||
"commit": "git-cz", | ||
"cz": "git-cz", | ||
"test": "jest" | ||
@@ -19,0 +21,0 @@ }, |
21932
468