UNPKG

44.5 kBJavaScriptView Raw
1// @ts-check
2'use strict';
3
4const util = require('util');
5const url = require('url');
6
7const yaml = require('yaml');
8const uuidv4 = require('uuid/v4');
9const safeJson = require('safe-json-stringify');
10const Case = require('case');
11const stools = require('swagger-tools');
12const sampler = require('openapi-sampler');
13const deref = require('reftools/lib/dereference.js').dereference;
14const clone = require('reftools/lib/clone.js').circularClone;
15const walkSchema = require('oas-schema-walker').walkSchema;
16const wsGetState = require('oas-schema-walker').getDefaultState;
17const validator = require('oas-validator').validateSync;
18const downconverter = require('./lib/orange/downconvert.js');
19
20const schemaProperties = [
21 'format',
22 'minimum',
23 'maximum',
24 'exclusiveMinimum',
25 'exclusiveMaximum',
26 'minLength',
27 'maxLength',
28 'multipleOf',
29 'minItems',
30 'maxItems',
31 'uniqueItems',
32 'minProperties',
33 'maxProperties',
34 'additionalProperties',
35 'pattern',
36 'enum',
37 'default'
38];
39
40// used by helper functions / convertToArray's toString method
41let arrayMode = 'length';
42let thisFunc = encodeURIComponent;
43
44function safeSample(schema,options,api) {
45 try {
46 return sampler.sample(schema,options,api);
47 }
48 catch (ex) {
49 console.warn('Sampler:',ex.message);
50 }
51 return {};
52}
53
54function convertArray(arr) {
55 if (!arr) arr = [];
56 if (arr.length) {
57 arr.isEmpty = false;
58 for (let i=0;i<arr.length;i++) {
59 arr[i]['-first'] = (i === 0);
60 arr[i]['-last'] = (i === arr.length-1);
61 arr[i].hasMore = (i<arr.length-1);
62 }
63 }
64 else arr.isEmpty = true;
65 arr.toString = function() { if (arrayMode === 'length') return this.length.toString() };
66 return arr;
67}
68
69function getAuthData(secSchemes,api) {
70 let result = {};
71 result.hasAuthMethods = (secSchemes && secSchemes.length>0);
72 result.authMethods = [];
73 if (result.hasAuthMethods) {
74 for (let ss of secSchemes) {
75 for (let s in ss) {
76 let scheme = api.components.securitySchemes[s];
77 let entry = {};
78 entry.name = s;
79 entry.isApiKey = false;
80 entry.isBasic = false;
81 entry.isOAuth = false;
82 if (scheme.type === 'http') {
83 entry.isBasic = true;
84 }
85 else if (scheme.type === 'oauth2') {
86 entry.isOAuth = true;
87 if (scheme.flows) {
88 entry.flow = Object.keys(scheme.flows)[0];
89 let flow = Object.values(scheme.flows)[0];
90 entry.authorizationUrl = flow.authorizationUrl;
91 entry.tokenUrl = flow.tokenUrl;
92 entry.scopes = [];
93 if (flow.scopes) {
94 for (let scope in flow.scopes) {
95 let sc = {};
96 sc.scope = scope;
97 entry.scopes.push(sc);
98 }
99 }
100 // override scopes with local subset
101 if (Array.isArray(ss[s])) {
102 let newScopes = [];
103 for (let scope of entry.scopes) {
104 if (ss[s].indexOf(scope.scope)>=0) {
105 newScopes.push(scope);
106 }
107 }
108 entry.scopes = newScopes;
109 }
110 entry.scopes = convertArray(entry.scopes);
111 }
112 }
113 else if (scheme.type == 'apiKey') {
114 entry.isApiKey = true;
115 entry.keyParamName = scheme.name;
116 entry.isKeyInQuery = (scheme.in === 'query');
117 entry.isKeyInHeader = (scheme.in === 'header');
118 entry.isKeyInCookie = (scheme.in === 'cookie'); // extension
119 }
120 else {
121 entry.openapi = {};
122 entry.openapi.scheme = scheme;
123 }
124 result.authMethods.push(entry);
125 }
126 }
127 result.authMethods = convertArray(result.authMethods);
128 }
129 return result;
130}
131
132function specificationExtensions(obj) {
133 let result = {};
134 for (let k in obj) {
135 if (k.startsWith('x-')) result[k] = obj[k];
136 }
137 return result;
138}
139
140function convertOperation(op,verb,path,pathItem,obj,api) {
141 let operation = {};
142 operation.httpMethod = verb.toUpperCase();
143 if (obj.httpMethodCase === 'original') operation.httpMethod = verb; // extension
144 operation.path = path;
145 operation.replacedPathName = path; //?
146
147 operation.description = op.description;
148 operation.summary = op.summary;
149 operation.allParams = [];
150 operation.pathParams = [];
151 operation.queryParams = [];
152 operation.headerParams = [];
153 operation.formParams = [];
154 operation.summary = op.summary;
155 operation.notes = op.description;
156 if (!operation.notes) {
157 operation.notes = {isEmpty:true};
158 operation.notes.toString = function() { return '' };
159 }
160 //operation.hasMore = true; // last one gets reset to false
161 operation.isResponseBinary = false; //TODO
162 operation.isResponseFile = false; //TODO
163 operation.baseName = 'Default';
164 if (op.tags && op.tags.length) {
165 operation.baseName = op.tags[0];
166 }
167 operation.produces = [];
168 operation.consumes = [];
169 operation.hasParams = false;
170 operation.hasOptionalParams = false;
171 operation.hasRequiredParams = false;
172 operation.hasQueryParams = false;
173 operation.hasFormParams = false;
174 operation.hasPathParams = false;
175 operation.hasHeaderParams = false;
176 operation.hasBodyParam = false;
177 operation.openapi = {};
178
179 let authData = getAuthData(op.security||api.security,api);
180 operation = Object.assign(operation,authData);
181
182 let effParameters = (op.parameters||[]).concat(pathItem.parameters||[]);
183 effParameters = effParameters.filter((param, index, self) => self.findIndex((p) => {return p.name === param.name && p.in === param.in; }) === index);
184
185 const paramList = [];
186 for (let pa in effParameters) {
187 operation.hasParams = true;
188 let param = effParameters[pa];
189 let parameter = {};
190 parameter.isHeaderParam = false;
191 parameter.isQueryParam = false;
192 parameter.isPathParam = false;
193 parameter.isBodyParam = false;
194 parameter.isFormParam = false;
195 parameter.paramName = param.name;
196 parameter.baseName = param.name;
197 paramList.push(param.name);
198 parameter.required = param.required||false;
199 parameter.optional = !parameter.required;
200 if (parameter.required) operation.hasRequiredParams = true;
201 if (!parameter.required) operation.hasOptionalParams = true;
202 parameter.dataType = typeMap(param.schema.type,parameter.required,param.schema);
203 parameter["%dataType%"] = parameter.dataType; // bug in typescript-fetch template? trying to use {{{ with different delimiters
204 for (let p of schemaProperties) {
205 if (typeof param.schema[p] !== 'undefined') parameter[p] = param.schema[p];
206 }
207 parameter.example = JSON.stringify(safeSample(param.schema,{},api));
208 parameter.isBoolean = (param.schema.type === 'boolean');
209 parameter.isPrimitiveType = (!param.schema["x-oldref"]);
210 parameter.dataFormat = param.schema.format;
211 parameter.isDate = (parameter.dataFormat == 'date');
212 parameter.isDateTime = (parameter.dataFormat == 'date-time');
213 parameter.description = param.description||'';
214 parameter.unescapedDescription = param.description;
215 parameter.defaultValue = (param.schema && typeof param.schema.default !== 'undefined') ? param.schema.default : undefined;
216 parameter.isFile = false;
217 parameter.isEnum = false; // TODO?
218 parameter.vendorExtensions = specificationExtensions(param);
219 if (param.schema && param.schema.nullable) {
220 parameter.vendorExtensions["x-nullable"] = true;
221 }
222 if (param.style === 'form') {
223 if (param.explode) {
224 parameter.collectionFormat = 'multi';
225 }
226 else {
227 parameter.collectionFormat = 'csv';
228 }
229 }
230 else if (param.style === 'simple') {
231 parameter.collectionFormat = 'csv';
232 }
233 else if (param.style === 'spaceDelimited') {
234 parameter.collectionFormat = 'ssv';
235 }
236 else if (param.style === 'pipeDelimited') {
237 parameter.collectionFormat = 'pipes';
238 }
239 if ((param["x-collectionFormat"] === 'tsv') || (param["x-tabDelimited"])) {
240 parameter.collectionFormat = 'tsv';
241 }
242
243 operation.allParams.push(parameter);
244 if (param.in === 'path') {
245 parameter.isPathParam = true;
246 operation.pathParams.push(clone(parameter));
247 operation.hasPathParams = true;
248 }
249 if (param.in === 'query') {
250 parameter.isQueryParam = true;
251 operation.queryParams.push(clone(parameter));
252 operation.hasQueryParams = true;
253 }
254 if (param.in === 'header') {
255 parameter.isHeaderParam = true;
256 operation.headerParams.push(clone(parameter));
257 operation.hasHeaderParams = true;
258 }
259 /* if (param.in === 'form') { // TODO need to do this in requestBody
260 parameter.isFormParam = true;
261 operation.formParams.push(clone(parameter));
262 operation.hasFormParams = true;
263 }*/
264 } // end of effective parameters
265
266 operation.operationId = op.operationId || Case.camel((op.tags ? op.tags[0] : '') + (paramList ? '_' + paramList.join('_') + '_' : '') + verb);
267 operation.operationIdLowerCase = operation.operationId.toLowerCase();
268 operation.operationIdSnakeCase = Case.snake(operation.operationId);
269 operation.nickname = operation.operationId;
270
271 operation.bodyParams = [];
272 if (op.requestBody) {
273 operation.openapi.requestBody = op.requestBody;
274 operation.hasParams = true;
275 operation.hasBodyParam = true;
276 operation.bodyParam = {};
277 operation.bodyParam.isBodyParam = true;
278 operation.bodyParam.isHeaderParam = false;
279 operation.bodyParam.isQueryParam = false;
280 operation.bodyParam.isPathParam = false;
281 operation.bodyParam.isFormParam = false;
282 operation.bodyParam.isDate = false;
283 operation.bodyParam.isDateTime = false;
284 operation.bodyParam.baseName = 'body';
285 operation.bodyParam.paramName = 'body';
286 operation.bodyParam.baseType = 'object';
287 operation.bodyParam.required = op.requestBody.required||false;
288 operation.bodyParam.optional = !operation.bodyParam.required;
289 if (operation.bodyParam.required) operation.hasRequiredParams = true;
290 if (!operation.bodyParam.required) operation.hasOptionalParams = true;
291 operation.bodyParam.dataType = typeMap('object',operation.bodyParam.required,{}); // can be changed below
292 operation.bodyParam.description = op.requestBody.description||'';
293 operation.bodyParam.schema = {};
294 operation.bodyParam.isEnum = false; // TODO?
295 operation.bodyParam.vendorExtensions = specificationExtensions(op.requestBody);
296 if (op.requestBody.content) {
297 let contentType = Object.values(op.requestBody.content)[0];
298 let mt = { mediaType: Object.keys(op.requestBody.content)[0] };
299 operation.consumes.push(mt);
300 operation.hasConsumes = true;
301 let tmp = obj.consumes.find(function(e,i,a){
302 return (e.mediaType === mt.mediaType);
303 });
304 if (!tmp) {
305 obj.consumes.push(clone(mt)); // so convertArray works correctly
306 obj.hasConsumes = true;
307 }
308 operation.bodyParam.schema = contentType.schema;
309 operation.bodyParam.example = JSON.stringify(safeSample(contentType.schema,{},api));
310 for (let p in schemaProperties) {
311 if (typeof contentType.schema[p] !== 'undefined') operation.bodyParam[p] = contentType.schema[p];
312 }
313 if (contentType.schema.type) {
314 operation.bodyParam.type = contentType.schema.type;
315 operation.bodyParam.dataType = typeMap(contentType.schema.type,operation.bodyParam.required,contentType.schema); // this is the below mentioned
316 }
317 }
318 operation.bodyParam["%dataType%"] = operation.bodyParam.dataType; // bug in typescript-fetch template?
319 operation.bodyParam.jsonSchema = safeJson({schema: operation.bodyParam.schema},null,2);
320 operation.bodyParams.push(operation.bodyParam);
321 operation.bodyParam.isFile = false; // TODO
322 operation.allParams.push(clone(operation.bodyParam));
323 }
324 operation.tags = op.tags;
325 operation.imports = op.tags;
326 operation.vendorExtensions = specificationExtensions(op);
327
328 operation.responses = [];
329 for (let r in op.responses) {
330 if (!r.startsWith('x-')) {
331 let response = op.responses[r];
332 let entry = {};
333 entry.code = r;
334 entry.isDefault = (r === 'default');
335 entry.nickname = 'response'+r;
336 entry.message = response.description;
337 entry.description = response.description||'';
338 entry.simpleType = true;
339 entry.schema = {};
340 entry.jsonSchema = safeJson({ schema: entry.schema },null,2);
341 if (response.content) {
342 entry.baseType = 'object';
343 entry.dataType = typeMap(entry.baseType,false,{});
344 let contentType = Object.values(response.content)[0];
345 let mt = {};
346 mt.mediaType = Object.keys(response.content)[0];
347 operation.produces.push(mt);
348 operation.hasProduces = true;
349 let tmp = obj.produces.find(function(e,i,a){
350 return (e.mediaType === mt.mediaType);
351 });
352 if (!tmp) {
353 obj.produces.push(clone(mt)); // so convertArray works correctly
354 obj.hasProduces = true;
355 }
356 if (contentType && contentType.schema) {
357 entry.schema = contentType.schema;
358 entry.jsonSchema = safeJson({schema:entry.schema},null,2);
359 entry.baseType = contentType.schema.type;
360 entry.isPrimitiveType = true;
361 entry.dataType = typeMap(contentType.schema.type,false,entry.schema);
362 if (contentType.schema["x-oldref"]) {
363 entry.dataType = contentType.schema["x-oldref"].replace('#/components/schemas/','');
364 entry.isPrimitiveType = false;
365 }
366 }
367 if (contentType && contentType.example) {
368 entry.hasExamples = true;
369 if (!entry.examples) entry.examples = [];
370 entry.examples.push({contentType: mt.mediaType, example: JSON.stringify(contentType.example,null,2)});
371 }
372 if (contentType && contentType.examples) {
373 for (let ex in contentType.examples) {
374 const example = contentType.examples[ex];
375 if (example.value) {
376 entry.hasExamples = true;
377 if (!entry.examples) entry.examples = [];
378 entry.examples.push({contentType: mt.mediaType, example: JSON.stringify(example.value,null,2)});
379 }
380 }
381 }
382
383 if (!entry.hasExamples && entry.schema) {
384 let example = safeSample(entry.schema,{},api);
385 if (example) {
386 entry.hasExamples = true;
387 if (!entry.examples) entry.examples = [];
388 entry.examples.push({contentType: mt.mediaType, example:JSON.stringify(example,null,2)});
389 }
390 }
391
392 operation.examples = (operation.examples||[]).concat(entry.examples||[]);
393
394 operation.returnType = entry.dataType;
395 operation.returnBaseType = entry.baseType;
396 operation.returnTypeIsPrimitive = entry.isPrimitiveType;
397 operation.returnContainer = ((entry.baseType === 'object') || (entry.baseType === 'array'));
398
399 }
400 entry.responseHeaders = []; // TODO responseHeaders
401 entry.responseHeaders = convertArray(entry.responseHeaders);
402 entry.examples = convertArray(entry.examples);
403 entry.openapi = {};
404 entry.openapi.links = response.links;
405 operation.responses.push(entry);
406 operation.responses = convertArray(operation.responses);
407 }
408
409 if (obj.sortParamsByRequiredFlag) {
410 operation.allParams = operation.allParams.sort(function(a,b){
411 if (a.required && !b.required) return -1;
412 if (b.required && !a.required) return +1;
413 return 0;
414 });
415 }
416 }
417 operation.queryParams = convertArray(operation.queryParams);
418 operation.headerParams = convertArray(operation.headerParams);
419 operation.pathParams = convertArray(operation.pathParams);
420 operation.formParams = convertArray(operation.formParams);
421 operation.bodyParams = convertArray(operation.bodyParams);
422 operation.allParams = convertArray(operation.allParams);
423 operation.examples = convertArray(operation.examples);
424
425 if (operation.hasConsumes) {
426 operation.consumes = convertArray(operation.consumes);
427 }
428 else {
429 delete operation.consumes;
430 }
431 if (operation.hasProduces) {
432 operation.produces = convertArray(operation.produces);
433 }
434 else {
435 delete operation.produces;
436 }
437
438 operation.openapi.callbacks = op.callbacks;
439
440 //let container = {};
441 //container.baseName = operation.nickname;
442 //container.operation = operation;
443 //obj.operations.push(container);
444 return operation;
445}
446
447function convertToApis(source,obj,defaults) {
448 let apis = [];
449 for (let p in source.paths) {
450 for (let m in source.paths[p]) {
451 if ((m !== 'parameters') && (m !== 'summary') && (m !== 'description') && (!m.startsWith('x-'))) {
452 let op = source.paths[p][m];
453 let tagName = 'Default';
454 if (op.tags && op.tags.length > 0) {
455 tagName = op.tags[0];
456 }
457 let entry = apis.find(function(e,i,a){
458 return (e.name === tagName);
459 });
460 if (!entry) {
461 entry = {};
462 entry.name = tagName;
463 //if (defaults.language === 'typescript') {
464 // entry.classname = Case.pascal(entry.name);
465 //}
466 //else {
467 entry.classname = tagName+'Api';
468 //}
469 entry.classFilename = tagName+'Api';
470 entry.classVarName = tagName; // see issue #21
471 entry.packageName = obj.packageName; //! this may not be enough / sustainable. Or many props at wrong level :(
472 entry.operations = {};
473 entry.operations.operation = [];
474 apis.push(entry);
475 }
476 let operation = convertOperation(op,m,p,source.paths[p],obj,source);
477 entry.operations.operation.push(operation);
478 }
479 }
480 }
481 for (let t in source.tags) {
482 let tag = source.tags[t];
483 let entry = apis.find(function(e,i,a){
484 return (e.name === t);
485 });
486 if (entry) {
487 entry.classname = tag.name+'Api';
488 entry.description = tag.description;
489 entry.externalDocs = tag.externalDocs;
490 }
491 }
492 for (let api of apis) {
493 api.operations.operation = convertArray(api.operations.operation);
494 }
495 apis = convertArray(apis);
496 return apis;
497}
498
499function convertToPaths(source,obj,defaults) {
500 let paths = [];
501 for (let p in source.paths) {
502 for (let m in source.paths[p]) {
503 if ((m !== 'parameters') && (m !== 'summary') && (m !== 'description') && (!m.startsWith('x-'))) {
504 let op = source.paths[p][m];
505 let tagName = 'Default';
506 if (op.tags && op.tags.length > 0) {
507 tagName = op.tags[0];
508 }
509 let entry = paths.find(function(e,i,a){
510 return (e.name === p);
511 });
512 if (!entry) {
513 const split = p.replace(/^\//,'').split(/\//g);
514 const dirname = split.slice(0,-1).join('/');
515 const filename = split.slice(-1).join('');
516 // Generates class name from path using the rules
517 // * the slashes are stripped out
518 // * each path part is capitalised
519 // * each parameter is changed to By<param>
520 // i.e.
521 // /users => Users
522 // /users/{id} => UsersById
523 // /users/{id}/delete => UsersByIdDelete
524 const className = split.map(v=>v.replace(/{([^}]+)}/g,(v,v1)=>`By${v1[0].toUpperCase()}${v1.slice(1)}`).replace(/^./,(v)=>`${v[0].toUpperCase()}${v.slice(1)}`)).join('');
525
526 entry = {};
527 entry.name = p;
528 //if (defaults.language === 'typescript') {
529 // entry.classname = Case.pascal(entry.name);
530 //}
531 //else {
532 entry.classname = className+'Api';
533 //}
534 entry.classDirName = dirname;
535 entry.classFilename = filename;
536 entry.classVarName = tagName; // see issue #21
537 entry.packageName = obj.packageName; //! this may not be enough / sustainable. Or many props at wrong level :(
538 entry.operations = {};
539 entry.operations.operation = [];
540 paths.push(entry);
541 }
542 let operation = convertOperation(op,m,p,source.paths[p],obj,source);
543 entry.operations.operation.push(operation);
544 }
545 }
546 }
547 for (let t in source.tags) {
548 let tag = source.tags[t];
549 let entry = paths.find(function(e,i,a){
550 return (e.name === t);
551 });
552 if (entry) {
553 entry.classname = tag.name+'Api';
554 entry.description = tag.description;
555 entry.externalDocs = tag.externalDocs;
556 }
557 }
558 for (let path of paths) {
559 path.operations.operation = convertArray(path.operations.operation);
560 }
561 paths = convertArray(paths);
562 return paths;
563}
564
565// TODO add html and possibly termcap (https://www.npmjs.com/package/hermit) renderers
566const markdownPPs = {
567 nop: function(markdown) {
568 return markdown;
569 }
570};
571
572const typeMaps = {
573 nop: function(type,required,schema) {
574 return type;
575 },
576 java: function(type,required,schema) {
577 let result = type;
578 if (!required) result += '?';
579 return result;
580 },
581 javascript: function(type,required,schema) {
582 let result = type;
583 if (result === 'integer') result = 'number';
584 return result;
585 },
586 typescript: function(type,required,schema) {
587 let result = type;
588 if (result === 'integer') result = 'number';
589 if (result === 'array') {
590 result = 'Array';
591 if (schema.items && schema.items.type) {
592 result += '<'+typeMap(schema.items.type,false,schema.items)+'>';
593 }
594 }
595 return result;
596 },
597 go: function(type,required,schema) {
598 let result = type;
599 if (result === 'integer') result = 'int';
600 if (result === 'boolean') result = 'bool';
601 if (result === 'object') result = 'struct{}';
602 if (result === 'array') {
603 result = '[100]'; //!
604 if (schema.items && schema.items.type) {
605 result += typeMap(schema.items.type,false,schema.items);
606 }
607 }
608 return result;
609 }
610};
611
612const reservedWords = {
613 nop: [],
614 go: [ 'type' ]
615};
616
617let typeMap = typeMaps.nop;
618let markdownPP = markdownPPs.nop;
619let reserved = reservedWords.nop;
620
621function getBase() {
622 let base = {};
623 base.supportingFiles = [];
624 base.modelTests = [];
625 base.modelDocs = [];
626 base.apiTests = [];
627 base.apiDocs = [];
628 base.allowUnicodeIdentifiers = false; /* boolean, toggles whether unicode identifiers are allowed in names or not, default is false */
629 //base.developerName = x; /* developer name in generated pom.xml */
630 //base.developerEmail = x; /* developer email in generated pom.xml */
631 //base.developerOrganization = x; /* developer organization in generated pom.xml */
632 //base.developerOrganizationUrl = x; /* developer organization URL in generated pom.xml */
633 base.gitUserId = 'Mermade'; /* Git user ID, e.g. swagger-api. */
634 base.gitRepoId = 'openapi-codegen'; /* Git repo ID, e.g. swagger-codegen. */
635 base.licenseName = 'Unlicense'; /* The name of the license */
636 base.projectLicenseName = 'Unlicense'; /* The name of the license */
637 base.licenseUrl = 'https://unlicense.org'; /* The URL of the license */
638 base.projectLicenseUrl = 'https://unlicense.org'; /* The URL of the license */
639 base.projectUrl = 'https://github.com/Mermade/openapi-codegen';
640 base.localVariablePrefix = ''; /* prefix for generated code members and local variables */
641 base.serializableModel = true; /* boolean - toggle "implements Serializable" for generated models */
642 base.bigDecimalAsString = false; /* Treat BigDecimal values as Strings to avoid precision loss. */
643 base.sortParamsByRequiredFlag = true; /* Sort method arguments to place required parameters before optional parameters. */
644 base.useDateTimeOffset = 0; /* Use DateTimeOffset to model date-time properties */
645 base.ensureUniqueParams = false; /* Whether to ensure parameter names are unique in an operation (rename parameters that are not). */
646 base.optionalMethodArgument = false; /* Optional method argument, e.g. void square(int x=10) (.net 4.0+ only). */
647 base.optionalAssemblyInfo = false; /* Generate AssemblyInfo.cs. */
648 base.netCoreProjectFile = true; /* Use the new format (.NET Core) for .NET project files (.csproj). */
649 base.useCollection = false; /* Deserialize array types to Collection<T> instead of List<T>. */
650 base.interfacePrefix = ''; /* Prefix interfaces with a community standard or widely accepted prefix. */
651 base.returnICollection = false; /* Return ICollection<T> instead of the concrete type. */
652 base.optionalProjectFile = false; /* Generate {PackageName}.csproj. */
653 base.variableNamingConvention = 'original'; /* {camelCase, PascalCase, snake_case, original, UPPERCASE} */
654 base.modelPropertyNaming = 'original'; /* {camelCase, PascalCase, snake_case, original, UPPERCASE} */
655 base.targetFramework = 4; /* The target .NET framework version. */
656 base.modelNamePrefix = ''; /* Prefix that will be prepended to all model names. Default is the empty string. */
657 base.modelNameSuffix = ''; /* Suffix that will be appended to all model names. Default is the empty string. */
658 base.releaseNote = 'Minor update'; /* Release note, default to 'Minor update'. */
659 base.supportsES6 = true; /* Generate code that conforms to ES6. */
660 base.supportsAsync = true; /* Generate code that supports async operations. */
661 base.emitJSDoc = true; /* */
662 base.emitModelMethods = true; /* */
663 base.excludeTests = false; /* Specifies that no tests are to be generated. */
664 base.generateApiDocs = true; /* Not user-configurable. System provided for use in templates. */
665 base.generateApiTests = true; /* Specifies that api tests are to be generated. */
666 base.generateModelDocs = true; /* Not user-configurable. System provided for use in templates. */
667 base.generateModelTests = true; /* Specifies that model tests are to be generated. */
668 base.hideGenerationTimestamp = false; /* Hides the generation timestamp when files are generated. */
669 base.generatePropertyChanged = true; /* Specifies that models support raising property changed events. */
670 base.nonPublicApi = false; /* Generates code with reduced access modifiers; allows embedding elsewhere without exposing non-public API calls to consumers. */
671 base.validatable = true; /* Generates self-validatable models. */
672 base.ignoreFileOverride = '.swagger-codegen-ignore'; /* Specifies an override location for the .swagger-codegen-ignore file. Most useful on initial generation. */
673 base.removeOperationIdPrefix = false; /* Remove prefix of operationId, e.g. config_getId => getId */
674 base.serverPort = 8000;
675 base.newline = '\n';
676 base.apiDocPath = '';
677 base.modelDocPath = '';
678 base.classPrefix = 'cls';
679
680 //extensions
681 base.modelNaming = 'original'; /* {camelCase, PascalCase, snake_case, original, UPPERCASE} */
682 return base;
683}
684
685function getPrime(api,defaults) {
686 let prime = {};
687 prime.classname = api.info.title.toLowerCase().split(' ').join('_').split('-').join('_');
688 prime.projectName = prime.classname;
689 prime.appVersion = api.info.version;
690 prime.apiVersion = api.info.version;
691 prime.packageVersion = api.info.version;
692 prime.projectVersion = api.info.version;
693 prime.version = api.info.version;
694 prime.title = api.info.title;
695 prime.swaggerVersion = '2.0';
696 prime.generatorVersion = require('./package.json').version;
697 prime.swaggerCodegenVersion = 'openapi-codegen-v'+prime.generatorVersion;
698 prime.appDescription = api.info.description||'No description';
699 prime.projectDescription = prime.appDescription;
700 prime.classVarName = 'default'; // see issue #21
701 prime.exportedName = prime.classname;
702 prime.packageTitle = prime.classname; /* Specifies an AssemblyTitle for the .NET Framework global assembly attributes stored in the AssemblyInfo file. */
703 prime.infoEmail = api.info.contact ? api.info.contact.email : null;
704 prime.appContact = prime.infoEmail;
705 prime.infoUrl = api.info.contact ? api.info.contact.url : null;
706 prime.licenseInfo = api.info.license ? api.info.license.name : null;
707 prime.licenseUrl = api.info.license ? api.info.license.url : null;
708 prime.appName = api.info.title;
709 prime.host = ''
710 prime.basePath = '/';
711 prime.basePathWithoutHost = '/';
712 prime.contextPath = '/';
713 prime.packageName = 'IO.OpenAPI';
714 prime.apiPackage = prime.packageName; /* package for generated api classes */
715 prime.generatorPackage = 'IO.OpenAPI';
716 prime.invokerPackage = 'IO.OpenAPI'; /* root package for generated code */
717 prime.modelPackage = 'IO.OpenAPI'; /* package for generated models */
718 prime.package = 'IO.OpenAPI.Api';
719 prime.phpInvokerPackage = prime.invokerPackage; /* root package for generated php code */
720 prime.perlModuleName = prime.invokerPackage; /* root module name for generated perl code */
721 prime.podVersion = '1.0.0';
722 prime.pythonPackageName = prime.invokerPackage; /* package name for generated python code */
723 prime.clientPackage = 'IO.OpenAPI.Client';
724 prime.importPath = 'IO.OpenAPI.Api.Default';
725 prime.hasImport = true;
726 prime.hasMore = true;
727 prime.generatedDate = new Date().toString();
728 prime.generatorClass = defaults.configName; // 'class ' prefix?
729 prime.fullyQualifiedGeneratorClass = prime.generatorPackage+'.'+prime.generatorClass;
730 prime.imports = [ { "import": "IO.OpenAPI.Model.Default" } ];
731 prime.name = prime.classname;
732 prime.classFilename = prime.classname;
733 prime.jsModuleName = prime.classname;
734 prime.moduleName = prime.classname;
735 prime.jsProjectName = prime.classname;
736 prime.baseNamespace = prime.packageName;
737 prime.sourceFolder = './out/'+defaults.configName; /* source folder for generated code */
738 prime.templateDir = './templates/'+defaults.configName;
739 prime.implFolder = prime.sourceFolder; /* folder for generated implementation code */
740 prime.library = ''; /* library template (sub-template) */
741 prime.packageGuid = uuidv4(); /* The GUID that will be associated with the C# project */
742 prime.optionalEmitDefaultValues = false; /* Set DataMember's EmitDefaultValue. */
743
744 prime.packageProductName = prime.projectName; /* Specifies an AssemblyProduct for the .NET Framework global assembly attributes stored in the AssemblyInfo file. */
745 prime.packageCompany = 'Smartbear Software'; /* Specifies an AssemblyCompany for the .NET Framework global assembly attributes stored in the AssemblyInfo file. */
746 prime.packageAuthors = 'Swagger-Codegen authors'; /* Specifies Authors property in the .NET Core project file. */
747 prime.packageCopyright = 'Copyright 2016 Smartbear Software'; /* Specifies an AssemblyCopyright for the .NET Framework global assembly attributes stored in the AssemblyInfo file. */
748
749// prime.groupId = x; /* groupId in generated pom.xml */
750// prime.artifactId = x; /* artifactId in generated pom.xml */
751// prime.artifactVersion = x; /* artifact version in generated pom.xml */
752// prime.artifactUrl = x; /* artifact URL in generated pom.xml */
753// prime.scmConnection = x; /* SCM connection in generated pom.xml */
754// prime.scmDeveloperConnection = x; /* SCM developer connection in generated pom.xml */
755// prime.scmUrl = x; /* SCM URL in generated pom.xml */
756
757 prime.httpUserAgent = 'OpenAPI-Codegen/'+prime.packageVersion+'/'+defaults.configName; /* HTTP user agent, e.g. codegen_csharp_api_client, default to 'Swagger-Codegen/{packageVersion}}/{language}' */
758 return prime;
759}
760
761function transform(api, defaults, callback) {
762 let base = getBase(); // defaults which are hard-coded
763
764 let lang = (defaults.language||'').toLowerCase();
765 if (typeMaps[lang]) typeMap = typeMaps[lang];
766 if (reservedWords[lang]) reserved = reservedWords[lang];
767
768 let prime = getPrime(api,defaults); // defaults which depend in some way on the api definition
769 let obj = Object.assign({},base,prime,defaults);
770
771 if (defaults.swagger) {
772 obj.swagger = defaults.swagger;
773 }
774 else {
775 const container = {};
776 container.spec = api;
777 container.source = defaults.source;
778 let conv = new downconverter(container);
779 obj.swagger = conv.convert();
780 }
781
782 obj["swagger-yaml"] = yaml.stringify(obj.swagger); // set to original if converted v2.0
783 obj["swagger-json"] = JSON.stringify(obj.swagger, null, 2); // set to original if converted 2.0
784 obj["openapi-yaml"] = yaml.stringify(api);
785 obj["openapi-json"] = JSON.stringify(api, null, 2);
786
787 // openapi3 extensions
788 obj.openapi = {};
789 obj.openapi.operationCounter = 1;
790 obj.openapi.version = api.openapi;
791 obj.openapi.servers = api.servers;
792
793 // helper functions (seen in erlang-client)
794 obj.qsEncode = function() {
795 thisFunc = encodeURIComponent;
796 return function(template,context){
797 console.warn(util.inspect(template));
798 console.warn(util.inspect(this));
799 };
800 };
801 obj.this = function() {
802 console.warn('this called');
803 return thisFunc(this.paramName);
804 };
805 obj.length = function() {
806 arrayMode = 'length';
807 return true;
808 };
809 obj.capitalize = function() {
810 //? seen in akka-scala
811 return true;
812 };
813
814 let allSecurity = [];
815 if (api.components && api.components.securitySchemes) {
816 for (let s in api.components.securitySchemes) {
817 let entry = {};
818 entry[s] = api.components.securitySchemes[s];
819 allSecurity.push(entry);
820 }
821 }
822 let authData = getAuthData(allSecurity,api);
823 obj = Object.assign(obj,authData);
824
825 api = deref(api,api,{$ref:'x-oldref'});
826
827 obj.messages = [];
828 let message = {};
829 let vOptions = {lint:defaults.lint};
830 if (defaults.stools && defaults.swagger) {
831 stools.specs.v2_0.validate(defaults.swagger,function(err,result){
832 if (err) console.error(util.inspect(err));
833 if (result.errors) {
834 for (let e of result.errors) {
835 let message = {};
836 message.level = 'Error';
837 message.elementType = 'Path';
838 message.message = e.message;
839 message.elementId = e.path.join('/');
840 obj.messages.push(message);
841 if (defaults.verbose) console.log(message);
842 }
843 for (let w of result.warnings) {
844 let message = {};
845 message.level = 'Warning';
846 message.elementType = 'Path';
847 message.message = w.message;
848 message.elementId = w.path.join('/');
849 obj.messages.push(message);
850 if (defaults.verbose) console.log(message);
851 }
852 }
853 });
854 }
855 else try {
856 validator(api,vOptions);
857 message.level = 'Valid';
858 message.elementType = 'Context';
859 message.elementId = 'None';
860 message.message = 'No validation errors detected';
861 obj.messages.push(message);
862 if (defaults.verbose) console.log(message);
863 }
864 catch (ex) {
865 message.level = 'Error';
866 message.elementType = 'Context';
867 message.elementId = vOptions.context.pop();
868 message.message = ex.message;
869 obj.messages.push(message);
870 console.error(message);
871 }
872
873 if (api.servers && api.servers.length) {
874 let u = api.servers[0].url;
875 let up = url.parse(u);
876 obj.host = up.host;
877 obj.basePath = up.path;
878 obj.basePathWithoutHost = up.path;
879 }
880
881 obj.consumes = [];
882 obj.produces = [];
883
884 obj.apiInfo = {};
885 obj.apiInfo.apis = convertToApis(api,obj,defaults);
886 obj.apiInfo.paths = convertToPaths(api,obj,defaults);
887
888 obj.produces = convertArray(obj.produces);
889 obj.consumes = convertArray(obj.consumes);
890
891 if (defaults.debug) obj.debugOperations = JSON.stringify(obj,null,2);
892
893 obj.models = [];
894 if (api.components) {
895 for (let s in api.components.schemas) {
896 let schema = api.components.schemas[s];
897 if (schema !== null) {
898 let container = {};
899 let model = {};
900 model.name = s;
901 if (obj.modelNaming === 'snake_case') {
902 model.name = Case.snake(model.name);
903 }
904 model.classname = model.name;
905 model.classVarName = s;
906 model.modelJson = safeJson(schema,null,2);
907 model.title = schema.title;
908 model.unescapedDescription = schema.description;
909 model.classFilename = obj.classPrefix+model.name;
910 model.modelPackage = model.name;
911 model.hasEnums = false;
912 model.vars = [];
913 walkSchema(schema,{},wsGetState,function(schema,parent,state){
914 let entry = {};
915 entry.name = schema.name || schema.title;
916 if (!entry.name && state.property && (state.property.startsWith('properties') ||
917 state.property.startsWith('additionalProperties'))) {
918 entry.name = state.property.split('/')[1];
919 }
920
921 if (entry.name) {
922 entry.baseName = entry.name.toLowerCase();
923 }
924
925 if (obj.variableNamingConvention === 'original') {
926 if (obj.modelPropertyNaming === 'snake_case') {
927 entry.name = Case.snake(entry.name);
928 }
929 } else {
930 if (obj.variableNamingConvention === 'snake_case') {
931 entry.baseName = entry.name;
932 entry.name = Case.snake(entry.name);
933 }
934 }
935
936 if (reserved.indexOf(entry.name)>=0) {
937 entry.name = Case.pascal(entry.name);
938 }
939
940 entry.getter = Case.camel('get_'+entry.name);
941 entry.setter = Case.camel('set_'+entry.name);
942 entry.description = schema.description||'';
943 entry.unescapedDescription = entry.description;
944 if (entry.name && !schema.type && schema["x-oldref"]) {
945 entry.type = obj.modelPackage + '\\' + schema["x-oldref"].replace('#/components/schemas/', '');
946 } else {
947 entry.type = schema.type;
948 }
949 entry.required = (parent.required && parent.required.indexOf(entry.name)>=0)||false;
950 entry.isNotRequired = !entry.required;
951 entry.readOnly = !!schema.readOnly;
952 entry.type = typeMap(entry.type,entry.required,schema);
953 entry.dataType = entry.type; //camelCase for imported files
954 entry.datatype = entry.type; //lower for other files
955 entry.jsonSchema = safeJson(schema,null,2);
956 for (let p in schemaProperties) {
957 if (typeof schema[p] !== 'undefined') entry[p] = schema[p];
958 }
959 entry.isEnum = !!schema.enum;
960 entry.isListContainer = schema.type === 'array';
961 entry.isMapContainer = schema.type === 'object';
962 entry.isPrimitiveType = !entry.isListContainer && !entry.isMapContainer;
963 entry.isNotContainer = entry.isPrimitiveType;
964 if (entry.isEnum) entry.isNotContainer = false;
965 entry.isContainer = !entry.isNotContainer;
966 if ((schema.type === 'object') && schema.properties && schema.properties["x-oldref"]) {
967 entry.complexType = schema.properties["x-oldref"].replace('#/components/schemas/','');
968 }
969 if ((schema.type === 'array') && schema.items && schema.items["x-oldref"]) {
970 entry.itemsComplexType = schema.items["x-oldref"].replace('#/components/schemas/','');
971 }
972 entry.dataFormat = schema.format;
973 entry.defaultValue = schema.default;
974
975 if (entry.isEnum) {
976 model.allowableValues = {};
977 model.allowableValues.enumVars = [];
978 model["allowableValues.values"] = schema.enum;
979 model.allowableValues.values = schema.enum;
980 for (let v of schema.enum) {
981 let e = { name: v, nameInCamelCase: Case.camel(v), value: '"'+v+'"' }; // insane, why aren't the quotes in the template?
982 model.allowableValues.enumVars.push(e);
983 }
984 model.allowableValues.enumVars = convertArray(model.allowableValues.enumVars);
985 }
986
987 if (entry.name && state.depth<=1) {
988 entry.nameInCamelCase = Case.pascal(entry.name); // for erlang-client
989 entry.datatypeWithEnum = s+'.'+entry.name+'Enum';
990 entry.enumName = entry.name+'Enum';
991 model.hasEnums = true;
992 model.vars.push(entry);
993 }
994 });
995 model.vars = convertArray(model.vars);
996 container.model = model;
997 container.importPath = model.name;
998 obj.models.push(container);
999 }
1000 }
1001 }
1002
1003 if (obj.models.length === 0) {
1004 obj.models = { isEmpty: true };
1005 }
1006 else {
1007 Object.defineProperty(obj.models, 'isEmpty', {
1008 enumerable: true,
1009 value: false
1010 });
1011 }
1012
1013 obj.orderedModels = {};
1014 Object.keys(obj.models).sort().forEach(function(key) {
1015 obj.orderedModels[key] = obj.models[key];
1016 });
1017
1018 if (defaults.debug) obj.debugModels = JSON.stringify(obj.models,null,2);
1019
1020 if (callback) callback(null,obj);
1021 return obj;
1022}
1023
1024module.exports = {
1025 getBase : getBase,
1026 getPrime : getPrime,
1027 transform : transform
1028};
1029