diff --git a/README.md b/README.md index 3743650..423f7f4 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ Connect [`Express`](http://www.expressjs.com) route controllers to restful paths ## Prerequisites -This library assumes: +This library assumes you are using: -1. You are using [`expressjs`](http://www.expressjs.com) -2. You are using [`swagger`](http://swagger.io) _version 2_ or [`OpenAPI`](https://www.openapis.org) _version 3_ +1. [NodeJS](https://nodejs.org) _version 6.4.0_ or better, +2. [`expressjs`](http://www.expressjs.com) _any version_, and +3. [`swagger`](http://swagger.io) _version 2_, or [`OpenAPI`](https://www.openapis.org) _version 3_. ## Install @@ -49,9 +50,9 @@ const ping = (req, res) => { module.exports = { ping, versions } ``` -### Swagger Version 2 Example +### Swagger Version 2 example -Given a Swagger (v2) YAML file `my-api.yml` along the lines of: +Given a Swagger (v2) YAML file `api.yml` along the lines of: ```yml swagger: '2.0' @@ -185,9 +186,9 @@ components: $ref: '#/components/schemas/APIVersion' ``` -## Your Express Server +## Connecting your Express server -You could set up your server as follows: +You can `connect` your `Express` app or router as follows: ```js const express = require('express') @@ -196,15 +197,16 @@ const { connector } = require('swagger-routes-express') const api = require('./api') const makeApp = () => { - const apiDefinition = YAML.load('api.yml') - const connect = connector(api, apiDefinition) + const apiDefinition = YAML.load('api.yml') // load the api as json + const connect = connector(api, apiDefinition) // make the connector + const app = express() // make the app - const app = express() // do any other app stuff, such as wire in passport, use cors etc - // then attach the routes - connect(app) + + connect(app) // attach the routes // add any error handlers last + return app } ``` @@ -213,11 +215,11 @@ With the result that requests to `GET /` will invoke the `versions` controller a ## Adding security middleware handlers -You can pass in a range of options, so if your swagger document defines security scopes you can pass in via a `security` option: +If your swagger document defines security, you can map this to your own Auth Middleware by passing in a `security` option to the `connector`. -### With scopes +### Security with scopes -For example if your path has a `security` block like +For example if your path defines oAuth style `security` like: ```yml paths: @@ -238,15 +240,15 @@ Supply a `security` option as follows ```js const options = { security: { - 'read-write': readWriteAuthMiddlewareFunction, + 'read,write': readWriteAuthMiddlewareFunction, admin: adminAuthMiddlewareFunction } } ``` -### Without scopes +### Security without scopes -If your paths supply a `security` block but its `scopes` array is empty, you can just use its name instead in the `security` option. +If your path defines `security`, and its `scopes` array is empty, you use its name in the `security` option. Given: @@ -269,10 +271,31 @@ const options = { } ``` +### Global security definition + +Both Swagger V2 and OpenAPI V3 allow you to define global `security`. The global `security` definition will be applied if there is no path-specific one defined. + +### Exempting a path from global security + +If you've defined global `security` but wish to exempt a specific path, then you can configure the path like: + +```yml +paths: + /my-route + get: + summary: some route that is exempt from the default security + security: [] +``` + +### Further reading on Swagger and security + +- [Swagger V2 Authentication](https://swagger.io/docs/specification/2-0/authentication/), and +- [Open API V3 Authentication](https://swagger.io/docs/specification/authentication/) docs. + ### Notes -- The scopes, if supplied, are sorted alphabetically. -- Only the **first** security option is used, the others are ignored. +- Only the **first** security option is used, the others are ignored. Your Auth Middleware function must handle any alternative authentication schemes. +- Scopes, if supplied, are sorted alphabetically. ### What's an Auth Middleware function? @@ -295,13 +318,9 @@ async function correspondingMiddlewareFunction(req, res, next) { - [More information…](https://duckduckgo.com/?q=express+auth+middleware) (via DuckDuckGo) -### OpenAPI V3 Global Security Blocks - -OpenAPI V3 allows you to define a global `security` definition as well as path specific ones. The global `security` block will be applied if there is no path specific one defined. - ### Adding other path-level middleware -You can add your own path specific middleware by passing in a `middleware` option +You can add your own path specific middleware by passing in a `middleware` option: ```js { @@ -311,7 +330,7 @@ You can add your own path specific middleware by passing in a `middleware` optio } ``` -and then in the path specification adding an `x-middleware` option +With either Swagger v2 or OpenAPI v3, add an `x-middleware` option in the path specification: ```yml paths: @@ -322,9 +341,7 @@ paths: - myMiddleware ``` -The `someMiddlewareFunction` will be inserted **after** any auth middleware. - -This works for both Swagger v2 and OpenAPI v3 documents. +The `someMiddlewareFunction` will be inserted **after** any Auth Middleware. ## Adding hooks @@ -339,7 +356,7 @@ const onCreateRoute = (method, descriptor) => { The method will be one of 'get', 'post', 'patch', 'put', or 'delete'. -The descriptor is an array of +The `descriptor` is an array of: ```js ;[ @@ -364,7 +381,7 @@ You can supply your own `apiSeparator` option in place of `_` to map from `/`. ## Missing Route Controllers -If a route controller is defined as an `operationId` in swagger but there is no corresponding controller, a default `notImplemented` controller will be inserted that simply responds with a `501` error. You can also specify your own `notImplemented` controller in `options`. +If a route controller is defined as an `operationId` in Swagger but there is no corresponding controller, a default `notImplemented` controller will be inserted that simply responds with a `501` error. You can also specify your own `notImplemented` controller in `options`. If no `operationId` is supplied for a path then a default `notFound` controller that responds with a `404` status will be inserted. You can also specify your own `notFound` controller in `options`. @@ -372,15 +389,15 @@ If no `operationId` is supplied for a path then a default `notFound` controller ### Swagger Version 2 -For the root path `/` we check the route's `tags`. If the first tag defined for a path is `'root'` we don't inject the api basePath, otherwise we do. You can define your own `rootTag` option to override this. +For the root path `/` we check the route's `tags`. If the first `tag` defined for a path is `'root'` we don't inject the api `basePath`, otherwise we do. You can define your own `rootTag` option to override this behaviour. ### OpenAPI Version 3 -The OpenAPI format allows you to define both a default `servers` array, and `path` specific `servers` arrays. The `url` fields in those arrays are parsed, ignoring any absolute URLS (as they are deemed to refer to controllers external to this API Server). +The OpenAPI V3 format allows you to define both a default `servers` array, and `path` specific `servers` arrays. The `url` fields in those arrays are parsed, ignoring any absolute URLS (as they are deemed to refer to controllers external to this API Server). The spec allows you to include template variables in the `servers`' `url` field. To accomodate this you can supply a `variables` option in `options`. Any variables you specify will be substituted. -## Default Options +## Default options If you don't pass in any options the defaults are: @@ -398,7 +415,7 @@ If you don't pass in any options the defaults are: } ``` -## Generating API Summary information +## Generating API summary information You can generate a summary of your Swagger v2 or OpenAPI v3 API specification in the form: @@ -419,7 +436,7 @@ const apiDefinition = YAML.load('api.yml') const apiSummary = summarise(apiDefinition) ``` -## Upgrading from Swagger Routes Express V2 to V3. +## Upgrading from Swagger Routes Express V2 to V3 These docs refer to Version 3 of Swagger Routes Express which changed the way you invoke the `connector`. @@ -449,7 +466,7 @@ const { connector } = require('swagger-routes-express') ### Prerequisites -- [NodeJS](https://nodejs.org) — Ideally version `10.16.3 (LTS)` or better. +- [NodeJS](https://nodejs.org) — Ideally you will develop with version `12.13.0 (LTS)` or better, but it will work with node versions going back to version `6.4.0`. ### Test it diff --git a/package.json b/package.json index 3037dfa..b14b85c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "swagger-routes-express", - "version": "3.1.0", + "version": "3.1.1", "description": "Connect Express route controllers to restful paths using a Swagger 2 or OpenAPI 3 definition file", "main": "src/index.js", "engines": { diff --git a/src/extract/extractVersion.js b/src/extract/extractVersion.js index 07ef3be..d52b2db 100644 --- a/src/extract/extractVersion.js +++ b/src/extract/extractVersion.js @@ -1,5 +1,11 @@ const { major, coerce } = require('semver') +/** + * Determins whether to use version 2 or 3 given the version defined in the supplied swagger document. + * @param an object with keys `swagger` or `openapi` + * @returns 2 if the document is a swagger version 2 document, + * 3 if it's an openapi version 3 document, or undefined otherwise. + */ const extractVersion = ({ swagger, openapi }) => swagger && parseInt(major(coerce(swagger))) === 2 ? 2 diff --git a/src/extract/v2/extractPaths.js b/src/extract/v2/extractPaths.js index ac7e1a6..2507999 100644 --- a/src/extract/v2/extractPaths.js +++ b/src/extract/v2/extractPaths.js @@ -1,5 +1,5 @@ const { METHODS } = require('../../constants') -const normaliseSecurity = require('../../normalise/v2/normaliseSecurity') +const normaliseSecurity = require('../../normalise/normaliseSecurity') const normaliseOperationId = require('../../normalise/normaliseOperationId') const normaliseMiddleware = require('../../normalise/normaliseMiddleware') const normaliseRoute = require('../../normalise/normaliseRoute') @@ -17,13 +17,15 @@ const normaliseRoute = require('../../normalise/normaliseRoute') * ] * */ -const extractPaths = ({ basePath, paths }, options = {}) => { +const extractPaths = ({ security, basePath, paths }, options = {}) => { const { apiSeparator, // What to swap for `/` in the swagger doc rootTag = 'root', // The tag that tells us not to prepend the basePath middleware = {} } = options + const defaultSecurity = normaliseSecurity(security) + const reduceRoutes = (acc, elem) => { METHODS.forEach(method => { const op = paths[elem][method] @@ -33,7 +35,7 @@ const extractPaths = ({ basePath, paths }, options = {}) => { method, route: normaliseRoute(`${isRoot ? '' : basePath}${elem}`), operationId: normaliseOperationId(op.operationId, apiSeparator), - security: normaliseSecurity(op.security), + security: normaliseSecurity(op.security, defaultSecurity), middleware: normaliseMiddleware(middleware, op['x-middleware']) }) } diff --git a/src/extract/v3/extractPaths.js b/src/extract/v3/extractPaths.js index 1bff6a8..bb548b3 100644 --- a/src/extract/v3/extractPaths.js +++ b/src/extract/v3/extractPaths.js @@ -1,5 +1,5 @@ const { METHODS } = require('../../constants') -const normaliseSecurity = require('../../normalise/v3/normaliseSecurity') +const normaliseSecurity = require('../../normalise/normaliseSecurity') const normaliseOperationId = require('../../normalise/normaliseOperationId') const normaliseMiddleware = require('../../normalise/normaliseMiddleware') const normaliseRoute = require('../../normalise/normaliseRoute') @@ -30,11 +30,11 @@ const extractPaths = ({ security, servers, paths }, options = {}) => { const defaultBasePath = basePath(servers, variables) const defaultSecurity = normaliseSecurity(security) - const pathSecurity = (opSecurity, defaultSecurity) => { - const pathSecurity = normaliseSecurity(opSecurity) - if (pathSecurity === null) return // the security was an empty array. - return pathSecurity || defaultSecurity - } + // const pathSecurity = (opSecurity, defaultSecurity) => { + // const pathSecurity = normaliseSecurity(opSecurity) + // if (pathSecurity === null) return // the security was an empty array. + // return pathSecurity || defaultSecurity + // } const reduceRoutes = (acc, elem) => { METHODS.forEach(method => { @@ -45,7 +45,7 @@ const extractPaths = ({ security, servers, paths }, options = {}) => { method, route: normaliseRoute(`${trimBase(base)}${elem}`), operationId: normaliseOperationId(op.operationId, apiSeparator), - security: pathSecurity(op.security, defaultSecurity), + security: normaliseSecurity(op.security, defaultSecurity), middleware: normaliseMiddleware(middleware, op['x-middleware']) }) } diff --git a/src/normalise/keyOrScopes.js b/src/normalise/keyOrScopes.js new file mode 100644 index 0000000..37c5c1a --- /dev/null +++ b/src/normalise/keyOrScopes.js @@ -0,0 +1,40 @@ +/** + * Given a block of security data, either return the key, if there are no scopes, + * or return the sorted, joined up scopes. + * + * From the official docs + * > `security` is an array of hashmaps, where each hashmap contains + * > one or more named security schemes. + * + * @param security — The array of security blocks + * @return the key if there are no scopes, or + * the joined up sorted scopes if there are scopes, or + * undefined if the array of blocks is empty + */ +const keyOrScopes = ([data]) => { + if (!data) return // there is no security + const [key] = Object.keys(data) // we only care about the first key + const scopes = data[key] // there must be at least one key + if (!scopes.length) return key // if there are no scopes use the key instead + return scopes.sort().join(',') +} + +module.exports = keyOrScopes + +/* +security: [] + +=> undefined + +security: + - someKey: [] + +=> someKey + +security: + - someKey: + - scope1 + - scope2 + +=> scope1,scope2 +*/ diff --git a/src/normalise/normaliseSecurity.js b/src/normalise/normaliseSecurity.js new file mode 100644 index 0000000..e847ad2 --- /dev/null +++ b/src/normalise/normaliseSecurity.js @@ -0,0 +1,18 @@ +const keyOrScopes = require('./keyOrScopes') + +/** + * Maps the supplied security block (if any) to a simple string representation + * that can in turn be used as a key for the appropriate security middleware. + * + * Refs: + * - V2 https://swagger.io/docs/specification/2-0/authentication + * - V3 https://swagger.io/docs/specification/authentication + * + * @param security — A swagger security block + * @param globalSecurity — A previously computed global security key. + * @return a string representation used as a key for the appropriate security middleware. + */ +const normaliseSecurity = (security, globalSecurity) => + security ? keyOrScopes(security) : globalSecurity + +module.exports = normaliseSecurity diff --git a/src/normalise/v2/keyOrScopes.js b/src/normalise/v2/keyOrScopes.js deleted file mode 100644 index 68cc391..0000000 --- a/src/normalise/v2/keyOrScopes.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Given a block of security data, either return the key, if there are no scopes, - * or return the sorted, joined up scopes. - * @param data — The block of security data - * @return the key, if there are no scopes, or the joined up sorted scopes. - */ -const keyOrScopes = data => { - const keys = Object.keys(data) - if (!keys.length) return // security block has no keys - const [key] = keys // we only care about the first key - const scopes = data[key] - if (!scopes.length) return key // if there are no scopes use the key instead - return scopes.sort().join(',') -} - -module.exports = keyOrScopes diff --git a/src/normalise/v2/normaliseSecurity.js b/src/normalise/v2/normaliseSecurity.js deleted file mode 100644 index 1b7e41e..0000000 --- a/src/normalise/v2/normaliseSecurity.js +++ /dev/null @@ -1,14 +0,0 @@ -const keyOrScopes = require('./keyOrScopes') - -/** - * Normalises the supplied security object into a string. - * ref: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#securityRequirementObject - * - * @param security — The security object, or undefined - * @returns a string representation of the security, or undefined if the - * security was undefined - */ -const normaliseSecurity = security => - security ? keyOrScopes(security[0]) : undefined - -module.exports = normaliseSecurity diff --git a/src/normalise/v3/normaliseSecurity.js b/src/normalise/v3/normaliseSecurity.js deleted file mode 100644 index 1eaecf7..0000000 --- a/src/normalise/v3/normaliseSecurity.js +++ /dev/null @@ -1,22 +0,0 @@ -// ref https://swagger.io/docs/specification/authentication/ -const normaliseV2Security = require('../v2/normaliseSecurity') - -/** - * Normalises the supplied security object into a string. - * - * @param security — The security object, or undefined - * @returns a string representation of the security, or undefined if the - * security was undefined, or null if the security was defined - * but was an empty array. - */ -const normaliseSecurity = security => { - if (!security) return - const [first] = security - if (!first) return null - const [key] = Object.keys(first) - const value = first[key] - if (!value.length) return key - return normaliseV2Security(security) -} - -module.exports = normaliseSecurity diff --git a/test/unit/extract/v2/extractPaths.test.js b/test/unit/extract/v2/extractPaths.test.js index 58ba83d..e7ba921 100644 --- a/test/unit/extract/v2/extractPaths.test.js +++ b/test/unit/extract/v2/extractPaths.test.js @@ -3,65 +3,145 @@ const { expect } = require('chai') const extractPaths = require('src/extract/v2/extractPaths') describe('src/extract/v2/extractPaths', () => { - const api = { - basePath: '/api/v1', - paths: { - '/': { - get: { - tags: ['root'], - operationId: 'versions' + context('without root level security', () => { + const api = { + basePath: '/api/v1', + paths: { + '/': { + get: { + tags: ['root'], + operationId: 'versions' + } + }, + '/ping': { + get: { + tags: ['root'], + operationId: 'ping' + } + }, + '/test': { + get: { + operationId: 'v1/test', + security: [ + { + example: ['identity.basic', 'identity.email', 'admin'] + } + ] + } } + } + } + + const expected = [ + { + method: 'get', + route: '/', + operationId: 'versions', + security: undefined, + middleware: [] }, - '/ping': { - get: { - tags: ['root'], - operationId: 'ping' - } + { + method: 'get', + route: '/ping', + operationId: 'ping', + security: undefined, + middleware: [] }, - '/test': { - get: { - operationId: 'v1/test', - security: [ - { - example: ['identity.basic', 'identity.email', 'admin'] + { + method: 'get', + route: '/api/v1/test', + operationId: 'v1_test', + security: 'admin,identity.basic,identity.email', + middleware: [] + } + ] + + let paths + + before(() => { + paths = extractPaths(api) + }) + + it('returns the expected paths', () => { + expect(paths).to.deep.equal(expected) + }) + }) + + context('with root level security', () => { + const baseApi = { + basePath: '/api/v1', + security: [ + { + bearerAuth: [] + } + ] + } + + context('path level default', () => { + const api = { + ...baseApi, + paths: { + '/': { + get: { + tags: ['root'], + operationId: 'versions' } - ] + } } } - } - } + const expected = [ + { + method: 'get', + route: '/', + operationId: 'versions', + security: 'bearerAuth', + middleware: [] + } + ] - const expected = [ - { - method: 'get', - route: '/', - operationId: 'versions', - security: undefined, - middleware: [] - }, - { - method: 'get', - route: '/ping', - operationId: 'ping', - security: undefined, - middleware: [] - }, - { - method: 'get', - route: '/api/v1/test', - operationId: 'v1_test', - security: 'admin,identity.basic,identity.email', - middleware: [] - } - ] + let paths - let paths + before(() => { + paths = extractPaths(api) + }) - before(() => { - paths = extractPaths(api) - }) + it('returns the expected paths', () => { + expect(paths).to.deep.equal(expected) + }) + }) + + context('path level security opt-out', () => { + const api = { + ...baseApi, + paths: { + '/': { + get: { + tags: ['root'], + operationId: 'versions', + security: [] + } + } + } + } + const expected = [ + { + method: 'get', + route: '/', + operationId: 'versions', + security: undefined, + middleware: [] + } + ] + + let paths + + before(() => { + paths = extractPaths(api) + }) - it('returns the expected paths', () => { - expect(paths).to.deep.equal(expected) + it('returns the expected paths', () => { + expect(paths).to.deep.equal(expected) + }) + }) }) }) diff --git a/test/unit/extract/v3/extractPaths.test.js b/test/unit/extract/v3/extractPaths.test.js index 49751e2..731970a 100644 --- a/test/unit/extract/v3/extractPaths.test.js +++ b/test/unit/extract/v3/extractPaths.test.js @@ -3,72 +3,7 @@ const { expect } = require('chai') const extractPaths = require('src/extract/v3/extractPaths') describe('src/extract/v3/extractPaths', () => { - const api = { - servers: [{ url: '/api/v1' }], - paths: { - '/': { - get: { - servers: [{ url: '/' }], - tags: ['root'], - operationId: 'versions' - } - }, - '/ping': { - get: { - servers: [ - { - url: '/' - } - ], - tags: ['root'], - operationId: 'ping' - } - }, - '/test': { - get: { - tags: ['test'], - operationId: 'v1/test', - security: [{ example: ['identity.basic', 'identity.email', 'admin'] }] - } - } - } - } - - const expected = [ - { - method: 'get', - route: '/', - operationId: 'versions', - security: undefined, - middleware: [] - }, - { - method: 'get', - route: '/ping', - operationId: 'ping', - security: undefined, - middleware: [] - }, - { - method: 'get', - route: '/api/v1/test', - operationId: 'v1_test', - security: 'admin,identity.basic,identity.email', - middleware: [] - } - ] - - let paths - - before(() => { - paths = extractPaths(api) - }) - - it('returns the expected paths', () => { - expect(paths).to.deep.equal(expected) - }) - - context('root level security opt-out', () => { + context('without root level security', () => { const api = { servers: [{ url: '/api/v1' }], paths: { @@ -76,27 +11,53 @@ describe('src/extract/v3/extractPaths', () => { get: { servers: [{ url: '/' }], tags: ['root'], - operationId: 'root', - security: [] + operationId: 'versions' + } + }, + '/ping': { + get: { + servers: [ + { + url: '/' + } + ], + tags: ['root'], + operationId: 'ping' + } + }, + '/test': { + get: { + tags: ['test'], + operationId: 'v1/test', + security: [ + { example: ['identity.basic', 'identity.email', 'admin'] } + ] } } - }, - security: [ - { - bearerAuth: [] - } - ] - // components: { - // securitySchemes - // } + } } + const expected = [ { method: 'get', route: '/', - operationId: 'root', + operationId: 'versions', security: undefined, middleware: [] + }, + { + method: 'get', + route: '/ping', + operationId: 'ping', + security: undefined, + middleware: [] + }, + { + method: 'get', + route: '/api/v1/test', + operationId: 'v1_test', + security: 'admin,identity.basic,identity.email', + middleware: [] } ] @@ -110,4 +71,84 @@ describe('src/extract/v3/extractPaths', () => { expect(paths).to.deep.equal(expected) }) }) + + context('with root level security', () => { + const baseApi = { + servers: [{ url: '/api/v1' }], + security: [ + { + bearerAuth: [] + } + ] + } + + context('path level default', () => { + const api = { + ...baseApi, + paths: { + '/': { + get: { + servers: [{ url: '/' }], + tags: ['root'], + operationId: 'root' + } + } + } + } + const expected = [ + { + method: 'get', + route: '/', + operationId: 'root', + security: 'bearerAuth', + middleware: [] + } + ] + + let paths + + before(() => { + paths = extractPaths(api) + }) + + it('returns the expected paths', () => { + expect(paths).to.deep.equal(expected) + }) + }) + + context('path level security opt-out', () => { + const api = { + ...baseApi, + paths: { + '/': { + get: { + servers: [{ url: '/' }], + tags: ['root'], + operationId: 'root', + security: [] + } + } + } + } + const expected = [ + { + method: 'get', + route: '/', + operationId: 'root', + security: undefined, + middleware: [] + } + ] + + let paths + + before(() => { + paths = extractPaths(api) + }) + + it('returns the expected paths', () => { + expect(paths).to.deep.equal(expected) + }) + }) + }) }) diff --git a/test/unit/normalise/keyOrScopes.test.js b/test/unit/normalise/keyOrScopes.test.js new file mode 100644 index 0000000..21630ea --- /dev/null +++ b/test/unit/normalise/keyOrScopes.test.js @@ -0,0 +1,35 @@ +const { expect } = require('chai') + +const keyOrScopes = require('src/normalise/keyOrScopes') + +describe('src/normalise/keyOrScopes', () => { + context('given a security array', () => { + context('with scopes', () => { + const security = [ + { + example: ['identity.basic', 'identity.email', 'admin'] + } + ] + const expected = 'admin,identity.basic,identity.email' + + it('normalises correctly', () => { + expect(keyOrScopes(security)).to.equal(expected) + }) + }) + + context('without scopes', () => { + const security = [{ example: [] }] + const expected = 'example' + + it('normalises the security array correctly', () => { + expect(keyOrScopes(security)).to.equal(expected) + }) + }) + }) + + context('given an empty array', () => { + it('returns undefined', () => { + expect(keyOrScopes([])).to.be.undefined + }) + }) +}) diff --git a/test/unit/normalise/normaliseSecurity.test.js b/test/unit/normalise/normaliseSecurity.test.js new file mode 100644 index 0000000..30f0fd9 --- /dev/null +++ b/test/unit/normalise/normaliseSecurity.test.js @@ -0,0 +1,25 @@ +const { expect } = require('chai') + +const normaliseSecurity = require('src/normalise/normaliseSecurity') + +describe('src/normalise/normaliseSecurity', () => { + const doTest = ([label, security, defaultSecurity, expected]) => { + context(`given ${label}`, () => { + it('normalises the security block correctly', () => { + expect(normaliseSecurity(security, defaultSecurity)).to.equal(expected) + }) + }) + } + + ;[ + ['there are no scopes', [{ example: [] }], undefined, 'example'], + [ + 'there are scopes', + [{ example: ['identity.basic', 'identity.email', 'admin'] }], + undefined, + 'admin,identity.basic,identity.email' + ], + ['there is no security but a default', undefined, 'default', 'default'], + ['there is empty security and a default', [], 'default', undefined] + ].forEach(doTest) +}) diff --git a/test/unit/normalise/v2/keyOrScopes.test.js b/test/unit/normalise/v2/keyOrScopes.test.js deleted file mode 100644 index d75128d..0000000 --- a/test/unit/normalise/v2/keyOrScopes.test.js +++ /dev/null @@ -1,33 +0,0 @@ -const { expect } = require('chai') - -const keyOrScopes = require('src/normalise/v2/keyOrScopes') - -describe('src/normalise/v2/keyOrScopes', () => { - context('given a security block', () => { - context('with scopes', () => { - const security = { - example: ['identity.basic', 'identity.email', 'admin'] - } - const expected = 'admin,identity.basic,identity.email' - - it('normalises correctly', () => { - expect(keyOrScopes(security)).to.equal(expected) - }) - }) - - context('without scopes', () => { - const security = { example: [] } - const expected = 'example' - - it('normalises the security block correctly', () => { - expect(keyOrScopes(security)).to.equal(expected) - }) - }) - }) - - context('given an empty block', () => { - it('returns undefined', () => { - expect(keyOrScopes({})).to.be.undefined - }) - }) -}) diff --git a/test/unit/normalise/v2/normaliseSecurity.test.js b/test/unit/normalise/v2/normaliseSecurity.test.js deleted file mode 100644 index ffd0780..0000000 --- a/test/unit/normalise/v2/normaliseSecurity.test.js +++ /dev/null @@ -1,33 +0,0 @@ -const { expect } = require('chai') - -const normaliseSecurity = require('src/normalise/v2/normaliseSecurity') - -describe('src/normalise/v2/normaliseSecurity', () => { - context('given a swagger security block', () => { - context('with scopes', () => { - const security = [ - { example: ['identity.basic', 'identity.email', 'admin'] } - ] - const expected = 'admin,identity.basic,identity.email' - - it('normalises the security block correctly', () => { - expect(normaliseSecurity(security)).to.equal(expected) - }) - }) - - context('without scopes', () => { - const security = [{ example: [] }] - const expected = 'example' - - it('normalises the security block correctly', () => { - expect(normaliseSecurity(security)).to.equal(expected) - }) - }) - }) - - context('given nothing', () => { - it('returns undefined', () => { - expect(normaliseSecurity()).to.be.undefined - }) - }) -}) diff --git a/test/unit/normalise/v3/normaliseSecurity.test.js b/test/unit/normalise/v3/normaliseSecurity.test.js deleted file mode 100644 index 3a827d2..0000000 --- a/test/unit/normalise/v3/normaliseSecurity.test.js +++ /dev/null @@ -1,45 +0,0 @@ -const { expect } = require('chai') - -const normaliseSecurity = require('src/normalise/v3/normaliseSecurity') - -describe('src/normalise/v3/normaliseSecurity', () => { - const doTest = (security, expected) => { - it('normalises the security block correctly', () => { - expect(normaliseSecurity(security)).to.equal(expected) - }) - } - - context('given a swagger security block', () => { - context('there are no scopes', () => { - const security = [ - { - example: [] - } - ] - - doTest(security, 'example') - }) - - context('there are scopes', () => { - const security = [ - { - example: ['identity.basic', 'identity.email', 'admin'] - } - ] - - doTest(security, 'admin,identity.basic,identity.email') - }) - }) - - context('given nothing', () => { - it('returns undefined', () => { - expect(normaliseSecurity()).to.be.undefined - }) - }) - - context('given empty security block', () => { - it('returns null', () => { - expect(normaliseSecurity([])).to.be.null - }) - }) -})