From 597825d58238aded0c5511755faca23091614e60 Mon Sep 17 00:00:00 2001 From: ehmicky Date: Wed, 11 Sep 2019 13:49:33 +0200 Subject: [PATCH] Improve OpenAPI logic --- README.md | 10 +++++----- src/index.js | 10 ++++++---- src/open-api/index.js | 16 +++++++--------- src/open-api/shape-swagger.js | 29 ----------------------------- src/open-api/util.js | 32 -------------------------------- src/operations.js | 28 ++++++++++++++++++++++++++++ 6 files changed, 46 insertions(+), 79 deletions(-) delete mode 100644 src/open-api/shape-swagger.js create mode 100644 src/operations.js diff --git a/README.md b/README.md index be72248..c805abe 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ const client = new NetlifyAPI('1234myAccessToken') const sites = await client.listSites() ``` -## Using Open API methods +## Using Open API operations ```js const NetlifyAPI = require('netlify') @@ -67,9 +67,9 @@ A getter that returns the formatted base URL of the endpoint the client is confi ### Open API Client methods -The client is dynamically generated from the [open-api](https://github.com/netlify/open-api) definition file. Each method is is named after the `operationId` name of each endpoint action. **To see list of available operations see the [open-api website](https://open-api.netlify.com/)**. +The client is dynamically generated from the [open-api](https://github.com/netlify/open-api) definition file. Each method is is named after the `operationId` name of each operation. **To see list of available operations see the [open-api website](https://open-api.netlify.com/)**. -Every open-api method has the following signature: +Every open-api operation has the following signature: #### `promise(response) = client.operationId([params], [opts])` @@ -103,7 +103,7 @@ Optional `opts` can include any property you want passed to `node-fetch`. The `h } ``` -All methods are conveniently consumed with async/await: +All operations are conveniently consumed with async/await: ```js async function getSomeData() { @@ -123,7 +123,7 @@ If the request response includes `json` in the `contentType` header, fetch will ### API Flow Methods -Some methods have been added in addition to the open API methods that make certain actions simpler to perform. +Some methods have been added in addition to the open API operations that make certain actions simpler to perform. #### `promise(accessToken) = client.getAccessToken(ticket, [opts])` diff --git a/src/index.js b/src/index.js index 1ecc41c..dd7d6f6 100644 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,8 @@ const dfn = require('@netlify/open-api') const pWaitFor = require('p-wait-for') const debug = require('debug')('netlify') -const { methods, generateMethod } = require('./open-api') +const { generateOperation } = require('./open-api') +const { getOperations } = require('./operations') const deploy = require('./deploy') class NetlifyAPI { @@ -107,12 +108,13 @@ class NetlifyAPI { } } -methods.forEach(method => { +const operations = getOperations() +operations.forEach(operation => { // Generate open-api methods /* {param1, param2, body, ... }, [opts] */ - NetlifyAPI.prototype[method.operationId] = generateMethod(method) + NetlifyAPI.prototype[operation.operationId] = generateOperation(operation) }) module.exports = NetlifyAPI -module.exports.methods = methods +module.exports.methods = operations diff --git a/src/open-api/index.js b/src/open-api/index.js index 56ffc75..6a05868 100644 --- a/src/open-api/index.js +++ b/src/open-api/index.js @@ -12,10 +12,8 @@ const isStream = require('is-stream') const { sleep, unixNow } = require('./util') -exports.methods = require('./shape-swagger') - // open-api 2.0 -exports.generateMethod = method => { +exports.generateOperation = operation => { // // Warning: Expects `this`. These methods expect to live on the client prototype // @@ -23,11 +21,11 @@ exports.generateMethod = method => { opts = Object.assign({}, opts) params = Object.assign({}, this.globalParams, params) - let path = this.basePath + method.path + let path = this.basePath + operation.path debug(`path template: ${path}`) // Path parameters - Object.values(method.parameters.path).forEach(param => { + Object.values(operation.parameters.path).forEach(param => { const val = params[param.name] || params[camelCase(param.name)] if (val != null) { path = path.replace(`{${param.name}}`, val) @@ -39,7 +37,7 @@ exports.generateMethod = method => { // qs parameters let qs - Object.values(method.parameters.query).forEach(param => { + Object.values(operation.parameters.query).forEach(param => { const val = params[param.name] || params[camelCase(param.name)] if (val != null) { if (!qs) qs = {} @@ -58,7 +56,7 @@ exports.generateMethod = method => { let bodyType = 'json' if (params.body) { body = params.body - Object.values(method.parameters.body).forEach(param => { + Object.values(operation.parameters.body).forEach(param => { const type = get(param, 'schema.format') if (type === 'binary') { bodyType = 'binary' @@ -86,8 +84,8 @@ exports.generateMethod = method => { debug('specialHeaders: %O', specialHeaders) opts.headers = new Headers(Object.assign({}, this.defaultHeaders, specialHeaders, opts.headers)) - opts.method = method.verb.toUpperCase() - debug(`method: ${opts.method}`) + opts.method = operation.verb.toUpperCase() + debug(`HTTP method: ${opts.method}`) // TODO: Consider using micro-api-client when it supports node-fetch diff --git a/src/open-api/shape-swagger.js b/src/open-api/shape-swagger.js deleted file mode 100644 index 5548687..0000000 --- a/src/open-api/shape-swagger.js +++ /dev/null @@ -1,29 +0,0 @@ -const dfn = require('@netlify/open-api') - -const { sortParams, mergeParams } = require('./util') -const methods = [] - -Object.entries(dfn.paths).forEach(([apiPath, verbs]) => { - const topParams = sortParams(verbs.parameters) - delete verbs.parameters - - Object.entries(verbs).forEach(([verb, props]) => { - const verbParams = sortParams(props.parameters) - delete props.parameters - - const opSpec = Object.assign( - {}, - props, - { - verb, - path: apiPath - }, - { - parameters: mergeParams(topParams, verbParams) - } - ) - methods.push(opSpec) - }) -}) - -module.exports = methods diff --git a/src/open-api/util.js b/src/open-api/util.js index 24eadc7..bcda4a0 100644 --- a/src/open-api/util.js +++ b/src/open-api/util.js @@ -1,35 +1,3 @@ -const set = require('lodash.set') - -exports.sortParams = (parameters = []) => { - const paramSet = { - // minimum param set - path: {}, - query: {}, - body: {} - } - - parameters.forEach(param => { - set(paramSet, `${param.in}.${param.name}`, param) - }) - - return paramSet -} - -exports.mergeParams = (...params) => { - const merged = {} - - params.forEach(paramSet => { - Object.entries(paramSet).forEach(([type, params]) => { - if (!merged[type]) merged[type] = {} // preserve empty objects - Object.values(params).forEach((param, index) => { - set(merged, `${param.in}.${param.name}`, Object.assign(param, { index })) - }) - }) - }) - - return merged -} - // Async sleep. A whole new WORLD! exports.sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) diff --git a/src/operations.js b/src/operations.js new file mode 100644 index 0000000..bd26977 --- /dev/null +++ b/src/operations.js @@ -0,0 +1,28 @@ +const { paths } = require('@netlify/open-api') + +// Retrieve all OpenAPI operations +const getOperations = function() { + return [].concat( + ...Object.entries(paths).map(([path, pathItem]) => { + const operations = Object.assign({}, pathItem) + delete operations.parameters + return Object.entries(operations).map(([method, operation]) => { + const parameters = getParameters(pathItem.parameters, operation.parameters) + return Object.assign({}, operation, { verb: method, path, parameters }) + }) + }) + ) +} + +const getParameters = function(pathParameters = [], operationParameters = []) { + const parameters = [...pathParameters, ...operationParameters] + return parameters.reduce(addParameter, { path: {}, query: {}, body: {} }) +} + +const addParameter = function(parameters, param) { + return Object.assign({}, parameters, { + [param.in]: Object.assign({}, parameters[param.in], { [param.name]: param }) + }) +} + +module.exports = { getOperations }