Skip to content
This repository was archived by the owner on Oct 7, 2021. It is now read-only.

Commit f39781d

Browse files
author
cvignola
authored
Merge pull request #2 from ibm-developer/development
Initial load into master
2 parents 2a64767 + e78e75d commit f39781d

17 files changed

+8287
-1
lines changed

.eslintignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
# If eslint goes into this dir it will read package.json as a config file
3+
# We either rename the package.json template, or we don't lint this dir
4+
generators/app/templates/
5+
generators/core/templates/
6+
coverage/

.eslintrc.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
env:
2+
node: true
3+
es6: true
4+
mocha: true
5+
6+
rules:
7+
strict: 2
8+
no-var: 2
9+
no-console: 0
10+
indent: [2, 2, { "SwitchCase": 1}]
11+
12+
extends: eslint:recommended
13+
14+
plugins: [ejs]

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
coverage/
3+
.nyc_output/
4+
.vscode/
5+
.idea
6+
*.lock

.travis.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
language: node_js
2+
3+
node_js:
4+
- '8'
5+
6+
script: npm test && npm run coveralls && ./npm_patch.sh
7+
8+
branches:
9+
only:
10+
- development
11+
- master
12+
13+
notifications:
14+
slack:
15+
rooms:
16+
- $SLACK_ARF_DEVOPS
17+
18+
deploy:
19+
provider: npm
20+
email: $NPM_EMAIL
21+
api_key: $NPM_TOKEN
22+
on:
23+
branch: master

README.md

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,82 @@
1-
# openapi-support
1+
# ibm-openapi-support
2+
3+
This module is a utility to make the process of loading and parsing documents in the OpenAPI (swagger) format a simple task. The primary use case for this is code generation where a swagger document is loaded, parsed and then relavent data structures for building api endpoints are made available.
4+
5+
## Quick start
6+
7+
Swaggerize contains two modules, index.js and utils.js. They are described below:
8+
9+
* __utils.loadAsync__: This utility method is used for loading a swagger document from a file path or url. The method takes two arguments and returns a dictionary with two keys, _**loaded**_ and _**parsed**_. The _**loaded**_ key contains a javascript object containing the original document. The _**parsed**_ key contains the a dictionary with parsed swagger elements. The arguments to __loadAsync__:
10+
- path or url to the openApi document.
11+
- in-memory filesystem object; only required if loading from a file path.
12+
13+
```javascript
14+
var swaggerize = require('ibm-openapi-support')
15+
var loadedApi
16+
var parsedSwagger
17+
18+
var memFs = require('mem-fs')
19+
var editor = require('mem-fs-editor')
20+
var store = memFs.create()
21+
var fs = editor.create(store)
22+
23+
return utils.loadAsync('../resources/person_dino.json', fs)
24+
.then(loaded => swaggerize.parse(loaded, formatters))
25+
.then(response => {
26+
loadedApi = response.loaded
27+
parsedSwagger = response.parsed
28+
})
29+
```
30+
31+
* __index.parse__: This method is used to parse the swagger conument and build a dictionary of structures that contain the routes, resources and basepath required for generating API code. The parse method will use the supplied formatters to modify the path and the resource. This method takes two parameters:
32+
- stringified OpenApi (swagger) document.
33+
- formatters dictionary: This contains formatters for the path _**pathFormatter**_ and for the resource _**resourceFormatter**_ The formatters take a path parameter and return a string.
34+
35+
```javascript
36+
var swaggerize = require('ibm-openapi-support')
37+
var swaggerizeUtils = require('ibm-openapi-support/utils')
38+
39+
var swaggerDocument = 'your OpenApi (swagger) document goes here'
40+
41+
function reformatPathToNodeExpress (thepath) {
42+
// take a swagger path and convert the parameters to express format.
43+
// i.e. convert "/path/to/{param1}/{param2}" to "/path/to/:param1/:param2"
44+
var newPath = thepath.replace(/{/g, ':')
45+
return newPath.replace(/}/g, '')
46+
}
47+
48+
function resourceNameFromPath (thePath) {
49+
// grab the first valid element of a path (or partial path) and return it.
50+
return thePath.match(/^\/*([^/]+)/)[1]
51+
}
52+
53+
this.parsedSwagger = undefined;
54+
var formatters = {
55+
'pathFormatter': helpers.reformatPathToNodeExpress,
56+
'resourceFormatter': helpers.resourceNameFromPath
57+
}
58+
59+
if (swaggerDocument !== undefined) {
60+
return swaggerize.parse(swaggerDocument, formatters)
61+
.then(response => {
62+
this.loadedApi = response.loaded
63+
this.parsedSwagger = response.parsed
64+
})
65+
.catch(err => {
66+
err.message = 'failed to parse document ' + err.message
67+
throw err
68+
})
69+
}
70+
71+
if (this.parsedSwagger) {
72+
Object.keys(this.parsedSwagger.resources).forEach(function(resource) {
73+
var context = {
74+
'resource': resource,
75+
'routes': this.parsedSwagger.resources[resource],
76+
'basepath': this.parsedSwagger.basepath
77+
}
78+
this.fs.copyTpl(this.templatePath('fromswagger/routers/router.js'), this.destinationPath(`server/routers/${resource}.js`), context)
79+
this.fs.copyTpl(this.templatePath('test/resource.js'), this.destinationPath(`test/${resource}.js`), context)
80+
}.bind(this))
81+
}
82+
```

coverage.lcov

Whitespace-only changes.

index.js

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright IBM Corporation 2017
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
'use strict'
17+
let debug = require('debug')('arf:swaggerize:index')
18+
let utils = require('./utils')
19+
let swaggerParser = require('swagger-parser')
20+
let builderUtils = require('swaggerize-routes/lib/utils')
21+
22+
function ensureValidAsync (loadedSwagger) {
23+
debug('in ensureValidAsync')
24+
return swaggerParser.validate(loadedSwagger)
25+
.catch(function (err) {
26+
debug(err)
27+
throw new Error('does not conform to swagger specification')
28+
})
29+
}
30+
31+
function parseSwagger (api, formatters) {
32+
debug('in parseSwagger')
33+
// walk the api, extract the schemas from the definitions, the parameters and the responses.
34+
let resources = {}
35+
let refs = []
36+
let basePath = api.basePath || undefined
37+
38+
formatters = formatters === undefined ? {} : formatters
39+
40+
let pathFormatter = formatters['pathFormatter'] || function (path) { return path }
41+
let resourceFormatter = formatters['resourceFormatter'] || function (route) { return route }
42+
43+
Object.keys(api.paths).forEach(function (path) {
44+
let resource = resourceFormatter(path)
45+
46+
debug('path:', path, 'becomes resource: "' + resource + '" with route: "' + pathFormatter(path) + '"')
47+
// for each path, walk the method verbs
48+
builderUtils.verbs.forEach(function (verb) {
49+
if (api.paths[path][verb]) {
50+
if (!resources[resource]) {
51+
resources[resource] = []
52+
}
53+
54+
debug('parsing verb:', verb)
55+
// save the method and the path in the resources list.
56+
resources[resource].push({method: verb, route: pathFormatter(path)})
57+
// process the parameters
58+
if (api.paths[path][verb].parameters) {
59+
let parameters = api.paths[path][verb].parameters
60+
61+
parameters.forEach(function (parameter) {
62+
if (parameter.schema) {
63+
if (parameter.schema.$ref) {
64+
// handle the schema ref
65+
let ref = utils.getRefName(parameter.schema.$ref)
66+
refs[ref] = api.definitions[ref]
67+
} else if (parameter.schema.items) {
68+
// handle array of schema items
69+
if (parameter.schema.items.$ref) {
70+
let ref = utils.getRefName(parameter.schema.items.$ref)
71+
// handle the schema ref
72+
refs[ref] = api.definitions[ref]
73+
}
74+
}
75+
}
76+
})
77+
}
78+
79+
// process the responses. 200 and default are probably the only ones that make any sense.
80+
['200', 'default'].forEach(function (responseType) {
81+
if (api.paths[path][verb].responses && api.paths[path][verb].responses[responseType]) {
82+
let responses = api.paths[path][verb].responses
83+
if (responses[responseType] && responses[responseType].schema) {
84+
let ref
85+
if (responses[responseType].schema.$ref) {
86+
// handle the schema ref
87+
ref = utils.getRefName(responses[responseType].schema.$ref)
88+
refs[ref] = api.definitions[ref]
89+
} else if (responses[responseType].schema.type && responses[responseType].schema.type === 'array') {
90+
if (responses[responseType].schema.items && responses[responseType].schema.items.$ref) {
91+
ref = utils.getRefName(responses[responseType].schema.items.$ref)
92+
refs[ref] = api.definitions[ref]
93+
if (responses[responseType].schema.items) {
94+
// handle array of schema items
95+
if (responses[responseType].schema.items.$ref) {
96+
// handle the schema ref
97+
ref = utils.getRefName(responses[responseType].schema.items.$ref)
98+
refs[ref] = api.definitions[ref]
99+
}
100+
}
101+
}
102+
}
103+
}
104+
}
105+
})
106+
}
107+
})
108+
})
109+
110+
let foundNewRef
111+
do {
112+
foundNewRef = false
113+
// now parse the schemas for child references.
114+
Object.keys(refs).forEach(function (schema) {
115+
if (refs[schema] && refs[schema].properties) {
116+
let properties = refs[schema].properties
117+
Object.keys(properties).forEach(function (property) {
118+
let name
119+
if (properties[property].$ref) {
120+
// this property contains a definition reference.
121+
name = utils.getRefName(properties[property].$ref)
122+
if (!refs[name]) {
123+
refs[name] = api.definitions[name]
124+
foundNewRef = true
125+
}
126+
} else if (properties[property].items && properties[property].items.$ref) {
127+
// this property contains a definition reference.
128+
name = utils.getRefName(properties[property].items.$ref)
129+
if (!refs[name]) {
130+
refs[name] = api.definitions[name]
131+
foundNewRef = true
132+
}
133+
}
134+
})
135+
}
136+
})
137+
} while (foundNewRef)
138+
139+
let parsed = {basepath: basePath, resources: resources, refs: refs}
140+
return parsed
141+
}
142+
143+
exports.parse = function (swaggerStr, formatters) {
144+
debug('in parse')
145+
let loaded = JSON.parse(swaggerStr)
146+
return ensureValidAsync(loaded)
147+
.then(function () {
148+
debug('successfully validated against schema')
149+
// restore the original swagger as the call to ensureValidAsync modifies the original loaded object.
150+
loaded = JSON.parse(swaggerStr)
151+
return { loaded: loaded, parsed: parseSwagger(loaded, formatters) }
152+
})
153+
}

npm_patch.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env bash
2+
3+
set -ev
4+
5+
if [[ "${TRAVIS_PULL_REQUEST}" = "false" ]]; then
6+
echo "Not a Pull Request build. Proceeding."
7+
8+
# On development branch, we want to auto increment package patch version number
9+
# and push commit back to repo
10+
if [[ "${TRAVIS_BRANCH}" = "development" ]]; then
11+
echo "On development branch"
12+
echo "Commit message: ${TRAVIS_COMMIT_MESSAGE}"
13+
14+
if [[ "${TRAVIS_COMMIT_MESSAGE}" == "[Travis - npm version patch]"* ]]; then
15+
echo "This is a version increment commit. Doing nothing."
16+
else
17+
echo "Incrementing patch version and pushing back to repo."
18+
19+
echo "commit: ${TRAVIS_COMMIT}"
20+
USER_EMAIL=$(git --no-pager show -s --format='%ae' "${TRAVIS_COMMIT}")
21+
USER_NAME=$(git --no-pager show -s --format='%an' "${TRAVIS_COMMIT}")
22+
echo "user email: ${USER_EMAIL}"
23+
echo "user name: ${USER_NAME}"
24+
git config user.email "${USER_EMAIL}"
25+
git config user.name "${USER_NAME}"
26+
27+
git checkout -- .
28+
git checkout -b increment-patch-version
29+
npm version patch -m "[Travis - npm version patch] Increment package version to %s"
30+
31+
git branch --set-upstream-to origin/development
32+
git push $GITHUB_URL_SECURED HEAD:development
33+
git push $GITHUB_URL_SECURED HEAD:development --tags
34+
fi
35+
fi
36+
else
37+
echo "This is a Pull Request build. Doing nothing."
38+
fi

0 commit comments

Comments
 (0)