From 6b6ae634f17cf1a089faec302b3d354633faaa66 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 | 28 ---------------------------- src/open-api/util.js | 32 -------------------------------- src/operations.js | 28 ++++++++++++++++++++++++++++ 6 files changed, 46 insertions(+), 78 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 a7a59f0..a0248b8 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 6a18c6e..7377e98 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,8 @@ const set = require('lodash.set') const get = require('lodash.get') const dfn = require('@netlify/open-api') -const { methods, generateMethod } = require('./open-api') +const { generateOperation } = require('./open-api') +const { getOperations } = require('./operations') const pWaitFor = require('p-wait-for') const deploy = require('./deploy') const debug = require('debug')('netlify') @@ -109,12 +110,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 859a5e2..fff2bf9 100644 --- a/src/open-api/index.js +++ b/src/open-api/index.js @@ -10,10 +10,8 @@ const debug = require('debug')('netlify:open-api') const { existy, sleep, unixNow } = require('./util') const isStream = require('is-stream') -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 // @@ -21,11 +19,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 (existy(val)) { path = path.replace(`{${param.name}}`, val) @@ -37,7 +35,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 (existy(val)) { if (!qs) qs = {} @@ -56,7 +54,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' @@ -84,8 +82,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 4628087..0000000 --- a/src/open-api/shape-swagger.js +++ /dev/null @@ -1,28 +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 c2a037f..b05cda8 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 -} - exports.existy = val => val != null // Async sleep. A whole new WORLD! diff --git a/src/operations.js b/src/operations.js new file mode 100644 index 0000000..7d8f937 --- /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 = [], propsParameters = []) { + const parameters = [...pathParameters, ...propsParameters] + 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 }