UNPKG

8.6 kBJavaScriptView Raw
1#!/usr/bin/env node
2// @ts-check
3'use strict';
4
5const fs = require('fs');
6const path = require('path');
7const url = require('url');
8const util = require('util');
9
10const yaml = require('yaml');
11const fetch = require('node-fetch');
12const co = require('co');
13const swagger2openapi = require('swagger2openapi');
14const stools = require('swagger-tools');
15const mkdirp = require('mkdirp');
16const rimraf = require('rimraf');
17const admzip = require('adm-zip');
18
19const processor = require('./local.js');
20const remote = require('./remote.js');
21
22async function list(provider, filter) {
23 process.exitCode = await remote.list(provider, filter);
24 process.exit();
25}
26
27var argv = require('yargs')
28 .usage('cg [options] {[path]configName} {openapi-definition}')
29 .boolean('debug')
30 .alias('d','debug')
31 .describe('debug','Turn on debugging information in the model')
32 .string('filter')
33 .describe('filter','Filter term to use with --list')
34 .boolean('flat')
35 .alias('f','flat')
36 .describe('flat','Do not include config-name in output directory structure')
37 .boolean('lint')
38 .alias('l','lint')
39 .describe('lint','Lint input definition')
40 .string('list')
41 .describe('list','List available templates for provider (og or sc)')
42 .string('output')
43 .alias('o','output')
44 .describe('output','Specify output directory')
45 .default('output','./out/')
46 .boolean('stools')
47 .alias('s','stools')
48 .describe('stools','Use swagger-tools to validate OpenAPI 2.0 definitions')
49 .string('templates')
50 .alias('t','templates')
51 .describe('templates','Specify templates directory')
52 .boolean('verbose')
53 .describe('verbose','Increase verbosity')
54 .alias('v','verbose')
55 .boolean('zip')
56 .alias('z','zip')
57 .describe('zip','Create a .zip file instead of individual files')
58 .version()
59 .argv;
60
61if (argv.list) {
62 list(argv.list, argv.filter);
63}
64
65let configStr = argv._[0] || 'nodejs';
66let configName = path.basename(configStr);
67let remoteConfig = configName.indexOf(':')>-1;
68let configPath = path.dirname(configStr);
69if (!configPath || (configPath === '.')) configPath = path.resolve(__dirname,'configs');
70let configFile = path.join(configPath,configName)+'.json';
71let config = remoteConfig ? { defaults: {} } : yaml.parse(fs.readFileSync(configFile,'utf8'), {prettyErrors: true});
72let defName = argv._[1] || path.resolve(__dirname,'defs/petstore3.json');
73
74let finish = remoteConfig ? finishRemote : finishLocal;
75
76config.outputDir = argv.output;
77config.templateDir = argv.templates;
78
79if (config.generator) {
80 let generator_path = path.resolve(configPath, config.generator);
81 config.generator = require(generator_path);
82}
83
84let zipFiles = {};
85
86function nop(arg, callback) { if (callback) callback(null,true); return true; }
87
88function zipFile(filename,contents,encoding) {
89 zipFiles[filename] = contents;
90}
91
92function finishLocal(err,result) {
93 if (argv.zip) {
94 // create archive
95 var zip = new admzip();
96
97 // add files directly
98 for (let f in zipFiles) {
99 zip.addFile(f, new Buffer(zipFiles[f]), 'Created with OpenAPI-CodeGen');
100 }
101 // write everything to disk
102 zip.writeZip(path.join(config.outputDir,configName+'.zip'));
103 }
104}
105
106function finishRemote(err,result) {
107 configName = configName.split(':').pop();
108 if (argv.verbose) console.log('Making/cleaning output directories');
109 mkdirp(path.join(config.outputDir,configName),function(){
110 rimraf(path.join(config.outputDir,configName)+'/*',function(){
111 if (argv.zip) {
112 fs.writeFileSync(path.join(config.outputDir,configName,configName+'.zip'),result);
113 }
114 else {
115 const zip = new admzip(result);
116 if (argv.verbose) {
117 console.log('Unzipping...');
118 const zipEntries = zip.getEntries(); // an array of ZipEntry records
119 zipEntries.forEach(function(zipEntry) {
120 console.log(zipEntry.entryName);
121 });
122 }
123 zip.extractAllTo(config.outputDir,true);
124 }
125 });
126 });
127}
128
129function despatch(obj, config, configName, callback) {
130 if (remoteConfig) {
131 remote.main(obj, config, configName, callback);
132 }
133 else {
134 processor.main(obj, config, configName, callback);
135 }
136}
137
138function convert20(obj){
139 if (argv.verbose) console.log('Converting OpenAPI 2.0 definition');
140 swagger2openapi.convertObj(obj,{patch:true,warnOnly:true,direct:true},function(err,openapi){
141 if (err) {
142 console.error(util.inspect(err));
143 }
144 else {
145 config.defaults.swagger = obj;
146 despatch(openapi,config,configName,finish);
147 }
148 });
149}
150
151function convert12(api){
152 if (argv.verbose) console.log('Converting Swagger 1.2 definition');
153 let options = {};
154 options.source = defName;
155 var aBase = options.source.split('/');
156 var filename = aBase.pop();
157 var extension = '';
158 if (filename.endsWith('.json')) {
159 extension = '.json';
160 }
161 else if (filename.endsWith('yaml')) {
162 extension = '.yaml';
163 }
164 else {
165 aBase.push(filename);
166 }
167 let base = aBase.join('/');
168
169 //if (options.source.endsWith('.json') || options.source.endsWith('.yaml')) {
170 // extension = '';
171 //}
172
173 var retrieve = [];
174 var apiDeclarations = [];
175 if (api.apis) {
176 for (var component of api.apis) {
177 component.path = component.path.replace('.{format}','.json');
178 var lbase = base;
179 if (component.path.startsWith('/')) {
180 let up = url.parse(base);
181 lbase = up.protocol + '//' + up.host;
182 }
183 if ((base.endsWith('/')) && (component.path.startsWith('/'))) {
184 lbase = base.substr(0,base.length-1);
185 }
186 if (component.path.indexOf('://')>=0) {
187 lbase = '';
188 }
189
190 var u = (lbase+component.path);
191 if (!u.endsWith(extension)) u += extension;
192 if (argv.verbose) console.log(u);
193 retrieve.push(fetch(u,options.fetchOptions)
194 .then(res => {
195 if (argv.verbose) console.log(res.status);
196 return res.text();
197 })
198 .then(data => {
199 apiDeclarations.push(yaml.parse(data));
200 })
201 .catch(err => {
202 console.error(util.inspect(err));
203 }));
204 }
205 }
206
207 co(function* () {
208 // resolve multiple promises in parallel
209 var res = yield retrieve;
210 var sVersion = 'v1_2';
211 stools.specs[sVersion].convert(api,apiDeclarations,true,function(err,converted){
212 if (err) {
213 console.error(util.inspect(err));
214 }
215 else {
216 if (converted.info && !converted.info.version) {
217 converted.info.version = '1.0.0';
218 }
219 convert20(converted);
220 }
221 });
222 });
223}
224
225function main(s) {
226 let o = yaml.parse(s, { prettyErrors: true });
227 if (argv.verbose) console.log('Loaded definition '+defName);
228
229 if (o && o.openapi) {
230 despatch(o,config,configName,finish);
231 }
232 else {
233 if (o && o.swaggerVersion && o.swaggerVersion === '1.2') {
234 convert12(o);
235 }
236 else if (o && o.swagger && o.swagger === '2.0') {
237 if (remoteConfig) {
238 despatch(o,config,configName,finish);
239 }
240 else {
241 convert20(o);
242 }
243 }
244 else {
245 console.error('Unrecognised OpenAPI/Swagger version');
246 }
247 }
248}
249
250if (argv.verbose) {
251 config.defaults.verbose = true;
252 console.log('Loaded config '+configName);
253}
254if (argv.lint) config.defaults.lint = true;
255if (argv.debug) config.defaults.debug = true;
256if (argv.flat) config.defaults.flat = true;
257if (argv.stools) config.defaults.stools = true;
258if (argv.zip) {
259 processor.fileFunctions.createFile = zipFile;
260 processor.fileFunctions.rimraf = nop;
261 processor.fileFunctions.mkdirp = nop;
262 processor.fileFunctions.mkdirp.sync = nop;
263}
264config.defaults.source = defName;
265
266let up = url.parse(defName);
267if (up.protocol && up.protocol.startsWith('http')) {
268 fetch(defName)
269 .then(function (res) {
270 return res.text();
271 }).then(function (body) {
272 main(body);
273 }).catch(function (err) {
274 console.error(err.message);
275 });
276}
277else {
278 let s = fs.readFileSync(defName,'utf8');
279 main(s);
280}