├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── cli.js ├── lib ├── bundler.js ├── codegen.js ├── helpers │ ├── extension.js │ └── handlebars.js └── swagger2.js ├── logo.ai ├── logo.png ├── package-lock.json ├── package.json ├── templates ├── README.md └── express-server │ ├── .editorconfig │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── config │ └── common.yml │ ├── log │ └── .gitkeep │ ├── package.json │ └── src │ ├── api │ ├── index.js │ ├── routes │ │ └── ___.js │ └── services │ │ └── ___.js │ ├── bin │ └── www │ └── lib │ ├── config.js │ ├── error.js │ └── logger.js └── tests ├── openapi3 ├── api-with-examples.yaml ├── callback-example.yaml ├── link-example.yaml ├── petstore-expanded.yaml ├── petstore.yaml └── uspto.yaml └── swagger2 ├── iris.yml ├── missing_description.yml └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [Makefile] 14 | indent_style = tab 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | gulpfile.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | extends: 'eslint:recommended' 2 | 3 | env: 4 | node: true 5 | es6: true 6 | 7 | parserOptions: 8 | ecmaVersion: 2017 9 | 10 | rules: 11 | # Possible Errors 12 | no-console: 0 13 | valid-jsdoc: [0, {requireReturn: false, requireParamDescription: false, requireReturnDescription: false}] 14 | 15 | # Best Practices 16 | consistent-return: 0 17 | curly: 0 18 | block-scoped-var: 2 19 | no-else-return: 2 20 | no-process-env: 2 21 | no-self-compare: 2 22 | no-throw-literal: 2 23 | no-void: 2 24 | radix: 2 25 | wrap-iife: [2, outside] 26 | 27 | # Variables 28 | no-shadow: 0 29 | no-use-before-define: [2, nofunc] 30 | 31 | # Node.js 32 | no-process-exit: 0 33 | handle-callback-err: [2, err] 34 | no-new-require: 2 35 | no-path-concat: 2 36 | 37 | # Stylistic Issues 38 | quotes: [2, single] 39 | camelcase: 0 40 | indent: [2, 2] 41 | no-lonely-if: 2 42 | no-floating-decimal: 2 43 | brace-style: [2, 1tbs, { "allowSingleLine": true }] 44 | comma-style: [2, last] 45 | consistent-this: [0, self] 46 | func-style: 0 47 | max-nested-callbacks: 0 48 | new-cap: [2, {capIsNewExceptions: [JID]}] 49 | no-multiple-empty-lines: [2, {max: 1}] 50 | no-nested-ternary: 2 51 | semi-spacing: [2, {before: false, after: true}] 52 | operator-assignment: [2, always] 53 | padded-blocks: [2, never] 54 | quote-props: [2, as-needed] 55 | space-before-function-paren: [2, always] 56 | keyword-spacing: [2, {after: true}] 57 | space-before-blocks: [2, always] 58 | array-bracket-spacing: [2, never] 59 | computed-property-spacing: [2, never] 60 | space-in-parens: [2, never] 61 | space-unary-ops: [2, {words: true, nonwords: false}] 62 | #spaced-line-comment: [2, always] 63 | wrap-regex: 2 64 | linebreak-style: [2, unix] 65 | semi: [2, always] 66 | 67 | # ECMAScript 6 68 | arrow-spacing: [2, {before: true, after: true}] 69 | no-class-assign: 2 70 | no-const-assign: 2 71 | no-dupe-class-members: 2 72 | no-this-before-super: 2 73 | no-var: 2 74 | object-shorthand: [2, always] 75 | prefer-arrow-callback: 2 76 | prefer-const: 2 77 | prefer-spread: 2 78 | prefer-template: 2 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | tests/generated* 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git* 2 | test/ 3 | .DS_Store 4 | *.swp 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 Fran Méndez 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

THIS PACKAGE IS NOT MAINTAINED ANYMORE. IF YOU WANT TO MAINTAIN IT DROP ME A LINE AT fran.mendez[at]hey.com.

2 | 3 |

4 |

5 | OpenAPI Node.js
Code Generator
6 |

7 |

8 | Use your API OpenAPI 3.x/Swagger 2 definition to generate Node.js ES7-compliant code for your API. 9 | 10 | The generated code features: 11 | 12 | * ES7 13 | * ESLint 14 | * YAML config file 15 | * Express 16 | * No transpiling 17 | 18 | ## Install 19 | 20 | To use it from the CLI: 21 | 22 | ```bash 23 | npm install -g swagger-node-codegen 24 | ``` 25 | 26 | To use it as a module in your project: 27 | 28 | ```bash 29 | npm install --save swagger-node-codegen 30 | ``` 31 | 32 | ## Requirements 33 | 34 | * Node.js v7.6+ 35 | 36 | ## Usage 37 | 38 | ### From the command-line interface (CLI) 39 | 40 | ```bash 41 | Usage: snc [options] 42 | 43 | 44 | Options: 45 | 46 | -V, --version output the version number 47 | -o, --output directory where to put the generated files (defaults to current directory) 48 | -t, --templates directory where templates are located (defaults to internal nodejs templates) 49 | -h, --help output usage information 50 | ``` 51 | 52 | #### Examples 53 | 54 | The shortest possible syntax: 55 | ```bash 56 | snc swagger.yaml 57 | ``` 58 | 59 | Specify where to put the generated code: 60 | ```bash 61 | snc swagger.yaml -o ./my-api 62 | ``` 63 | 64 | ### As a module in your project 65 | 66 | ```js 67 | const path = require('path'); 68 | const codegen = require('swagger-node-codegen'); 69 | const swagger = require('./swagger.json'); 70 | 71 | codegen.generate({ 72 | swagger, 73 | target_dir: path.resolve(__dirname, './my-api') 74 | }).then(() => { 75 | console.log('Done!'); 76 | }).catch(err => { 77 | console.error(`Something went wrong: ${err.message}`); 78 | }); 79 | ``` 80 | 81 | The `swagger` parameter can be either JSON or a path pointing to a JSON or YAML file. 82 | 83 | ```js 84 | const path = require('path'); 85 | const codegen = require('swagger-node-codegen'); 86 | 87 | codegen.generate({ 88 | swagger: path.resolve(__dirname, './swagger.yml'), 89 | target_dir: path.resolve(__dirname, './my-api') 90 | }).then(() => { 91 | console.log('Done!'); 92 | }).catch(err => { 93 | console.error(`Something went wrong: ${err.message}`); 94 | }); 95 | ``` 96 | #### Using async/await 97 | 98 | The function `codegen.generate` returns a Promise, so it means you can use async/await: 99 | 100 | ```js 101 | const path = require('path'); 102 | const codegen = require('swagger-node-codegen'); 103 | 104 | try { 105 | await codegen.generate({ 106 | swagger: path.resolve(__dirname, './swagger.yml'), 107 | target_dir: path.resolve(__dirname, './my-api') 108 | }); 109 | console.log('Done!'); 110 | } catch (err) { 111 | console.error(`Something went wrong: ${err.message}`); 112 | } 113 | ``` 114 | 115 | ## API Documentation 116 | 117 | ### Modules 118 | 119 |
120 |
codegen
121 |

This module generates a code skeleton for an API using OpenAPI/Swagger.

122 |
123 |
generatePromise
124 |

Generates a code skeleton for an API given an OpenAPI/Swagger file.

125 |
126 |
127 | 128 | 129 | 130 | ### codegen 131 | This module generates a code skeleton for an API using OpenAPI/Swagger. 132 | 133 | 134 | 135 | #### generate ⇒ Promise 136 | Generates a code skeleton for an API given an OpenAPI/Swagger file. 137 | 138 | 139 | | Param | Type | Description | 140 | | --- | --- | --- | 141 | | config | Object | Configuration options | 142 | | config.swagger | Object \| String | OpenAPI/Swagger JSON or a string pointing to an OpenAPI/Swagger file. | 143 | | config.target_dir | String | Path to the directory where the files will be generated. | 144 | | config.templates| String | Path to the directory where custom templates are (optional). | 145 | 146 | 147 | ## Templates 148 | You can create your own [templates](./templates/README.md). 149 | 150 | ## Authors 151 | 152 | * Fran Méndez ([@fmvilas](http://twitter.com/fmvilas)) 153 | * Richard Klose ([@richardklose](http://github.com/richardklose)) 154 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const path = require('path'); 4 | const program = require('commander'); 5 | const packageInfo = require('./package.json'); 6 | const codegen = require('./lib/codegen'); 7 | 8 | const red = text => `\x1b[31m${text}\x1b[0m`; 9 | const magenta = text => `\x1b[35m${text}\x1b[0m`; 10 | const yellow = text => `\x1b[33m${text}\x1b[0m`; 11 | const green = text => `\x1b[32m${text}\x1b[0m`; 12 | 13 | let swaggerFile; 14 | 15 | const parseOutput = dir => path.resolve(dir); 16 | 17 | program 18 | .version(packageInfo.version) 19 | .arguments('') 20 | .action((swaggerFilePath) => { 21 | swaggerFile = path.resolve(swaggerFilePath); 22 | }) 23 | .option('-b, --handlebars ', 'path to external handlebars helpers file (defaults to empty)', parseOutput) 24 | .option('-o, --output ', 'directory where to put the generated files (defaults to current directory)', parseOutput, process.cwd()) 25 | .option('-t, --templates ', 'directory where templates are located (defaults to internal nodejs templates)') 26 | .parse(process.argv); 27 | 28 | if (!swaggerFile) { 29 | console.error(red('> Path to Swagger file not provided.')); 30 | program.help(); // This exits the process 31 | } 32 | 33 | codegen.generate({ 34 | swagger: swaggerFile, 35 | target_dir: program.output, 36 | templates: program.templates ? path.resolve(process.cwd(), program.templates) : undefined, 37 | handlebars_helper: program.handlebars ? path.resolve(process.cwd(), program.handlebars) : undefined 38 | }).then(() => { 39 | console.log(green('Done! ✨')); 40 | console.log(yellow('Check out your shiny new API at ') + magenta(program.output) + yellow('.')); 41 | }).catch(err => { 42 | console.error(red('Aaww 💩. Something went wrong:')); 43 | console.error(red(err.stack || err.message)); 44 | }); 45 | 46 | process.on('unhandledRejection', (err) => console.error(err)); 47 | -------------------------------------------------------------------------------- /lib/bundler.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const YAML = require('js-yaml'); 4 | const RefParser = require('json-schema-ref-parser'); 5 | 6 | async function getFileContent (filePath) { 7 | return new Promise((resolve, reject) => { 8 | fs.readFile(path.resolve(__dirname, filePath), (err, content) => { 9 | if (err) return reject(err); 10 | resolve(content); 11 | }); 12 | }); 13 | } 14 | 15 | function parseContent (content) { 16 | content = content.toString('utf8'); 17 | try { 18 | return JSON.parse(content); 19 | } catch (e) { 20 | return YAML.safeLoad(content); 21 | } 22 | } 23 | 24 | async function dereference (json) { 25 | return RefParser.dereference(json, { 26 | dereference: { 27 | circular: 'ignore' 28 | } 29 | }); 30 | } 31 | 32 | async function bundle (json) { 33 | return RefParser.bundle(json, { 34 | dereference: { 35 | circular: 'ignore' 36 | } 37 | }); 38 | } 39 | 40 | async function bundler (filePath) { 41 | let content, parsedContent, dereferencedJSON, bundledJSON; 42 | 43 | try { 44 | content = await getFileContent(filePath); 45 | } catch (e) { 46 | console.error('Can not load the content of the Swagger specification file'); 47 | console.error(e); 48 | return; 49 | } 50 | 51 | try { 52 | parsedContent = parseContent(content); 53 | } catch (e) { 54 | console.error('Can not parse the content of the Swagger specification file'); 55 | console.error(e); 56 | return; 57 | } 58 | 59 | try { 60 | dereferencedJSON = await dereference(parsedContent); 61 | } catch (e) { 62 | console.error('Can not dereference the JSON obtained from the content of the Swagger specification file'); 63 | console.error(e); 64 | return; 65 | } 66 | 67 | try { 68 | bundledJSON = await bundle(dereferencedJSON); 69 | } catch (e) { 70 | console.error('Can not bundle the JSON obtained from the content of the Swagger specification file'); 71 | console.error(e); 72 | return; 73 | } 74 | 75 | return JSON.parse(JSON.stringify(bundledJSON)); 76 | } 77 | 78 | module.exports = bundler; 79 | -------------------------------------------------------------------------------- /lib/codegen.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module generates a code skeleton for an API using OpenAPI/Swagger. 3 | * @module codegen 4 | */ 5 | 6 | const os = require('os'); 7 | const path = require('path'); 8 | const fs = require('fs'); 9 | const Handlebars = require('handlebars'); 10 | const _ = require('lodash'); 11 | const swagger2 = require('./swagger2'); 12 | const xfs = require('fs.extra'); 13 | const randomName = require('project-name-generator'); 14 | 15 | const codegen = module.exports; 16 | 17 | require('./helpers/handlebars'); 18 | 19 | /** 20 | * Generates a file. 21 | * 22 | * @private 23 | * @param {Object} options 24 | * @param {String} options.templates_dir Directory where the templates live. 25 | * @param {String} options.target_dir Directory where the file will be generated. 26 | * @param {String} options.file_name Name of the generated file. 27 | * @param {String} options.root Root directory. 28 | * @param {Object} options.data Data to pass to the template. 29 | * @return {Promise} 30 | */ 31 | const generateFile = options => new Promise((resolve, reject) => { 32 | const templates_dir = options.templates_dir; 33 | const target_dir = options.target_dir; 34 | const file_name = options.file_name; 35 | const root = options.root; 36 | const data = options.data; 37 | 38 | fs.readFile(path.resolve(root, file_name), 'utf8', (err, content) => { 39 | if (err) return reject(err); 40 | 41 | try { 42 | const template = Handlebars.compile(content); 43 | const parsed_content = template(data); 44 | const template_path = path.relative(templates_dir, path.resolve(root, file_name)); 45 | const generated_path = path.resolve(target_dir, template_path); 46 | 47 | fs.writeFile(generated_path, parsed_content, 'utf8', (err) => { 48 | if (err) return reject(err); 49 | resolve(); 50 | }); 51 | } catch (e) { 52 | reject(e); 53 | } 54 | }); 55 | }); 56 | 57 | /** 58 | * Generates a file for every operation. 59 | * 60 | * @param config 61 | * @param operation 62 | * @param operation_name 63 | * @returns {Promise} 64 | */ 65 | const generateOperationFile = (config, operation, operation_name) => new Promise((resolve, reject) => { 66 | let headOperation = []; 67 | const operations_no_head = JSON.parse(JSON.stringify(operation)); 68 | operations_no_head.forEach(op => { 69 | if(op.path.head) { 70 | const obj = Object.assign({}, op); 71 | 72 | obj.path = { 73 | head: obj.path.head, 74 | endpointName: obj.path.endpointName 75 | }; 76 | 77 | delete op.path.head; 78 | headOperation.push(Object.assign({}, obj)); 79 | } 80 | }); 81 | 82 | fs.readFile(path.join(config.root, config.file_name), 'utf8', (err, data) => { 83 | if (err) return reject(err); 84 | const subdir = config.root.replace(new RegExp(`${config.templates_dir}[/]?`),''); 85 | const new_filename = config.file_name.replace('___', operation_name); 86 | const target_file = path.resolve(config.target_dir, subdir, new_filename); 87 | const template = Handlebars.compile(data.toString()); 88 | 89 | const content = template({ 90 | openbrace: '{', 91 | closebrace: '}' , 92 | operation_name: operation_name.replace(/[}{]/g, ''), 93 | operation: config.root.endsWith('routes') ? operations_no_head : operation, 94 | headOperation, 95 | swagger: config.data.swagger 96 | }); 97 | 98 | fs.writeFile(target_file, content, 'utf8', (err) => { 99 | if (err) return reject(err); 100 | resolve(); 101 | }); 102 | }); 103 | }); 104 | 105 | /** 106 | * Generates all the files for each operation by iterating over the operations. 107 | * 108 | * @param {Object} config Configuration options 109 | * @returns {Promise} 110 | */ 111 | const generateOperationFiles = config => new Promise((resolve, reject) => { 112 | const files = {}; 113 | _.each(config.data.swagger.paths, (path, path_name) => { 114 | const operation_name = path.endpointName; 115 | if (files[operation_name] === undefined) { 116 | files[operation_name] = []; 117 | } 118 | 119 | path_name = path_name.replace(/}/g, '').replace(/{/g, ':'); 120 | 121 | files[operation_name].push({ 122 | path_name, 123 | path, 124 | subresource: (path_name.substring(operation_name.length+1) || '/').replace(/}/g, '').replace(/{/g, ':') 125 | }); 126 | 127 | Promise.all( 128 | _.map(files, (operation, operation_name) => generateOperationFile(config, operation, operation_name)) 129 | ).then(resolve).catch(reject); 130 | }); 131 | }); 132 | 133 | /** 134 | * Generates the directory structure. 135 | * 136 | * @private 137 | * @param {Object} config Configuration options 138 | * @param {Object|String} config.swagger Swagger JSON or a string pointing to a Swagger file. 139 | * @param {String} config.target_dir Absolute path to the directory where the files will be generated. 140 | * @param {String} config.templates Absolute path to the templates that should be used. 141 | * @return {Promise} 142 | */ 143 | const generateDirectoryStructure = config => new Promise((resolve, reject) => { 144 | const target_dir = config.target_dir; 145 | const templates_dir = config.templates; 146 | 147 | xfs.mkdirpSync(target_dir); 148 | 149 | xfs.copyRecursive(templates_dir, target_dir, (err) => { 150 | if (err) return reject(err); 151 | 152 | const walker = xfs.walk(templates_dir, { 153 | followLinks: false 154 | }); 155 | 156 | walker.on('file', async (root, stats, next) => { 157 | try { 158 | if (stats.name.substr(0,3) === '___') { 159 | // this file should be handled for each in swagger.paths 160 | await generateOperationFiles({ 161 | root, 162 | templates_dir, 163 | target_dir, 164 | data: config, 165 | file_name: stats.name 166 | }); 167 | const template_path = path.relative(templates_dir, path.resolve(root, stats.name)); 168 | fs.unlink(path.resolve(target_dir, template_path), next); 169 | next(); 170 | } else { 171 | // this file should only exist once. 172 | await generateFile({ 173 | root, 174 | templates_dir, 175 | target_dir, 176 | data: config, 177 | file_name: stats.name 178 | }); 179 | next(); 180 | } 181 | } catch (e) { 182 | reject(e); 183 | } 184 | }); 185 | 186 | walker.on('errors', (root, nodeStatsArray) => { 187 | reject(nodeStatsArray); 188 | }); 189 | 190 | walker.on('end', async () => { 191 | resolve(); 192 | }); 193 | }); 194 | }); 195 | 196 | /** 197 | * Generates a code skeleton for an API given an OpenAPI/Swagger file. 198 | * 199 | * @module codegen.generate 200 | * @param {Object} config Configuration options 201 | * @param {Object|String} config.swagger OpenAPI/Swagger JSON or a string pointing to an OpenAPI/Swagger file. 202 | * @param {String} config.target_dir Path to the directory where the files will be generated. 203 | * @return {Promise} 204 | */ 205 | codegen.generate = config => new Promise((resolve, reject) => { 206 | if (config.handlebars_helper) { 207 | let handlebarsExt = require(config.handlebars_helper); 208 | handlebarsExt(Handlebars); 209 | } 210 | swagger2(config.swagger).then(swagger => { 211 | const random_name = randomName().dashed; 212 | config.swagger = swagger; 213 | 214 | _.defaultsDeep(config, { 215 | swagger: { 216 | info: { 217 | title: random_name 218 | } 219 | }, 220 | package: { 221 | name: _.kebabCase(_.result(config, 'swagger.info.title', random_name)) 222 | }, 223 | target_dir: path.resolve(os.tmpdir(), 'swagger-node-generated-code'), 224 | templates: path.resolve(__dirname, '../templates/express-server') 225 | }); 226 | 227 | generateDirectoryStructure(config).then(resolve).catch(reject); 228 | }).catch(reject); 229 | }); 230 | -------------------------------------------------------------------------------- /lib/helpers/extension.js: -------------------------------------------------------------------------------- 1 | function handlebarsExt(Handlebars) { 2 | 3 | // Create a file in your project like this and put your handlebars extensions in here 4 | // include it with the -b directive 5 | // 6 | // 7 | // /** 8 | // * Function to output the word "bar" 9 | // */ 10 | // Handlebars.registerHelper('foo', () => { 11 | // return "bar"; 12 | // }); 13 | } 14 | 15 | module.exports = handlebarsExt; 16 | -------------------------------------------------------------------------------- /lib/helpers/handlebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module registers different convenient Handlebars helpers. 3 | * @module HandlebarsHelper 4 | */ 5 | 6 | const _ = require('lodash'); 7 | const Handlebars = require('handlebars'); 8 | 9 | /** 10 | * Compares two values. 11 | */ 12 | Handlebars.registerHelper('equal', (lvalue, rvalue, options) => { 13 | if (arguments.length < 3) 14 | throw new Error('Handlebars Helper equal needs 2 parameters'); 15 | if (lvalue!=rvalue) { 16 | return options.inverse(this); 17 | } 18 | 19 | return options.fn(this); 20 | }); 21 | 22 | /** 23 | * Checks if a string ends with a provided value. 24 | */ 25 | Handlebars.registerHelper('endsWith', (lvalue, rvalue, options) => { 26 | if (arguments.length < 3) 27 | throw new Error('Handlebars Helper equal needs 2 parameters'); 28 | if (lvalue.lastIndexOf(rvalue) !== lvalue.length-1 || lvalue.length-1 < 0) { 29 | return options.inverse(this); 30 | } 31 | return options.fn(this); 32 | }); 33 | 34 | /** 35 | * Checks if a method is a valid HTTP method. 36 | */ 37 | Handlebars.registerHelper('validMethod', (method, options) => { 38 | const authorized_methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'COPY', 'HEAD', 'OPTIONS', 'LINK', 'UNLIK', 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND']; 39 | 40 | if (arguments.length < 3) 41 | throw new Error('Handlebars Helper validMethod needs 1 parameter'); 42 | if (authorized_methods.indexOf(method.toUpperCase()) === -1) { 43 | return options.inverse(this); 44 | } 45 | 46 | return options.fn(this); 47 | }); 48 | 49 | /** 50 | * Checks if a collection of responses contains no error responses. 51 | */ 52 | Handlebars.registerHelper('ifNoErrorResponses', (responses, options) => { 53 | const codes = responses ? Object.keys(responses) : []; 54 | if (codes.find(code => Number(code) >= 400)) return options.inverse(this); 55 | 56 | return options.fn(this); 57 | }); 58 | 59 | /** 60 | * Checks if a collection of responses contains no success responses. 61 | */ 62 | Handlebars.registerHelper('ifNoSuccessResponses', (responses, options) => { 63 | const codes = responses ? Object.keys(responses) : []; 64 | if (codes.find(code => Number(code) >= 200 && Number(code) < 300)) return options.inverse(this); 65 | 66 | return options.fn(this); 67 | }); 68 | 69 | /** 70 | * Checks if a string matches a RegExp. 71 | */ 72 | Handlebars.registerHelper('match', (lvalue, rvalue, options) => { 73 | if (arguments.length < 3) 74 | throw new Error('Handlebars Helper match needs 2 parameters'); 75 | if (!lvalue.match(rvalue)) { 76 | return options.inverse(this); 77 | } 78 | 79 | return options.fn(this); 80 | }); 81 | 82 | /** 83 | * Provides different ways to compare two values (i.e. equal, greater than, different, etc.) 84 | */ 85 | Handlebars.registerHelper('compare', (lvalue, rvalue, options) => { 86 | if (arguments.length < 3) throw new Error('Handlebars Helper "compare" needs 2 parameters'); 87 | 88 | const operator = options.hash.operator || '=='; 89 | const operators = { 90 | '==': (l,r) => { return l == r; }, 91 | '===': (l,r) => { return l === r; }, 92 | '!=': (l,r) => { return l != r; }, 93 | '<': (l,r) => { return l < r; }, 94 | '>': (l,r) => { return l > r; }, 95 | '<=': (l,r) => { return l <= r; }, 96 | '>=': (l,r) => { return l >= r; }, 97 | typeof: (l,r) => { return typeof l == r; } 98 | }; 99 | 100 | if (!operators[operator]) throw new Error(`Handlebars Helper 'compare' doesn't know the operator ${operator}`); 101 | 102 | const result = operators[operator](lvalue,rvalue); 103 | 104 | if (result) { 105 | return options.fn(this); 106 | } 107 | 108 | return options.inverse(this); 109 | }); 110 | 111 | /** 112 | * Capitalizes a string. 113 | */ 114 | Handlebars.registerHelper('capitalize', (str) => { 115 | return _.capitalize(str); 116 | }); 117 | 118 | /** 119 | * Converts a string to its camel-cased version. 120 | */ 121 | Handlebars.registerHelper('camelCase', (str) => { 122 | return _.camelCase(str); 123 | }); 124 | 125 | /** 126 | * Converts a multi-line string to a single line. 127 | */ 128 | Handlebars.registerHelper('inline', (str) => { 129 | return str ? str.replace(/\n/g, '') : ''; 130 | }); 131 | 132 | /** 133 | * Quotes a JS identifier, if necessary. 134 | */ 135 | Handlebars.registerHelper('quote', (str) => { 136 | return /[$&@-]/.test(str) ? `'${str}'` : str 137 | }); 138 | -------------------------------------------------------------------------------- /lib/swagger2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module is used to accommodate Swagger 2 data for easier rendering. 3 | * @module swagger2 4 | */ 5 | 6 | const _ = require('lodash'); 7 | const wrap = require('word-wrap'); 8 | const bundler = require('./bundler'); 9 | 10 | /** 11 | * Generates an "operationId" attribute based on path and method names. 12 | * 13 | * @private 14 | * @param {String} method_name HTTP method name. 15 | * @param {String} path_name Path name. 16 | * @return {String} 17 | */ 18 | const generateOperationId = (method_name, path_name) => { 19 | if (path_name === '/') return method_name; 20 | 21 | // clean url path for requests ending with '/' 22 | let clean_path = path_name; 23 | if (clean_path.indexOf('/', clean_path.length - 1) !== -1) { 24 | clean_path = clean_path.substring(0, clean_path.length - 1); 25 | } 26 | 27 | let segments = clean_path.split('/').slice(1); 28 | segments = _.transform(segments, (result, segment) => { 29 | if (segment[0] === '{' && segment[segment.length - 1] === '}') { 30 | segment = `by-${_.capitalize(segment.substring(1, segment.length - 1))}}`; 31 | } 32 | result.push(segment); 33 | }); 34 | 35 | return _.camelCase(`${method_name.toLowerCase()}-${segments.join('-')}`); 36 | }; 37 | 38 | /** 39 | * Accommodates Swagger object for easier rendering. 40 | * 41 | * @param {String|Object} swagger File path to Swagger file or **fully bundled and dereferenced** Swagger JS object. 42 | * @return {Object} The accommodated Swagger object. 43 | */ 44 | const swagger2 = async swagger => { 45 | const authorized_methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'COPY', 'HEAD', 'OPTIONS', 'LINK', 'UNLINK', 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND']; 46 | 47 | if (typeof swagger === 'string') { 48 | try { 49 | swagger = await bundler(swagger); 50 | } catch (e) { 51 | throw e; 52 | } 53 | } else if (typeof swagger !== 'object') { 54 | throw new Error(`Could not find a valid swagger definition: ${swagger}`); 55 | } 56 | 57 | swagger.basePath = swagger.basePath || ''; 58 | 59 | _.each(swagger.paths, (path, path_name) => { 60 | path.endpointName = path_name === '/' ? 'root' : path_name.split('/')[1]; 61 | _.each(path, (method, method_name) => { 62 | if (authorized_methods.indexOf(method_name.toUpperCase()) === -1) return; 63 | 64 | method.operationId = _.camelCase(method.operationId || generateOperationId(method_name, path_name).replace(/\s/g, '-')); 65 | method.descriptionLines = wrap(method.description || method.summary || '', { width: 60, indent: '' }).split(/\n/); 66 | _.each(method.parameters, param => { 67 | param.type = param.type || (param.schema ? param.schema.type : undefined); 68 | }); 69 | }); 70 | }); 71 | 72 | swagger.endpoints = _.uniq(_.map(swagger.paths, 'endpointName')); 73 | 74 | return swagger; 75 | }; 76 | 77 | module.exports = swagger2; 78 | -------------------------------------------------------------------------------- /logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmvilas/swagger-node-codegen/a10c1e9d2dcbc8919755b2795879d2f136ec7aef/logo.ai -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmvilas/swagger-node-codegen/a10c1e9d2dcbc8919755b2795879d2f136ec7aef/logo.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-node-codegen", 3 | "version": "1.6.3", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "argparse": { 8 | "version": "1.0.10", 9 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 10 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 11 | "requires": { 12 | "sprintf-js": "~1.0.2" 13 | } 14 | }, 15 | "call-me-maybe": { 16 | "version": "1.0.1", 17 | "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", 18 | "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" 19 | }, 20 | "commander": { 21 | "version": "2.12.2", 22 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", 23 | "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==" 24 | }, 25 | "debug": { 26 | "version": "3.1.0", 27 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 28 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 29 | "requires": { 30 | "ms": "2.0.0" 31 | } 32 | }, 33 | "esprima": { 34 | "version": "4.0.1", 35 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 36 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 37 | }, 38 | "foreachasync": { 39 | "version": "3.0.0", 40 | "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", 41 | "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=" 42 | }, 43 | "format-util": { 44 | "version": "1.0.3", 45 | "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.3.tgz", 46 | "integrity": "sha1-Ay3KShFiYqEsQ/TD7IVmQWxbLZU=" 47 | }, 48 | "fs-extra": { 49 | "version": "0.6.4", 50 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", 51 | "integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=", 52 | "requires": { 53 | "jsonfile": "~1.0.1", 54 | "mkdirp": "0.3.x", 55 | "ncp": "~0.4.2", 56 | "rimraf": "~2.2.0" 57 | }, 58 | "dependencies": { 59 | "jsonfile": { 60 | "version": "1.0.1", 61 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz", 62 | "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=" 63 | } 64 | } 65 | }, 66 | "fs.extra": { 67 | "version": "1.3.2", 68 | "resolved": "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz", 69 | "integrity": "sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k=", 70 | "requires": { 71 | "fs-extra": "~0.6.1", 72 | "mkdirp": "~0.3.5", 73 | "walk": "^2.3.9" 74 | } 75 | }, 76 | "handlebars": { 77 | "version": "4.1.2", 78 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", 79 | "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", 80 | "requires": { 81 | "neo-async": "^2.6.0", 82 | "optimist": "^0.6.1", 83 | "source-map": "^0.6.1", 84 | "uglify-js": "^3.1.4" 85 | } 86 | }, 87 | "js-yaml": { 88 | "version": "3.13.1", 89 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 90 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 91 | "requires": { 92 | "argparse": "^1.0.7", 93 | "esprima": "^4.0.0" 94 | } 95 | }, 96 | "json-schema-ref-parser": { 97 | "version": "4.0.4", 98 | "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-4.0.4.tgz", 99 | "integrity": "sha512-jbED8wPzWeGipggE2d7oM1kohCSZpscU+kBkkF+vjqI683y72hcPRGI/Ql1OqYK3MOnSQp+t1ZcHdQQ/MRF4UQ==", 100 | "requires": { 101 | "call-me-maybe": "^1.0.1", 102 | "debug": "^3.1.0", 103 | "js-yaml": "^3.10.0", 104 | "ono": "^4.0.2" 105 | } 106 | }, 107 | "lodash": { 108 | "version": "4.17.11", 109 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 110 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 111 | }, 112 | "minimist": { 113 | "version": "0.0.10", 114 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 115 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" 116 | }, 117 | "mkdirp": { 118 | "version": "0.3.5", 119 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", 120 | "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" 121 | }, 122 | "ms": { 123 | "version": "2.0.0", 124 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 125 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 126 | }, 127 | "ncp": { 128 | "version": "0.4.2", 129 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", 130 | "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=" 131 | }, 132 | "neo-async": { 133 | "version": "2.6.0", 134 | "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", 135 | "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==" 136 | }, 137 | "ono": { 138 | "version": "4.0.2", 139 | "resolved": "https://registry.npmjs.org/ono/-/ono-4.0.2.tgz", 140 | "integrity": "sha512-EFXJFoeF+KkZW4lwmcPMKHp2ZU7o6CM+ccX2nPbEJKiJIdyqbIcS1v6pmNgeNJ6x4/vEYn0/8oz66qXSPnnmSQ==", 141 | "requires": { 142 | "format-util": "^1.0.3" 143 | } 144 | }, 145 | "optimist": { 146 | "version": "0.6.1", 147 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 148 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 149 | "requires": { 150 | "minimist": "~0.0.1", 151 | "wordwrap": "~0.0.2" 152 | } 153 | }, 154 | "project-name-generator": { 155 | "version": "2.1.6", 156 | "resolved": "https://registry.npmjs.org/project-name-generator/-/project-name-generator-2.1.6.tgz", 157 | "integrity": "sha512-5r5MzHoNf7GTKg0V7zZvolpip5UAQiVpfcSN6QfEK8i/fSKlusJwxzEsKty9ce8h0e5GLoQ85dIGgV1TAnj3Kg==", 158 | "requires": { 159 | "commander": "^2.15.1", 160 | "lodash": "^4.17.10" 161 | }, 162 | "dependencies": { 163 | "commander": { 164 | "version": "2.20.0", 165 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", 166 | "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" 167 | }, 168 | "lodash": { 169 | "version": "4.17.11", 170 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 171 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 172 | } 173 | } 174 | }, 175 | "rimraf": { 176 | "version": "2.2.8", 177 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", 178 | "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" 179 | }, 180 | "source-map": { 181 | "version": "0.6.1", 182 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 183 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 184 | }, 185 | "sprintf-js": { 186 | "version": "1.0.3", 187 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 188 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 189 | }, 190 | "uglify-js": { 191 | "version": "3.5.6", 192 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.6.tgz", 193 | "integrity": "sha512-YDKRX8F0Y+Jr7LhoVk0n4G7ltR3Y7qFAj+DtVBthlOgCcIj1hyMigCfousVfn9HKmvJ+qiFlLDwaHx44/e5ZKw==", 194 | "optional": true, 195 | "requires": { 196 | "commander": "~2.20.0", 197 | "source-map": "~0.6.1" 198 | }, 199 | "dependencies": { 200 | "commander": { 201 | "version": "2.20.0", 202 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", 203 | "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", 204 | "optional": true 205 | } 206 | } 207 | }, 208 | "walk": { 209 | "version": "2.3.9", 210 | "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz", 211 | "integrity": "sha1-MbTbZnjyrgHDnqn7hyWpAx5Vins=", 212 | "requires": { 213 | "foreachasync": "^3.0.0" 214 | } 215 | }, 216 | "word-wrap": { 217 | "version": "1.1.0", 218 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.1.0.tgz", 219 | "integrity": "sha1-NWFT1h0QYQ1gB4XF1wEojgrnZKY=" 220 | }, 221 | "wordwrap": { 222 | "version": "0.0.3", 223 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 224 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-node-codegen", 3 | "main": "./lib/codegen.js", 4 | "version": "1.6.3", 5 | "description": "An OpenAPI 3.x/Swagger 2 code generator for Node.js", 6 | "bin": { 7 | "snc": "./cli.js" 8 | }, 9 | "bugs": { 10 | "url": "https://github.com/fmvilas/swagger-node-codegen/issues" 11 | }, 12 | "engines": { 13 | "node": ">=7.6" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/fmvilas/swagger-node-codegen.git" 18 | }, 19 | "keywords": [ 20 | "swagger", 21 | "openapi", 22 | "rest", 23 | "es6", 24 | "es7", 25 | "es2015", 26 | "node.js", 27 | "codegen", 28 | "generator" 29 | ], 30 | "author": { 31 | "name": "Fran Méndez", 32 | "email": "fmvilas@gmail.com", 33 | "url": "http://fmvilas.com" 34 | }, 35 | "contributors": [ 36 | { 37 | "name": "Richard Klose", 38 | "email": "richard.klose@gmail.com" 39 | } 40 | ], 41 | "homepage": "https://github.com/fmvilas/swagger-node-codegen", 42 | "dependencies": { 43 | "commander": "^2.12.2", 44 | "fs.extra": "1.3.2", 45 | "handlebars": "^4.1.2", 46 | "js-yaml": "^3.13.1", 47 | "json-schema-ref-parser": "^4.0.4", 48 | "lodash": "^4.17.11", 49 | "project-name-generator": "^2.1.6", 50 | "word-wrap": "1.1.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /templates/README.md: -------------------------------------------------------------------------------- 1 | # Templates 2 | 3 | ## Creating your own templates 4 | Templates are the sources where the code will be generated from. They 5 | already contain the basic skeleton code and some placeholders, that will 6 | be replaced during the code generation process by concrete names from 7 | the Swagger/OpenAPI Spec. 8 | There are three possible different types of template files: 9 | 1. Static Code: They only contain working code. Those files will 10 | be simply copied to the output directory. 11 | 2. Static templates: Those files will exist only once in the output. 12 | Their content will simply modified once according to the placeholders. 13 | 3. Abstract templates: Those templates will produce one file per 14 | operation/path, so there will be one template, but many output files. 15 | 16 | Assuming we have the following OpenAPI Spec: 17 | ``` 18 | openapi: "3.0.0" 19 | info: 20 | version: 1.0.0 21 | title: Swagger Petstore 22 | license: 23 | name: MIT 24 | servers: 25 | - url: http://petstore.swagger.io/v1 26 | paths: 27 | /pet: 28 | get:... 29 | post:... 30 | /pet/{petId}: 31 | get:... 32 | /user/login: 33 | post:... 34 | /user/{username}: 35 | get:... 36 | put:... 37 | delete:... 38 | ... 39 | ``` 40 | And some template files like this: 41 | ``` 42 | |- index.js // This file contains static code, e.g. starting a webserver and including ./api/index.js 43 | |+ api/ 44 | |- index.js // This is a static template, it contains placeholders that will be filled in, e.g. includes for each file in routes 45 | |+ routes/ 46 | |- ___.route.js // This file will be generated for each operation and contains skeleton code for each method for an operation. 47 | ``` 48 | The first important thing to notice here is the triple underscore in `___.route.js`. It will be replaced by the name of the path. 49 | 50 | In this example the generated directory structure will be like this: 51 | ``` 52 | |- index.js // This file still contains static code like before. 53 | |+ api/ 54 | |- index.js // This file will now e.g. have included the two files in routes. 55 | |+ routes/ 56 | |- pet.route.js // This file contains the code for methods on pets. 57 | | // (e.g. getPet, postPet, getPetByPetId). 58 | |- user.route.js // This file will contain the code for methods on users. 59 | // (e.g. postUserLogin, getUserByUsername, putUserByUsername, deleteUserByUsername). 60 | ``` 61 | 62 | ## Template file content 63 | The templates will be rendered into working code using 64 | [handlebars](http://handlebarsjs.com/), so they use the template 65 | language of handlebars. 66 | The code generator passes the Swagger/OpenAPI Spec to handlebars, so all 67 | information should be available there. In addition to that, the code 68 | generator adds a bit [more data](#data-that-is-passed-into-handlebars) that can be helpful. Also some 69 | [additional handlebars helpers](#additional-handlebars-helpers) exist. 70 | ### Examples: 71 | #### Dynamically require files in JavaScript 72 | ``` 73 | {{#each @root.swagger.endpoints}} 74 | const {{.}} = require('./routes/{{.}}.route.js') 75 | {{/each}} 76 | ``` 77 | will produce (using the OAS Spec example from above): 78 | ``` 79 | const pet = require('./routes/pet.route.js') 80 | const user = require('./routes/user.route.js') 81 | ``` 82 | 83 | ## Data that is passed into handlebars 84 | | Param | Type | Description | 85 | | --- | --- | --- | 86 | |swagger|object|The Swagger/OA Spec.| 87 | |swagger.endpoints| object | All first level endpoints (e.g `pet` and `user`) | 88 | 89 | ## Additional handlebars helpers 90 | All additional Handlebars helpers are located in [`lib/helpers/handlebars`](https://github.com/fmvilas/swagger-node-codegen/blob/master/lib/helpers/handlebars.js). 91 | 92 | ## Custom handlebars helpers 93 | Add your own handlebars helpers [`lib/helpers/handlebars/extension`](https://github.com/fmvilas/swagger-node-codegen/blob/master/lib/helpers/extension.js) 94 | -------------------------------------------------------------------------------- /templates/express-server/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | indent_size = 2 10 | 11 | [Makefile] 12 | indent_style = tab 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /templates/express-server/.eslintrc: -------------------------------------------------------------------------------- 1 | extends: 'eslint:recommended' 2 | 3 | env: 4 | node: true 5 | es6: true 6 | 7 | parserOptions: 8 | ecmaVersion: 2017 9 | 10 | rules: 11 | # Possible Errors 12 | no-console: 0 13 | valid-jsdoc: [0, {requireReturn: false, requireParamDescription: false, requireReturnDescription: false}] 14 | 15 | # Best Practices 16 | consistent-return: 0 17 | curly: 0 18 | block-scoped-var: 2 19 | no-else-return: 2 20 | no-process-env: 2 21 | no-self-compare: 2 22 | no-throw-literal: 2 23 | no-void: 2 24 | radix: 2 25 | wrap-iife: [2, outside] 26 | 27 | # Variables 28 | no-shadow: 0 29 | no-use-before-define: [2, nofunc] 30 | no-unused-vars: [2, { "argsIgnorePattern": "next" }] 31 | 32 | # Node.js 33 | no-process-exit: 0 34 | handle-callback-err: [2, err] 35 | no-new-require: 2 36 | no-path-concat: 2 37 | 38 | # Stylistic Issues 39 | quotes: [2, single] 40 | camelcase: 0 41 | indent: [2, 2] 42 | no-lonely-if: 2 43 | no-floating-decimal: 2 44 | brace-style: [2, 1tbs, { "allowSingleLine": true }] 45 | comma-style: [2, last] 46 | consistent-this: [0, self] 47 | func-style: 0 48 | max-nested-callbacks: 0 49 | new-cap: [2, {capIsNewExceptions: [JID]}] 50 | no-multiple-empty-lines: [2, {max: 1}] 51 | no-nested-ternary: 2 52 | semi-spacing: [2, {before: false, after: true}] 53 | operator-assignment: [2, always] 54 | padded-blocks: [2, never] 55 | quote-props: [2, as-needed] 56 | space-before-function-paren: [2, always] 57 | keyword-spacing: [2, {after: true}] 58 | space-before-blocks: [2, always] 59 | array-bracket-spacing: [2, never] 60 | computed-property-spacing: [2, never] 61 | space-in-parens: [2, never] 62 | space-unary-ops: [2, {words: true, nonwords: false}] 63 | #spaced-line-comment: [2, always] 64 | wrap-regex: 2 65 | linebreak-style: [2, unix] 66 | semi: [2, always] 67 | 68 | # ECMAScript 6 69 | arrow-spacing: [2, {before: true, after: true}] 70 | no-class-assign: 2 71 | no-const-assign: 2 72 | no-dupe-class-members: 2 73 | no-this-before-super: 2 74 | no-var: 2 75 | object-shorthand: [2, always] 76 | prefer-arrow-callback: 2 77 | prefer-const: 2 78 | prefer-spread: 2 79 | prefer-template: 2 80 | -------------------------------------------------------------------------------- /templates/express-server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /templates/express-server/README.md: -------------------------------------------------------------------------------- 1 | # {{swagger.info.title}} 2 | 3 | {{swagger.info.description}} 4 | -------------------------------------------------------------------------------- /templates/express-server/config/common.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | api: 3 | port: 3000 4 | 5 | logger: 6 | name: {{swagger.info.title}} 7 | level: debug 8 | levels: 9 | trace: 10 | debug: STDOUT 11 | info: 12 | warn: 13 | error: STDERR 14 | fatal: 15 | 16 | development: 17 | <<: *defaults 18 | 19 | production: 20 | <<: *defaults 21 | 22 | logger: 23 | level: debug 24 | levels: 25 | trace: 26 | debug: STDOUT 27 | info: ./log/info.log 28 | warn: ./log/warn.log 29 | error: ./log/error.log 30 | fatal: ./log/fatal.log 31 | -------------------------------------------------------------------------------- /templates/express-server/log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmvilas/swagger-node-codegen/a10c1e9d2dcbc8919755b2795879d2f136ec7aef/templates/express-server/log/.gitkeep -------------------------------------------------------------------------------- /templates/express-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{package.name}}", 3 | "description": "{{inline swagger.info.description}}", 4 | "version": "{{swagger.info.version}}", 5 | "scripts": { 6 | "start": "node src/bin/www", 7 | "dev": "node src/bin/www | bunyan" 8 | }, 9 | "dependencies": { 10 | "body-parser": "1.13.2", 11 | "bunyan": "1.5.1", 12 | "cookie-parser": "1.3.5", 13 | "express": "4.13.1", 14 | "node-yaml-config": "0.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /templates/express-server/src/api/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cookieParser = require('cookie-parser'); 3 | const bodyParser = require('body-parser'); 4 | const config = require('../lib/config'); 5 | const logger = require('../lib/logger'); 6 | 7 | const log = logger(config.logger); 8 | const app = express(); 9 | 10 | app.use(bodyParser.json()); 11 | app.use(bodyParser.urlencoded({ extended: false })); 12 | app.use(cookieParser()); 13 | 14 | /* 15 | * Routes 16 | */ 17 | {{#each @root.swagger.endpoints}} 18 | {{#endsWith @root.swagger.basePath '/'}} 19 | app.use('{{@root.swagger.basePath}}{{..}}', require('./routes/{{..}}')); 20 | {{else}} 21 | app.use('{{@root.swagger.basePath}}/{{..}}', require('./routes/{{..}}')); 22 | {{/endsWith}} 23 | {{/each}} 24 | 25 | // catch 404 26 | app.use((req, res, next) => { 27 | log.error(`Error 404 on ${req.url}.`); 28 | res.status(404).send({ status: 404, error: 'Not found' }); 29 | }); 30 | 31 | // catch errors 32 | app.use((err, req, res, next) => { 33 | const status = err.status || 500; 34 | const msg = err.error || err.message; 35 | log.error(`Error ${status} (${msg}) on ${req.method} ${req.url} with payload ${req.body}.`); 36 | res.status(status).send({ status, error: msg }); 37 | }); 38 | 39 | 40 | module.exports = app; 41 | -------------------------------------------------------------------------------- /templates/express-server/src/api/routes/___.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const {{camelCase operation_name}} = require('../services/{{operation_name}}'); 3 | 4 | const router = new express.Router(); 5 | 6 | {{#each headOperation}} 7 | {{#each this.path}} 8 | {{#validMethod @key}} 9 | /** 10 | {{#each ../descriptionLines}} 11 | * {{{this}}} 12 | {{/each}} 13 | */ 14 | router.{{@key}}('{{../../subresource}}', async (req, res, next) => { 15 | const options = { 16 | {{#if ../requestBody}} 17 | body: req.body{{#compare (lookup ../parameters 'length') 0 operator = '>' }},{{/compare}} 18 | {{/if}} 19 | {{#each ../parameters}} 20 | {{#equal this.in "query"}} 21 | {{{quote ../name}}}: req.query['{{../name}}']{{#unless @last}},{{/unless}} 22 | {{/equal}} 23 | {{#equal this.in "path"}} 24 | {{{quote ../name}}}: req.params['{{../name}}']{{#unless @last}},{{/unless}} 25 | {{/equal}} 26 | {{#equal this.in "header"}} 27 | {{{quote ../name}}}: req.header['{{../name}}']{{#unless @last}},{{/unless}} 28 | {{/equal}} 29 | {{/each}} 30 | }; 31 | 32 | try { 33 | const result = await {{camelCase ../../../operation_name}}.{{../operationId}}(options); 34 | {{#ifNoSuccessResponses ../responses}} 35 | res.header('X-Result', result.data).status(200).send(); 36 | {{else}} 37 | res.status(result.status || 200).send(result.data); 38 | {{/ifNoSuccessResponses}} 39 | } catch (err) { 40 | {{#ifNoErrorResponses ../responses}} 41 | return res.status(500).send({ 42 | status: 500, 43 | error: 'Server Error' 44 | }); 45 | {{else}} 46 | next(err); 47 | {{/ifNoErrorResponses}} 48 | } 49 | }); 50 | {{/validMethod}} 51 | {{/each}} 52 | {{/each}} 53 | 54 | {{#each operation}} 55 | {{#each this.path}} 56 | {{#validMethod @key}} 57 | /** 58 | {{#each ../descriptionLines}} 59 | * {{{this}}} 60 | {{/each}} 61 | */ 62 | router.{{@key}}('{{../../subresource}}', async (req, res, next) => { 63 | const options = { 64 | {{#if ../requestBody}} 65 | body: req.body{{#compare (lookup ../parameters 'length') 0 operator = '>' }},{{/compare}} 66 | {{/if}} 67 | {{#each ../parameters}} 68 | {{#equal this.in "query"}} 69 | {{{quote ../name}}}: req.query['{{../name}}']{{#unless @last}},{{/unless}} 70 | {{/equal}} 71 | {{#equal this.in "path"}} 72 | {{{quote ../name}}}: req.params['{{../name}}']{{#unless @last}},{{/unless}} 73 | {{/equal}} 74 | {{#equal this.in "header"}} 75 | {{{quote ../name}}}: req.header['{{../name}}']{{#unless @last}},{{/unless}} 76 | {{/equal}} 77 | {{#match @../key "(post|put)"}} 78 | {{#equal ../in "body"}} 79 | {{{quote ../name}}}: req.body['{{../name}}']{{#unless @last}},{{/unless}} 80 | {{/equal}} 81 | {{/match}} 82 | {{/each}} 83 | }; 84 | 85 | try { 86 | const result = await {{camelCase ../../../operation_name}}.{{../operationId}}(options); 87 | {{#ifNoSuccessResponses ../responses}} 88 | res.status(200).send(result.data); 89 | {{else}} 90 | res.status(result.status || 200).send(result.data); 91 | {{/ifNoSuccessResponses}} 92 | } catch (err) { 93 | {{#ifNoErrorResponses ../responses}} 94 | return res.status(500).send({ 95 | status: 500, 96 | error: 'Server Error' 97 | }); 98 | {{else}} 99 | next(err); 100 | {{/ifNoErrorResponses}} 101 | } 102 | }); 103 | 104 | {{/validMethod}} 105 | {{/each}} 106 | {{/each}} 107 | module.exports = router; 108 | -------------------------------------------------------------------------------- /templates/express-server/src/api/services/___.js: -------------------------------------------------------------------------------- 1 | const ServerError = require('../../lib/error'); 2 | {{#each operation}} 3 | {{#each this.path}} 4 | {{#validMethod @key}} 5 | /** 6 | * @param {Object} options 7 | {{#each ../parameters}} 8 | {{#if this.name}} 9 | * @param {{../../../../openbrace}}{{capitalize type}}{{../../../../closebrace}} options.{{name}} {{inline description}} 10 | {{/if}} 11 | {{/each}} 12 | * @throws {Error} 13 | * @return {Promise} 14 | */ 15 | module.exports.{{../operationId}} = async (options) => { 16 | // Implement your business logic here... 17 | // 18 | // This function should return as follows: 19 | // 20 | // return { 21 | // status: 200, // Or another success code. 22 | // data: [] // Optional. You can put whatever you want here. 23 | // }; 24 | // 25 | // If an error happens during your business logic implementation, 26 | // you should throw an error as follows: 27 | // 28 | // throw new ServerError({ 29 | // status: 500, // Or another error code. 30 | // error: 'Server Error' // Or another error message. 31 | // }); 32 | 33 | return { 34 | status: 200, 35 | data: '{{../operationId}} ok!' 36 | }; 37 | }; 38 | 39 | {{/validMethod}} 40 | {{/each}} 41 | {{/each}} 42 | -------------------------------------------------------------------------------- /templates/express-server/src/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | const app = require('../api'); 7 | const http = require('http'); 8 | const config = require('../lib/config'); 9 | const logger = require('../lib/logger'); 10 | 11 | const log = logger(config.logger); 12 | 13 | /** 14 | * Get port from environment and store in Express. 15 | */ 16 | const port = normalizePort(config.api.port || '3000'); 17 | app.set('port', port); 18 | 19 | /** 20 | * Create HTTP server. 21 | */ 22 | const server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | server.listen(port); 28 | server.on('error', onError); 29 | server.on('listening', onListening); 30 | 31 | /** 32 | * Normalize a port into a number, string, or false. 33 | */ 34 | function normalizePort (val) { 35 | const port = parseInt(val, 10); 36 | 37 | if (isNaN(port)) { 38 | // named pipe 39 | return val; 40 | } 41 | 42 | if (port >= 0) { 43 | // port number 44 | return port; 45 | } 46 | 47 | return false; 48 | } 49 | 50 | /** 51 | * Event listener for HTTP server "error" event. 52 | */ 53 | function onError (error) { 54 | if (error.syscall !== 'listen') { 55 | throw error; 56 | } 57 | 58 | const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`; 59 | 60 | // handle specific listen errors with friendly messages 61 | switch (error.code) { 62 | case 'EACCES': 63 | log.fatal(`${bind} requires elevated privileges`); 64 | process.exit(1); 65 | break; 66 | case 'EADDRINUSE': 67 | log.fatal(`${bind} is already in use`); 68 | process.exit(1); 69 | break; 70 | default: 71 | throw error; 72 | } 73 | } 74 | 75 | /** 76 | * Event listener for HTTP server "listening" event. 77 | */ 78 | function onListening () { 79 | const addr = server.address(); 80 | const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`; 81 | log.debug(`Listening on ${bind}`); 82 | } 83 | -------------------------------------------------------------------------------- /templates/express-server/src/lib/config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const yaml_config = require('node-yaml-config'); 3 | 4 | const config = yaml_config.load(path.join(__dirname, '../../config/common.yml')); 5 | 6 | module.exports = config; 7 | -------------------------------------------------------------------------------- /templates/express-server/src/lib/error.js: -------------------------------------------------------------------------------- 1 | class ServerError extends Error { 2 | constructor (...args) { 3 | super(...args); 4 | Error.captureStackTrace(this, ServerError); 5 | this.status = args[0].status; 6 | this.error = args[0].error; 7 | } 8 | } 9 | 10 | module.exports = ServerError; -------------------------------------------------------------------------------- /templates/express-server/src/lib/logger.js: -------------------------------------------------------------------------------- 1 | const bunyan = require('bunyan'); 2 | 3 | /** 4 | * @param {Object} config Logger configuration 5 | */ 6 | module.exports = config => { 7 | const bunyanConfig = []; 8 | const levels = Object.keys(config.levels); 9 | 10 | levels.forEach(level => { 11 | const bunyanLevel = config.levels[level]; 12 | if (!bunyanLevel) return; 13 | 14 | if (level === 'debug' && config.level !== 'debug') return; 15 | 16 | const logger = {level}; 17 | 18 | if (bunyanLevel === 'STDOUT') { 19 | logger.stream = process.stdout; 20 | } else if (bunyanLevel === 'STDERR') { 21 | logger.stream = process.stderr; 22 | } else if (bunyanLevel) { 23 | logger.path = bunyanLevel; 24 | } else { 25 | return; 26 | } 27 | 28 | bunyanConfig.push(logger); 29 | }); 30 | 31 | return bunyan.createLogger({ name: config.name, streams: bunyanConfig }); 32 | }; 33 | -------------------------------------------------------------------------------- /tests/openapi3/api-with-examples.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | title: Simple API overview 4 | version: v2 5 | paths: 6 | /: 7 | get: 8 | operationId: listVersionsv2 9 | summary: List API versions 10 | responses: 11 | '200': 12 | description: |- 13 | 200 response 14 | content: 15 | application/json: 16 | examples: 17 | foo: 18 | value: { 19 | "versions": [ 20 | { 21 | "status": "CURRENT", 22 | "updated": "2011-01-21T11:33:21Z", 23 | "id": "v2.0", 24 | "links": [ 25 | { 26 | "href": "http://127.0.0.1:8774/v2/", 27 | "rel": "self" 28 | } 29 | ] 30 | }, 31 | { 32 | "status": "EXPERIMENTAL", 33 | "updated": "2013-07-23T11:33:21Z", 34 | "id": "v3.0", 35 | "links": [ 36 | { 37 | "href": "http://127.0.0.1:8774/v3/", 38 | "rel": "self" 39 | } 40 | ] 41 | } 42 | ] 43 | } 44 | '300': 45 | description: |- 46 | 300 response 47 | content: 48 | application/json: 49 | examples: 50 | foo: 51 | value: | 52 | { 53 | "versions": [ 54 | { 55 | "status": "CURRENT", 56 | "updated": "2011-01-21T11:33:21Z", 57 | "id": "v2.0", 58 | "links": [ 59 | { 60 | "href": "http://127.0.0.1:8774/v2/", 61 | "rel": "self" 62 | } 63 | ] 64 | }, 65 | { 66 | "status": "EXPERIMENTAL", 67 | "updated": "2013-07-23T11:33:21Z", 68 | "id": "v3.0", 69 | "links": [ 70 | { 71 | "href": "http://127.0.0.1:8774/v3/", 72 | "rel": "self" 73 | } 74 | ] 75 | } 76 | ] 77 | } 78 | /v2: 79 | get: 80 | operationId: getVersionDetailsv2 81 | summary: Show API version details 82 | responses: 83 | '200': 84 | description: |- 85 | 200 response 86 | content: 87 | application/json: 88 | examples: 89 | foo: 90 | value: { 91 | "version": { 92 | "status": "CURRENT", 93 | "updated": "2011-01-21T11:33:21Z", 94 | "media-types": [ 95 | { 96 | "base": "application/xml", 97 | "type": "application/vnd.openstack.compute+xml;version=2" 98 | }, 99 | { 100 | "base": "application/json", 101 | "type": "application/vnd.openstack.compute+json;version=2" 102 | } 103 | ], 104 | "id": "v2.0", 105 | "links": [ 106 | { 107 | "href": "http://127.0.0.1:8774/v2/", 108 | "rel": "self" 109 | }, 110 | { 111 | "href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf", 112 | "type": "application/pdf", 113 | "rel": "describedby" 114 | }, 115 | { 116 | "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl", 117 | "type": "application/vnd.sun.wadl+xml", 118 | "rel": "describedby" 119 | }, 120 | { 121 | "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl", 122 | "type": "application/vnd.sun.wadl+xml", 123 | "rel": "describedby" 124 | } 125 | ] 126 | } 127 | } 128 | '203': 129 | description: |- 130 | 203 response 131 | content: 132 | application/json: 133 | examples: 134 | foo: 135 | value: { 136 | "version": { 137 | "status": "CURRENT", 138 | "updated": "2011-01-21T11:33:21Z", 139 | "media-types": [ 140 | { 141 | "base": "application/xml", 142 | "type": "application/vnd.openstack.compute+xml;version=2" 143 | }, 144 | { 145 | "base": "application/json", 146 | "type": "application/vnd.openstack.compute+json;version=2" 147 | } 148 | ], 149 | "id": "v2.0", 150 | "links": [ 151 | { 152 | "href": "http://23.253.228.211:8774/v2/", 153 | "rel": "self" 154 | }, 155 | { 156 | "href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf", 157 | "type": "application/pdf", 158 | "rel": "describedby" 159 | }, 160 | { 161 | "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl", 162 | "type": "application/vnd.sun.wadl+xml", 163 | "rel": "describedby" 164 | } 165 | ] 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /tests/openapi3/callback-example.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | title: Callback Example 4 | version: 1.0.0 5 | paths: 6 | /streams: 7 | post: 8 | description: subscribes a client to receive out-of-band data 9 | parameters: 10 | - name: callbackUrl 11 | in: query 12 | required: true 13 | description: | 14 | the location where data will be sent. Must be network accessible 15 | by the source server 16 | schema: 17 | type: string 18 | format: uri 19 | example: https://tonys-server.com 20 | responses: 21 | '201': 22 | description: subscription successfully created 23 | content: 24 | application/json: 25 | schema: 26 | description: subscription information 27 | required: 28 | - subscriptionId 29 | properties: 30 | subscriptionId: 31 | description: this unique identifier allows management of the subscription 32 | type: string 33 | example: 2531329f-fb09-4ef7-887e-84e648214436 34 | callbacks: 35 | # the name `onData` is a convenience locator 36 | onData: 37 | # when data is sent, it will be sent to the `callbackUrl` provided 38 | # when making the subscription PLUS the suffix `/data` 39 | '{$request.query.callbackUrl}/data': 40 | post: 41 | requestBody: 42 | description: subscription payload 43 | content: 44 | application/json: 45 | schema: 46 | properties: 47 | timestamp: 48 | type: string 49 | format: date-time 50 | userData: 51 | type: string 52 | responses: 53 | '202': 54 | description: | 55 | Your server implementation should return this HTTP status code 56 | if the data was received successfully 57 | '204': 58 | description: | 59 | Your server should return this HTTP status code if no longer interested 60 | in further updates 61 | -------------------------------------------------------------------------------- /tests/openapi3/link-example.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | title: Link Example 4 | version: 1.0.0 5 | paths: 6 | /2.0/users/{username}: 7 | get: 8 | operationId: getUserByName 9 | parameters: 10 | - name: username 11 | in: path 12 | required: true 13 | schema: 14 | type: string 15 | responses: 16 | '200': 17 | description: The User 18 | content: 19 | application/json: 20 | schema: 21 | $ref: '#/components/schemas/user' 22 | links: 23 | userRepositories: 24 | $ref: '#/components/links/UserRepositories' 25 | /2.0/repositories/{username}: 26 | get: 27 | operationId: getRepositoriesByOwner 28 | parameters: 29 | - name: username 30 | in: path 31 | required: true 32 | schema: 33 | type: string 34 | responses: 35 | '200': 36 | description: repositories owned by the supplied user 37 | content: 38 | application/json: 39 | schema: 40 | type: array 41 | items: 42 | $ref: '#/components/schemas/repository' 43 | links: 44 | userRepository: 45 | $ref: '#/components/links/UserRepository' 46 | /2.0/repositories/{username}/{slug}: 47 | get: 48 | operationId: getRepository 49 | parameters: 50 | - name: username 51 | in: path 52 | required: true 53 | schema: 54 | type: string 55 | - name: slug 56 | in: path 57 | required: true 58 | schema: 59 | type: string 60 | responses: 61 | '200': 62 | description: The repository 63 | content: 64 | application/json: 65 | schema: 66 | $ref: '#/components/schemas/repository' 67 | links: 68 | repositoryPullRequests: 69 | $ref: '#/components/links/RepositoryPullRequests' 70 | /2.0/repositories/{username}/{slug}/pullrequests: 71 | get: 72 | operationId: getPullRequestsByRepository 73 | parameters: 74 | - name: username 75 | in: path 76 | required: true 77 | schema: 78 | type: string 79 | - name: slug 80 | in: path 81 | required: true 82 | schema: 83 | type: string 84 | - name: state 85 | in: query 86 | schema: 87 | type: string 88 | enum: 89 | - open 90 | - merged 91 | - declined 92 | responses: 93 | '200': 94 | description: an array of pull request objects 95 | content: 96 | application/json: 97 | schema: 98 | type: array 99 | items: 100 | $ref: '#/components/schemas/pullrequest' 101 | /2.0/repositories/{username}/{slug}/pullrequests/{pid}: 102 | get: 103 | operationId: getPullRequestsById 104 | parameters: 105 | - name: username 106 | in: path 107 | required: true 108 | schema: 109 | type: string 110 | - name: slug 111 | in: path 112 | required: true 113 | schema: 114 | type: string 115 | - name: pid 116 | in: path 117 | required: true 118 | schema: 119 | type: string 120 | responses: 121 | '200': 122 | description: a pull request object 123 | content: 124 | application/json: 125 | schema: 126 | $ref: '#/components/schemas/pullrequest' 127 | links: 128 | pullRequestMerge: 129 | $ref: '#/components/links/PullRequestMerge' 130 | /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge: 131 | post: 132 | operationId: mergePullRequest 133 | parameters: 134 | - name: username 135 | in: path 136 | required: true 137 | schema: 138 | type: string 139 | - name: slug 140 | in: path 141 | required: true 142 | schema: 143 | type: string 144 | - name: pid 145 | in: path 146 | required: true 147 | schema: 148 | type: string 149 | responses: 150 | '204': 151 | description: the PR was successfully merged 152 | components: 153 | links: 154 | UserRepositories: 155 | # returns array of '#/components/schemas/repository' 156 | operationId: getRepositoriesByOwner 157 | parameters: 158 | username: $response.body#/username 159 | UserRepository: 160 | # returns '#/components/schemas/repository' 161 | operationId: getRepository 162 | parameters: 163 | username: $response.body#/owner/username 164 | slug: $response.body#/slug 165 | RepositoryPullRequests: 166 | # returns '#/components/schemas/pullrequest' 167 | operationId: getPullRequestsByRepository 168 | parameters: 169 | username: $response.body#/owner/username 170 | slug: $response.body#/slug 171 | PullRequestMerge: 172 | # executes /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge 173 | operationId: mergePullRequest 174 | parameters: 175 | username: $response.body#/author/username 176 | slug: $response.body#/repository/slug 177 | pid: $response.body#/id 178 | schemas: 179 | user: 180 | type: object 181 | properties: 182 | username: 183 | type: string 184 | uuid: 185 | type: string 186 | repository: 187 | type: object 188 | properties: 189 | slug: 190 | type: string 191 | owner: 192 | $ref: '#/components/schemas/user' 193 | pullrequest: 194 | type: object 195 | properties: 196 | id: 197 | type: integer 198 | title: 199 | type: string 200 | repository: 201 | $ref: '#/components/schemas/repository' 202 | author: 203 | $ref: '#/components/schemas/user' 204 | -------------------------------------------------------------------------------- /tests/openapi3/petstore-expanded.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification 6 | termsOfService: http://swagger.io/terms/ 7 | contact: 8 | name: Swagger API Team 9 | email: foo@example.com 10 | url: http://madskristensen.net 11 | license: 12 | name: MIT 13 | url: http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT 14 | servers: 15 | - url: http://petstore.swagger.io/api 16 | paths: 17 | /pets: 18 | get: 19 | description: | 20 | Returns all pets from the system that the user has access to 21 | Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. 22 | 23 | Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. 24 | operationId: findPets 25 | parameters: 26 | - name: tags 27 | in: query 28 | description: tags to filter by 29 | required: false 30 | style: form 31 | schema: 32 | type: array 33 | items: 34 | type: string 35 | - name: limit 36 | in: query 37 | description: maximum number of results to return 38 | required: false 39 | schema: 40 | type: integer 41 | format: int32 42 | responses: 43 | '200': 44 | description: pet response 45 | content: 46 | application/json: 47 | schema: 48 | type: array 49 | items: 50 | $ref: '#/components/schemas/Pet' 51 | default: 52 | description: unexpected error 53 | content: 54 | application/json: 55 | schema: 56 | $ref: '#/components/schemas/Error' 57 | head: 58 | description: | 59 | Gets the total count of all pets 60 | operationId: countPets 61 | parameters: 62 | - name: tags 63 | in: query 64 | description: tags to filter by 65 | required: false 66 | style: form 67 | schema: 68 | type: array 69 | items: 70 | type: string 71 | responses: 72 | '200': 73 | description: pets count 74 | headers: 75 | X-Total-Count: 76 | description: The total count of pets. 77 | style: simple 78 | explode: false 79 | schema: 80 | type: integer 81 | post: 82 | description: Creates a new pet in the store. Duplicates are allowed 83 | operationId: addPet 84 | requestBody: 85 | description: Pet to add to the store 86 | required: true 87 | content: 88 | application/json: 89 | schema: 90 | $ref: '#/components/schemas/NewPet' 91 | responses: 92 | '200': 93 | description: pet response 94 | content: 95 | application/json: 96 | schema: 97 | $ref: '#/components/schemas/Pet' 98 | default: 99 | description: unexpected error 100 | content: 101 | application/json: 102 | schema: 103 | $ref: '#/components/schemas/Error' 104 | /pets/{id}: 105 | get: 106 | description: Returns a user based on a single ID, if the user does not have access to the pet 107 | operationId: find pet by id 108 | parameters: 109 | - name: id 110 | in: path 111 | description: ID of pet to fetch 112 | required: true 113 | schema: 114 | type: integer 115 | format: int64 116 | responses: 117 | '200': 118 | description: pet response 119 | content: 120 | application/json: 121 | schema: 122 | $ref: '#/components/schemas/Pet' 123 | default: 124 | description: unexpected error 125 | content: 126 | application/json: 127 | schema: 128 | $ref: '#/components/schemas/Error' 129 | delete: 130 | description: deletes a single pet based on the ID supplied 131 | operationId: deletePet 132 | parameters: 133 | - name: id 134 | in: path 135 | description: ID of pet to delete 136 | required: true 137 | schema: 138 | type: integer 139 | format: int64 140 | responses: 141 | '204': 142 | description: pet deleted 143 | default: 144 | description: unexpected error 145 | content: 146 | application/json: 147 | schema: 148 | $ref: '#/components/schemas/Error' 149 | components: 150 | schemas: 151 | Pet: 152 | allOf: 153 | - $ref: '#/components/schemas/NewPet' 154 | - required: 155 | - id 156 | properties: 157 | id: 158 | type: integer 159 | format: int64 160 | 161 | NewPet: 162 | required: 163 | - name 164 | properties: 165 | name: 166 | type: string 167 | tag: 168 | type: string 169 | 170 | Error: 171 | required: 172 | - code 173 | - message 174 | properties: 175 | code: 176 | type: integer 177 | format: int32 178 | message: 179 | type: string 180 | -------------------------------------------------------------------------------- /tests/openapi3/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pets: 11 | get: 12 | summary: List all pets 13 | operationId: listPets 14 | tags: 15 | - pets 16 | parameters: 17 | - name: limit 18 | in: query 19 | description: How many items to return at one time (max 100) 20 | required: false 21 | schema: 22 | type: integer 23 | format: int32 24 | responses: 25 | '200': 26 | description: An paged array of pets 27 | headers: 28 | x-next: 29 | description: A link to the next page of responses 30 | schema: 31 | type: string 32 | content: 33 | application/json: 34 | schema: 35 | $ref: "#/components/schemas/Pets" 36 | default: 37 | description: unexpected error 38 | content: 39 | application/json: 40 | schema: 41 | $ref: "#/components/schemas/Error" 42 | post: 43 | summary: Create a pet 44 | operationId: createPets 45 | tags: 46 | - pets 47 | responses: 48 | '201': 49 | description: Null response 50 | default: 51 | description: unexpected error 52 | content: 53 | application/json: 54 | schema: 55 | $ref: "#/components/schemas/Error" 56 | /pets/{petId}: 57 | get: 58 | summary: Info for a specific pet 59 | operationId: showPetById 60 | tags: 61 | - pets 62 | parameters: 63 | - name: petId 64 | in: path 65 | required: true 66 | description: The id of the pet to retrieve 67 | schema: 68 | type: string 69 | responses: 70 | '200': 71 | description: Expected response to a valid request 72 | content: 73 | application/json: 74 | schema: 75 | $ref: "#/components/schemas/Pets" 76 | default: 77 | description: unexpected error 78 | content: 79 | application/json: 80 | schema: 81 | $ref: "#/components/schemas/Error" 82 | components: 83 | schemas: 84 | Pet: 85 | required: 86 | - id 87 | - name 88 | properties: 89 | id: 90 | type: integer 91 | format: int64 92 | name: 93 | type: string 94 | tag: 95 | type: string 96 | Pets: 97 | type: array 98 | items: 99 | $ref: "#/components/schemas/Pet" 100 | Error: 101 | required: 102 | - code 103 | - message 104 | properties: 105 | code: 106 | type: integer 107 | format: int32 108 | message: 109 | type: string 110 | -------------------------------------------------------------------------------- /tests/openapi3/uspto.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | servers: 3 | - url: '{scheme}://developer.uspto.gov/ds-api' 4 | variables: 5 | scheme: 6 | description: 'The Data Set API is accessible via https and http' 7 | enum: 8 | - 'https' 9 | - 'http' 10 | default: 'https' 11 | info: 12 | description: >- 13 | The Data Set API (DSAPI) allows the public users to discover and search 14 | USPTO exported data sets. This is a generic API that allows USPTO users to 15 | make any CSV based data files searchable through API. With the help of GET 16 | call, it returns the list of data fields that are searchable. With the help 17 | of POST call, data can be fetched based on the filters on the field names. 18 | Please note that POST call is used to search the actual data. The reason for 19 | the POST call is that it allows users to specify any complex search criteria 20 | without worry about the GET size limitations as well as encoding of the 21 | input parameters. 22 | version: 1.0.0 23 | title: USPTO Data Set API 24 | contact: 25 | name: Open Data Portal 26 | url: 'https://developer.uspto.gov' 27 | email: developer@uspto.gov 28 | tags: 29 | - name: metadata 30 | description: Find out about the data sets 31 | - name: search 32 | description: Search a data set 33 | paths: 34 | /: 35 | get: 36 | tags: 37 | - metadata 38 | operationId: list-data-sets 39 | summary: List available data sets 40 | responses: 41 | '200': 42 | description: Returns a list of data sets 43 | content: 44 | application/json: 45 | schema: 46 | $ref: '#/components/schemas/dataSetList' 47 | example: 48 | { 49 | "total": 2, 50 | "apis": [ 51 | { 52 | "apiKey": "oa_citations", 53 | "apiVersionNumber": "v1", 54 | "apiUrl": "https://developer.uspto.gov/ds-api/oa_citations/v1/fields", 55 | "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/oa_citations.json" 56 | }, 57 | { 58 | "apiKey": "cancer_moonshot", 59 | "apiVersionNumber": "v1", 60 | "apiUrl": "https://developer.uspto.gov/ds-api/cancer_moonshot/v1/fields", 61 | "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/cancer_moonshot.json" 62 | } 63 | ] 64 | } 65 | /{dataset}/{version}/fields: 66 | get: 67 | tags: 68 | - metadata 69 | summary: >- 70 | Provides the general information about the API and the list of fields 71 | that can be used to query the dataset. 72 | description: >- 73 | This GET API returns the list of all the searchable field names that are 74 | in the oa_citations. Please see the 'fields' attribute which returns an 75 | array of field names. Each field or a combination of fields can be 76 | searched using the syntax options shown below. 77 | operationId: list-searchable-fields 78 | parameters: 79 | - name: dataset 80 | in: path 81 | description: 'Name of the dataset. In this case, the default value is oa_citations' 82 | required: true 83 | schema: 84 | type: string 85 | default: oa_citations 86 | - name: version 87 | in: path 88 | description: Version of the dataset. 89 | required: true 90 | schema: 91 | type: string 92 | default: v1 93 | responses: 94 | '200': 95 | description: >- 96 | The dataset api for the given version is found and it is accessible 97 | to consume. 98 | content: 99 | application/json: 100 | schema: 101 | type: string 102 | '404': 103 | description: >- 104 | The combination of dataset name and version is not found in the 105 | system or it is not published yet to be consumed by public. 106 | content: 107 | application/json: 108 | schema: 109 | type: string 110 | /{dataset}/{version}/records: 111 | post: 112 | tags: 113 | - search 114 | summary: >- 115 | Provides search capability for the data set with the given search 116 | criteria. 117 | description: >- 118 | This API is based on Solr/Lucense Search. The data is indexed using 119 | SOLR. This GET API returns the list of all the searchable field names 120 | that are in the Solr Index. Please see the 'fields' attribute which 121 | returns an array of field names. Each field or a combination of fields 122 | can be searched using the Solr/Lucene Syntax. Please refer 123 | https://lucene.apache.org/core/3_6_2/queryparsersyntax.html#Overview for 124 | the query syntax. List of field names that are searchable can be 125 | determined using above GET api. 126 | operationId: perform-search 127 | parameters: 128 | - name: version 129 | in: path 130 | description: Version of the dataset. 131 | required: true 132 | schema: 133 | type: string 134 | default: v1 135 | - name: dataset 136 | in: path 137 | description: 'Name of the dataset. In this case, the default value is oa_citations' 138 | required: true 139 | schema: 140 | type: string 141 | default: oa_citations 142 | responses: 143 | '200': 144 | description: successful operation 145 | content: 146 | application/json: 147 | schema: 148 | type: array 149 | items: 150 | type: object 151 | additionalProperties: 152 | type: object 153 | '404': 154 | description: No matching record found for the given criteria. 155 | requestBody: 156 | content: 157 | application/x-www-form-urlencoded: 158 | schema: 159 | type: object 160 | properties: 161 | criteria: 162 | description: >- 163 | Uses Lucene Query Syntax in the format of 164 | propertyName:value, propertyName:[num1 TO num2] and date 165 | range format: propertyName:[yyyyMMdd TO yyyyMMdd]. In the 166 | response please see the 'docs' element which has the list of 167 | record objects. Each record structure would consist of all 168 | the fields and their corresponding values. 169 | type: string 170 | default: '*:*' 171 | start: 172 | description: Starting record number. Default value is 0. 173 | type: integer 174 | default: 0 175 | rows: 176 | description: >- 177 | Specify number of rows to be returned. If you run the search 178 | with default values, in the response you will see 'numFound' 179 | attribute which will tell the number of records available in 180 | the dataset. 181 | type: integer 182 | default: 100 183 | required: 184 | - criteria 185 | components: 186 | schemas: 187 | dataSetList: 188 | type: object 189 | properties: 190 | total: 191 | type: integer 192 | apis: 193 | type: array 194 | items: 195 | type: object 196 | properties: 197 | apiKey: 198 | type: string 199 | description: To be used as a dataset parameter value 200 | apiVersionNumber: 201 | type: string 202 | description: To be used as a version parameter value 203 | apiUrl: 204 | type: string 205 | format: uriref 206 | description: "The URL describing the dataset's fields" 207 | apiDocumentationUrl: 208 | type: string 209 | format: uriref 210 | description: A URL to the API console for each API 211 | -------------------------------------------------------------------------------- /tests/swagger2/iris.yml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | info: 3 | version: 1.0.0 4 | title: Iris 5 | description: > 6 | Video meetings integrated right inside your app. 7 | contact: 8 | name: Iris 9 | url: 'http://irishd.io' 10 | email: fmvilas@gmail.com 11 | license: 12 | name: Swedish API License Derivative 13 | url: 'http://www.apilicens.se/en/dokumentation/resultat/?1=y&2=y&3=y&4=y&5=y&6=y&7=y&8=y&9=y&10=y&11=y&12=y&13=y&' 14 | host: iris.herokuapp.com 15 | basePath: /v1 16 | schemes: 17 | - https 18 | consumes: 19 | - application/json 20 | produces: 21 | - application/json 22 | paths: 23 | /customers: 24 | get: 25 | description: Customers 26 | parameters: 27 | - name: limit 28 | in: query 29 | description: Maximum number of customers 30 | required: false 31 | type: integer 32 | default: 100 33 | minimum: 1 34 | maximum: 1000 35 | responses: 36 | '200': 37 | description: OK 38 | security: 39 | - oauth2: 40 | - list_customers 41 | post: 42 | description: Customers 43 | parameters: 44 | - name: customer 45 | in: body 46 | description: The customer JSON you want to post 47 | schema: 48 | $ref: '#/definitions/Customer' 49 | required: true 50 | responses: 51 | '201': 52 | description: OK 53 | security: 54 | - oauth2: 55 | - write_customers 56 | '/customers/{id}': 57 | get: 58 | description: Customers 59 | parameters: 60 | - name: id 61 | in: path 62 | type: integer 63 | required: true 64 | responses: 65 | '200': 66 | description: OK 67 | security: 68 | - oauth2: 69 | - read_customers 70 | put: 71 | description: Customers 72 | parameters: 73 | - name: id 74 | in: path 75 | type: integer 76 | required: true 77 | responses: 78 | '202': 79 | description: OK 80 | security: 81 | - oauth2: 82 | - write_customers 83 | delete: 84 | description: Customers 85 | parameters: 86 | - name: id 87 | in: path 88 | type: integer 89 | required: true 90 | responses: 91 | '202': 92 | description: OK 93 | security: 94 | - oauth2: 95 | - write_customers 96 | /rooms: 97 | get: 98 | description: | 99 | Gets `Room` objects. 100 | parameters: 101 | - name: limit 102 | in: query 103 | description: Maximum number of rooms 104 | required: false 105 | type: integer 106 | default: 10 107 | minimum: 1 108 | maximum: 100 109 | responses: 110 | '200': 111 | description: Successful response 112 | schema: 113 | title: ArrayOfRooms 114 | type: array 115 | items: 116 | $ref: '#/definitions/Room' 117 | security: 118 | - oauth2: 119 | - read_rooms 120 | post: 121 | description: | 122 | Creates new `Room` object. 123 | parameters: 124 | - name: room 125 | in: body 126 | description: The room JSON you want to post 127 | schema: 128 | $ref: '#/definitions/Room' 129 | required: true 130 | responses: 131 | '201': 132 | description: Make a new room 133 | security: 134 | - oauth2: 135 | - write_rooms 136 | '/rooms/{roomId}': 137 | get: 138 | description: | 139 | Retrieves a room. 140 | parameters: 141 | - name: roomId 142 | in: path 143 | type: string 144 | description: ID of the room 145 | required: true 146 | responses: 147 | '200': 148 | description: Sends the room with room Id 149 | security: 150 | - oauth2: 151 | - read_rooms 152 | put: 153 | description: | 154 | Updates a room. 155 | parameters: 156 | - name: roomId 157 | in: path 158 | type: string 159 | description: ID of the room 160 | required: true 161 | - name: room 162 | in: body 163 | description: The pet JSON you want to post 164 | schema: 165 | $ref: '#/definitions/Room' 166 | required: true 167 | responses: 168 | '200': 169 | description: Updates the pet 170 | security: 171 | - oauth2: 172 | - write_rooms 173 | delete: 174 | description: Deletes a room 175 | parameters: 176 | - in: path 177 | name: roomId 178 | description: Room id to delete 179 | required: true 180 | type: integer 181 | responses: 182 | '200': 183 | description: Deletes the room 184 | security: 185 | - oauth2: 186 | - write_rooms 187 | 188 | /users: 189 | get: 190 | description: Users 191 | parameters: 192 | - name: limit 193 | in: query 194 | description: Maximum number of users 195 | required: false 196 | type: integer 197 | default: 100 198 | minimum: 1 199 | maximum: 1000 200 | - name: room 201 | in: query 202 | description: Filter by room 203 | type: integer 204 | required: false 205 | responses: 206 | '200': 207 | description: OK 208 | security: 209 | - oauth2: 210 | - read_users 211 | post: 212 | description: Users 213 | parameters: 214 | - name: user 215 | in: body 216 | description: The user JSON you want to post 217 | schema: 218 | $ref: '#/definitions/User' 219 | required: true 220 | responses: 221 | '201': 222 | description: OK 223 | security: 224 | - oauth2: 225 | - write_users 226 | '/users/{id}': 227 | get: 228 | description: Users 229 | parameters: 230 | - name: id 231 | in: path 232 | type: integer 233 | required: true 234 | responses: 235 | '200': 236 | description: OK 237 | security: 238 | - oauth2: 239 | - read_users 240 | put: 241 | description: Users 242 | parameters: 243 | - name: id 244 | in: path 245 | type: integer 246 | required: true 247 | responses: 248 | '202': 249 | description: OK 250 | security: 251 | - oauth2: 252 | - write_users 253 | delete: 254 | description: Users 255 | parameters: 256 | - name: id 257 | in: path 258 | type: integer 259 | required: true 260 | responses: 261 | '202': 262 | description: OK 263 | security: 264 | - oauth2: 265 | - write_users 266 | 267 | securityDefinitions: 268 | api_key: 269 | type: apiKey 270 | name: api_key 271 | in: header 272 | oauth2: 273 | type: oauth2 274 | flow: accessCode 275 | authorizationUrl: /auth 276 | tokenUrl: /auth/token 277 | scopes: 278 | list_customers: List all users 279 | write_customers: Modify customers information 280 | read_customers: Read customers information 281 | write_users: Modify users information 282 | read_users: Read users information 283 | write_rooms: Modify rooms in your account 284 | read_rooms: Read your rooms 285 | 286 | definitions: 287 | Room: 288 | type: object 289 | properties: 290 | name: 291 | type: string 292 | private: 293 | type: boolean 294 | default: false 295 | required: 296 | - name 297 | User: 298 | type: object 299 | properties: 300 | name: 301 | type: string 302 | required: 303 | - name 304 | Customer: 305 | type: object 306 | properties: 307 | name: 308 | type: string 309 | required: 310 | - name 311 | -------------------------------------------------------------------------------- /tests/swagger2/missing_description.yml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | info: 3 | version: 1.0.0 4 | title: Iris 5 | description: > 6 | Video meetings integrated right inside your app. 7 | contact: 8 | name: Iris 9 | url: 'http://irishd.io' 10 | email: fmvilas@gmail.com 11 | license: 12 | name: Swedish API License Derivative 13 | url: 'http://www.apilicens.se/en/dokumentation/resultat/?1=y&2=y&3=y&4=y&5=y&6=y&7=y&8=y&9=y&10=y&11=y&12=y&13=y&' 14 | host: iris.herokuapp.com 15 | basePath: /v1 16 | schemes: 17 | - https 18 | consumes: 19 | - application/json 20 | produces: 21 | - application/json 22 | paths: 23 | /customers: 24 | get: 25 | parameters: 26 | - name: limit 27 | in: query 28 | description: Maximum number of customers 29 | required: false 30 | type: integer 31 | default: 100 32 | minimum: 1 33 | maximum: 1000 34 | responses: 35 | '200': 36 | description: OK 37 | security: 38 | - oauth2: 39 | - list_customers 40 | post: 41 | summary: Customers 42 | parameters: 43 | - name: customer 44 | in: body 45 | description: The customer JSON you want to post 46 | schema: 47 | $ref: '#/definitions/Customer' 48 | required: true 49 | responses: 50 | '201': 51 | description: OK 52 | security: 53 | - oauth2: 54 | - write_customers 55 | '/customers/{id}': 56 | get: 57 | description: Customers 58 | parameters: 59 | - name: id 60 | in: path 61 | type: integer 62 | required: true 63 | responses: 64 | '200': 65 | description: OK 66 | security: 67 | - oauth2: 68 | - read_customers 69 | put: 70 | description: Customers 71 | parameters: 72 | - name: id 73 | in: path 74 | type: integer 75 | required: true 76 | responses: 77 | '202': 78 | description: OK 79 | security: 80 | - oauth2: 81 | - write_customers 82 | delete: 83 | description: Customers 84 | parameters: 85 | - name: id 86 | in: path 87 | type: integer 88 | required: true 89 | responses: 90 | '202': 91 | description: OK 92 | security: 93 | - oauth2: 94 | - write_customers 95 | /rooms: 96 | get: 97 | description: | 98 | Gets `Room` objects. 99 | parameters: 100 | - name: limit 101 | in: query 102 | description: Maximum number of rooms 103 | required: false 104 | type: integer 105 | default: 10 106 | minimum: 1 107 | maximum: 100 108 | responses: 109 | '200': 110 | description: Successful response 111 | schema: 112 | title: ArrayOfRooms 113 | type: array 114 | items: 115 | $ref: '#/definitions/Room' 116 | security: 117 | - oauth2: 118 | - read_rooms 119 | post: 120 | description: | 121 | Creates new `Room` object. 122 | parameters: 123 | - name: room 124 | in: body 125 | description: The room JSON you want to post 126 | schema: 127 | $ref: '#/definitions/Room' 128 | required: true 129 | responses: 130 | '201': 131 | description: Make a new room 132 | security: 133 | - oauth2: 134 | - write_rooms 135 | '/rooms/{roomId}': 136 | get: 137 | description: | 138 | Retrieves a room. 139 | parameters: 140 | - name: roomId 141 | in: path 142 | type: string 143 | description: ID of the room 144 | required: true 145 | responses: 146 | '200': 147 | description: Sends the room with room Id 148 | security: 149 | - oauth2: 150 | - read_rooms 151 | put: 152 | description: | 153 | Updates a room. 154 | parameters: 155 | - name: roomId 156 | in: path 157 | type: string 158 | description: ID of the room 159 | required: true 160 | - name: room 161 | in: body 162 | description: The pet JSON you want to post 163 | schema: 164 | $ref: '#/definitions/Room' 165 | required: true 166 | responses: 167 | '200': 168 | description: Updates the pet 169 | security: 170 | - oauth2: 171 | - write_rooms 172 | delete: 173 | description: Deletes a room 174 | parameters: 175 | - in: path 176 | name: roomId 177 | description: Room id to delete 178 | required: true 179 | type: integer 180 | responses: 181 | '200': 182 | description: Deletes the room 183 | security: 184 | - oauth2: 185 | - write_rooms 186 | 187 | /users: 188 | get: 189 | description: Users 190 | parameters: 191 | - name: limit 192 | in: query 193 | description: Maximum number of users 194 | required: false 195 | type: integer 196 | default: 100 197 | minimum: 1 198 | maximum: 1000 199 | - name: room 200 | in: query 201 | description: Filter by room 202 | type: integer 203 | required: false 204 | responses: 205 | '200': 206 | description: OK 207 | security: 208 | - oauth2: 209 | - read_users 210 | post: 211 | description: Users 212 | parameters: 213 | - name: user 214 | in: body 215 | description: The user JSON you want to post 216 | schema: 217 | $ref: '#/definitions/User' 218 | required: true 219 | responses: 220 | '201': 221 | description: OK 222 | security: 223 | - oauth2: 224 | - write_users 225 | '/users/{id}': 226 | get: 227 | description: Users 228 | parameters: 229 | - name: id 230 | in: path 231 | type: integer 232 | required: true 233 | responses: 234 | '200': 235 | description: OK 236 | security: 237 | - oauth2: 238 | - read_users 239 | definitions: 240 | Room: 241 | type: object 242 | properties: 243 | name: 244 | type: string 245 | private: 246 | type: boolean 247 | default: false 248 | required: 249 | - name 250 | User: 251 | type: object 252 | properties: 253 | name: 254 | type: string 255 | required: 256 | - name 257 | Customer: 258 | type: object 259 | properties: 260 | name: 261 | type: string 262 | required: 263 | - name 264 | -------------------------------------------------------------------------------- /tests/swagger2/test.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import generator from '../dist/codegen.js'; 3 | import uber_api from './uber.json'; 4 | 5 | generator.generate({ 6 | swagger: path.resolve(__dirname, 'iris.yml'), 7 | target_dir: path.resolve(__dirname, 'generated-yaml/') 8 | }, (err) => { 9 | if (err) console.log(err.message); 10 | }); 11 | 12 | generator.generate({ 13 | swagger: path.resolve(__dirname, 'irisdd.yml'), 14 | target_dir: path.resolve(__dirname, 'generated-yaml/') 15 | }, (err) => { 16 | if (err) console.log('There should be an error here -> ', err.message); 17 | }); 18 | 19 | generator.generate({ 20 | swagger: path.resolve(__dirname, 'uber.json'), 21 | target_dir: path.resolve(__dirname, 'generated-json/') 22 | }, (err) => { 23 | if (err) console.log(err.message); 24 | }); 25 | 26 | generator.generate({ 27 | swagger: uber_api, 28 | target_dir: path.resolve(__dirname, 'generated/') 29 | }, (err) => { 30 | if (err) console.log(err.message); 31 | }); 32 | 33 | generator.generate({ 34 | swagger: path.resolve(__dirname, 'missing_description.yml'), 35 | target_dir: path.resolve(__dirname, 'generated-with-missing-description') 36 | }, (err) => { 37 | if (err) console.log(err.message); 38 | }); 39 | --------------------------------------------------------------------------------