UNPKG

openapi-codegen

Version:
923 lines (862 loc) 39.9 kB
// @ts-check 'use strict'; const util = require('util'); const url = require('url'); const yaml = require('js-yaml'); const uuidv4 = require('uuid/v4'); const safeJson = require('safe-json-stringify'); const Case = require('case'); const stools = require('swagger-tools'); const sampler = require('openapi-sampler'); const deref = require('reftools/lib/dereference.js').dereference; const clone = require('reftools/lib/clone.js').clone; const walkSchema = require('oas-schema-walker').walkSchema; const wsGetState = require('oas-schema-walker').getDefaultState; const validator = require('oas-validator').validateSync; const downconverter = require('./lib/orange/downconvert.js'); const schemaProperties = [ 'format', 'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'minLength', 'maxLength', 'multipleOf', 'minItems', 'maxItems', 'uniqueItems', 'minProperties', 'maxProperties', 'additionalProperties', 'pattern', 'enum', 'default' ]; // used by helper functions / convertToArray's toString method let arrayMode = 'length'; let thisFunc = encodeURIComponent; function convertArray(arr) { if (!arr) arr = []; if (arr.length) { arr.isEmpty = false; for (let i=0;i<arr.length;i++) { arr[i]['-first'] = (i === 0); arr[i]['-last'] = (i === arr.length-1); arr[i].hasMore = (i<arr.length-1); } } else arr.isEmpty = true; arr.toString = function() { if (arrayMode === 'length') return this.length.toString() }; return arr; } function getAuthData(secSchemes,api) { let result = {}; result.hasAuthMethods = (secSchemes && secSchemes.length>0); result.authMethods = []; if (result.hasAuthMethods) { for (let ss of secSchemes) { for (let s in ss) { let scheme = api.components.securitySchemes[s]; let entry = {}; entry.name = s; entry.isApiKey = false; entry.isBasic = false; entry.isOAuth = false; if (scheme.type === 'http') { entry.isBasic = true; } else if (scheme.type === 'oauth2') { entry.isOAuth = true; if (scheme.flows) { entry.flow = Object.keys(scheme.flows)[0]; let flow = Object.values(scheme.flows)[0]; entry.authorizationUrl = flow.authorizationUrl; entry.tokenUrl = flow.tokenUrl; entry.scopes = []; if (flow.scopes) { for (let scope in flow.scopes) { let sc = {}; sc.scope = scope; entry.scopes.push(sc); } } // override scopes with local subset if (Array.isArray(ss[s])) { let newScopes = []; for (let scope of entry.scopes) { if (ss[s].indexOf(scope.scope)>=0) { newScopes.push(scope); } } entry.scopes = newScopes; } entry.scopes = convertArray(entry.scopes); } } else if (scheme.type == 'apiKey') { entry.isApiKey = true; entry.keyParamName = scheme.name; entry.isKeyInQuery = (scheme.in === 'query'); entry.isKeyInHeader = (scheme.in === 'header'); entry.isKeyInCookie = (scheme.in === 'cookie'); // extension } else { entry.openapi = {}; entry.openapi.scheme = scheme; } result.authMethods.push(entry); } } result.authMethods = convertArray(result.authMethods); } return result; } function specificationExtensions(obj) { let result = {}; for (let k in obj) { if (k.startsWith('x-')) result[k] = obj[k]; } return result; } function convertOperation(op,verb,path,pathItem,obj,api) { let operation = {}; operation.httpMethod = verb.toUpperCase(); if (obj.httpMethodCase === 'original') operation.httpMethod = verb; // extension operation.path = path; operation.replacedPathName = path; //? operation.operationId = op.operationId||('operation'+obj.openapi.operationCounter++); operation.operationIdLowerCase = operation.operationId.toLowerCase(); operation.operationIdSnakeCase = Case.snake(operation.operationId); operation.nickname = operation.operationId; //operation.classname = obj.classPrefix+operation.nickname; operation.description = op.description; operation.summary = op.summary; operation.allParams = []; operation.pathParams = []; operation.queryParams = []; operation.headerParams = []; operation.formParams = []; operation.summary = op.summary; operation.notes = op.description; if (!operation.notes) { operation.notes = {isEmpty:true}; operation.notes.toString = function() { return '' }; } //operation.hasMore = true; // last one gets reset to false operation.isResponseBinary = false; //TODO operation.isResponseFile = false; //TODO operation.baseName = 'Default'; if (op.tags && op.tags.length) { operation.baseName = op.tags[0]; } operation.produces = []; operation.consumes = []; operation.hasParams = false; operation.hasOptionalParams = false; operation.hasRequiredParams = false; operation.hasQueryParams = false; operation.hasFormParams = false; operation.hasPathParams = false; operation.hasHeaderParams = false; operation.hasBodyParam = false; operation.openapi = {}; let authData = getAuthData(op.security||api.security,api); operation = Object.assign(operation,authData); let effParameters = (op.parameters||[]).concat(pathItem.parameters||[]); effParameters = effParameters.filter((param, index, self) => self.findIndex((p) => {return p.name === param.name && p.in === param.in; }) === index); for (let pa in effParameters) { operation.hasParams = true; let param = effParameters[pa]; let parameter = {}; parameter.isHeaderParam = false; parameter.isQueryParam = false; parameter.isPathParam = false; parameter.isBodyParam = false; parameter.isFormParam = false; parameter.paramName = param.name; parameter.baseName = param.name; parameter.required = param.required||false; parameter.optional = !parameter.required; if (parameter.required) operation.hasRequiredParams = true; if (!parameter.required) operation.hasOptionalParams = true; parameter.dataType = typeMap(param.schema.type,parameter.required,param.schema); parameter["%dataType%"] = parameter.dataType; // bug in typescript-fetch template? trying to use {{{ with different delimiters for (let p of schemaProperties) { if (typeof param.schema[p] !== 'undefined') parameter[p] = param.schema[p]; } parameter.example = JSON.stringify(sampler.sample(param.schema,{},api)); parameter.isBoolean = (param.schema.type === 'boolean'); parameter.isPrimitiveType = (!param.schema["x-oldref"]); parameter.dataFormat = param.schema.format; parameter.isDate = (parameter.dataFormat == 'date'); parameter.isDateTime = (parameter.dataFormat == 'date-time'); parameter.description = param.description||''; parameter.unescapedDescription = param.description; parameter.defaultValue = (param.schema && typeof param.schema.default !== 'undefined') ? param.schema.default : undefined; parameter.isFile = false; parameter.isEnum = false; // TODO? parameter.vendorExtensions = specificationExtensions(param); if (param.schema && param.schema.nullable) { parameter.vendorExtensions["x-nullable"] = true; } if (param.style === 'form') { if (param.explode) { parameter.collectionFormat = 'multi'; } else { parameter.collectionFormat = 'csv'; } } else if (param.style === 'simple') { parameter.collectionFormat = 'csv'; } else if (param.style === 'spaceDelimited') { parameter.collectionFormat = 'ssv'; } else if (param.style === 'pipeDelimited') { parameter.collectionFormat = 'pipes'; } if ((param["x-collectionFormat"] === 'tsv') || (param["x-tabDelimited"])) { parameter.collectionFormat = 'tsv'; } operation.allParams.push(parameter); if (param.in === 'path') { parameter.isPathParam = true; operation.pathParams.push(clone(parameter)); operation.hasPathParams = true; } if (param.in === 'query') { parameter.isQueryParam = true; operation.queryParams.push(clone(parameter)); operation.hasQueryParams = true; } if (param.in === 'header') { parameter.isHeaderParam = true; operation.headerParams.push(clone(parameter)); operation.hasHeaderParams = true; } /* if (param.in === 'form') { // TODO need to do this in requestBody parameter.isFormParam = true; operation.formParams.push(clone(parameter)); operation.hasFormParams = true; }*/ } // end of effective parameters operation.bodyParams = []; if (op.requestBody) { operation.openapi.requestBody = op.requestBody; operation.hasParams = true; operation.hasBodyParam = true; operation.bodyParam = {}; operation.bodyParam.isBodyParam = true; operation.bodyParam.isHeaderParam = false; operation.bodyParam.isQueryParam = false; operation.bodyParam.isPathParam = false; operation.bodyParam.isFormParam = false; operation.bodyParam.isDate = false; operation.bodyParam.isDateTime = false; operation.bodyParam.baseName = 'body'; operation.bodyParam.paramName = 'body'; operation.bodyParam.baseType = 'object'; operation.bodyParam.required = op.requestBody.required||false; operation.bodyParam.optional = !operation.bodyParam.required; if (operation.bodyParam.required) operation.hasRequiredParams = true; if (!operation.bodyParam.required) operation.hasOptionalParams = true; operation.bodyParam.dataType = typeMap('object',operation.bodyParam.required,{}); // can be changed below operation.bodyParam.description = op.requestBody.description||''; operation.bodyParam.schema = {}; operation.bodyParam.isEnum = false; // TODO? operation.bodyParam.vendorExtensions = specificationExtensions(op.requestBody); if (op.requestBody.content) { let contentType = Object.values(op.requestBody.content)[0]; let mt = { mediaType: Object.keys(op.requestBody.content)[0] }; operation.consumes.push(mt); operation.hasConsumes = true; let tmp = obj.consumes.find(function(e,i,a){ return (e.mediaType === mt.mediaType); }); if (!tmp) { obj.consumes.push(clone(mt)); // so convertArray works correctly obj.hasConsumes = true; } operation.bodyParam.schema = contentType.schema; operation.bodyParam.example = JSON.stringify(sampler.sample(contentType.schema,{},api)); for (let p in schemaProperties) { if (typeof contentType.schema[p] !== 'undefined') operation.bodyParam[p] = contentType.schema[p]; } if (contentType.schema.type) { operation.bodyParam.type = contentType.schema.type; operation.bodyParam.dataType = typeMap(contentType.schema.type,operation.bodyParam.required,contentType.schema); // this is the below mentioned } } operation.bodyParam["%dataType%"] = operation.bodyParam.dataType; // bug in typescript-fetch template? operation.bodyParam.jsonSchema = safeJson({schema: operation.bodyParam.schema},null,2); operation.bodyParams.push(operation.bodyParam); operation.bodyParam.isFile = false; // TODO operation.allParams.push(clone(operation.bodyParam)); } operation.tags = op.tags; operation.imports = op.tags; operation.vendorExtensions = specificationExtensions(op); operation.responses = []; for (let r in op.responses) { let response = op.responses[r]; let entry = {}; entry.code = r; entry.isDefault = (r === 'default'); entry.nickname = 'response'+r; entry.message = response.description; entry.description = response.description||''; entry.simpleType = true; entry.schema = {}; entry.jsonSchema = safeJson({ schema: entry.schema },null,2); if (response.content) { entry.baseType = 'object'; entry.dataType = typeMap(entry.baseType,false,{}); let contentType = Object.values(response.content)[0]; let mt = {}; mt.mediaType = Object.keys(response.content)[0]; operation.produces.push(mt); operation.hasProduces = true; let tmp = obj.produces.find(function(e,i,a){ return (e.mediaType === mt.mediaType); }); if (!tmp) { obj.produces.push(clone(mt)); // so convertArray works correctly obj.hasProduces = true; } if (contentType && contentType.schema) { entry.schema = contentType.schema; entry.jsonSchema = safeJson({schema:entry.schema},null,2); entry.baseType = contentType.schema.type; entry.isPrimitiveType = true; entry.dataType = typeMap(contentType.schema.type,false,entry.schema); if (contentType.schema["x-oldref"]) { entry.dataType = contentType.schema["x-oldref"].replace('#/components/schemas/',''); entry.isPrimitiveType = false; } } if (contentType && contentType.example) { entry.hasExamples = true; if (!entry.examples) entry.examples = []; entry.examples.push({contentType: mt.mediaType, example: JSON.stringify(contentType.example,null,2)}); } if (contentType && contentType.examples) { for (let ex in contentType.examples) { const example = contentType.examples[ex]; if (example.value) { entry.hasExamples = true; if (!entry.examples) entry.examples = []; entry.examples.push({contentType: mt.mediaType, example: JSON.stringify(example.value,null,2)}); } } } if (!entry.hasExamples && entry.schema) { let example = sampler.sample(entry.schema,{},api); if (example) { entry.hasExamples = true; if (!entry.examples) entry.examples = []; entry.examples.push({contentType: mt.mediaType, example:JSON.stringify(example,null,2)}); } } operation.examples = (operation.examples||[]).concat(entry.examples||[]); operation.returnType = entry.dataType; operation.returnBaseType = entry.baseType; operation.returnTypeIsPrimitive = entry.isPrimitiveType; operation.returnContainer = ((entry.baseType === 'object') || (entry.baseType === 'array')); } entry.responseHeaders = []; // TODO responseHeaders entry.responseHeaders = convertArray(entry.responseHeaders); entry.examples = convertArray(entry.examples); entry.openapi = {}; entry.openapi.links = response.links; operation.responses.push(entry); } if (obj.sortParamsByRequiredFlag) { operation.allParams = operation.allParams.sort(function(a,b){ if (a.required && !b.required) return -1; if (b.required && !a.required) return +1; return 0; }); } operation.queryParams = convertArray(operation.queryParams); operation.headerParams = convertArray(operation.headerParams); operation.pathParams = convertArray(operation.pathParams); operation.formParams = convertArray(operation.formParams); operation.bodyParams = convertArray(operation.bodyParams); operation.allParams = convertArray(operation.allParams); operation.examples = convertArray(operation.examples); if (operation.hasConsumes) { operation.consumes = convertArray(operation.consumes); } else { delete operation.consumes; } if (operation.hasProduces) { operation.produces = convertArray(operation.produces); } else { delete operation.produces; } operation.openapi.callbacks = op.callbacks; //let container = {}; //container.baseName = operation.nickname; //container.operation = operation; //obj.operations.push(container); return operation; } function convertToApis(source,obj,defaults) { let apis = []; for (let p in source.paths) { for (let m in source.paths[p]) { if ((m !== 'parameters') && (m !== 'summary') && (m !== 'description') && (!m.startsWith('x-'))) { let op = source.paths[p][m]; let tagName = 'Default'; if (op.tags && op.tags.length > 0) { tagName = op.tags[0]; } let entry = apis.find(function(e,i,a){ return (e.name === tagName); }); if (!entry) { entry = {}; entry.name = tagName; //if (defaults.language === 'typescript') { // entry.classname = Case.pascal(entry.name); //} //else { entry.classname = tagName+'Api'; //} entry.classFilename = tagName+'Api'; entry.classVarName = tagName; // see issue #21 entry.packageName = obj.packageName; //! this may not be enough / sustainable. Or many props at wrong level :( entry.operations = {}; entry.operations.operation = []; apis.push(entry); } let operation = convertOperation(op,m,p,source.paths[p],obj,source); entry.operations.operation.push(operation); } } } for (let t in source.tags) { let tag = source.tags[t]; let entry = apis.find(function(e,i,a){ return (e.name === t); }); if (entry) { entry.classname = tag.name+'Api'; entry.description = tag.description; entry.externalDocs = tag.externalDocs; } } for (let api of apis) { api.operations.operation = convertArray(api.operations.operation); } apis = convertArray(apis); return apis; } // TODO add html and possibly termcap (https://www.npmjs.com/package/hermit) renderers const markdownPPs = { nop: function(markdown) { return markdown; } }; const typeMaps = { nop: function(type,required,schema) { return type; }, java: function(type,required,schema) { let result = type; if (!required) result += '?'; return result; }, javascript: function(type,required,schema) { let result = type; if (result === 'integer') result = 'number'; return result; }, typescript: function(type,required,schema) { let result = type; if (result === 'integer') result = 'number'; if (result === 'array') { result = 'Array'; if (schema.items && schema.items.type) { result += '<'+typeMap(schema.items.type,false,schema.items)+'>'; } } return result; }, go: function(type,required,schema) { let result = type; if (result === 'integer') result = 'int'; if (result === 'boolean') result = 'bool'; if (result === 'object') result = 'struct{}'; if (result === 'array') { result = '[100]'; //! if (schema.items && schema.items.type) { result += typeMap(schema.items.type,false,schema.items); } } return result; } }; const reservedWords = { nop: [], go: [ 'type' ] }; let typeMap = typeMaps.nop; let markdownPP = markdownPPs.nop; let reserved = reservedWords.nop; function getBase() { let base = {}; base.supportingFiles = []; base.modelTests = []; base.modelDocs = []; base.apiTests = []; base.apiDocs = []; base.allowUnicodeIdentifiers = false; /* boolean, toggles whether unicode identifiers are allowed in names or not, default is false */ //base.developerName = x; /* developer name in generated pom.xml */ //base.developerEmail = x; /* developer email in generated pom.xml */ //base.developerOrganization = x; /* developer organization in generated pom.xml */ //base.developerOrganizationUrl = x; /* developer organization URL in generated pom.xml */ base.gitUserId = 'Mermade'; /* Git user ID, e.g. swagger-api. */ base.gitRepoId = 'openapi-codegen'; /* Git repo ID, e.g. swagger-codegen. */ base.licenseName = 'Unlicense'; /* The name of the license */ base.projectLicenseName = 'Unlicense'; /* The name of the license */ base.licenseUrl = 'https://unlicense.org'; /* The URL of the license */ base.projectLicenseUrl = 'https://unlicense.org'; /* The URL of the license */ base.projectUrl = 'https://github.com/Mermade/openapi-codegen'; base.localVariablePrefix = ''; /* prefix for generated code members and local variables */ base.serializableModel = true; /* boolean - toggle "implements Serializable" for generated models */ base.bigDecimalAsString = false; /* Treat BigDecimal values as Strings to avoid precision loss. */ base.sortParamsByRequiredFlag = true; /* Sort method arguments to place required parameters before optional parameters. */ base.useDateTimeOffset = 0; /* Use DateTimeOffset to model date-time properties */ base.ensureUniqueParams = false; /* Whether to ensure parameter names are unique in an operation (rename parameters that are not). */ base.optionalMethodArgument = false; /* Optional method argument, e.g. void square(int x=10) (.net 4.0+ only). */ base.optionalAssemblyInfo = false; /* Generate AssemblyInfo.cs. */ base.netCoreProjectFile = true; /* Use the new format (.NET Core) for .NET project files (.csproj). */ base.useCollection = false; /* Deserialize array types to Collection<T> instead of List<T>. */ base.interfacePrefix = ''; /* Prefix interfaces with a community standard or widely accepted prefix. */ base.returnICollection = false; /* Return ICollection<T> instead of the concrete type. */ base.optionalProjectFile = false; /* Generate {PackageName}.csproj. */ base.modelPropertyNaming = 'original'; /* {camelCase, PascalCase, snake_case, original, UPPERCASE} */ base.targetFramework = 4; /* The target .NET framework version. */ base.modelNamePrefix = ''; /* Prefix that will be prepended to all model names. Default is the empty string. */ base.modelNameSuffix = ''; /* Suffix that will be appended to all model names. Default is the empty string. */ base.releaseNote = 'Minor update'; /* Release note, default to 'Minor update'. */ base.supportsES6 = true; /* Generate code that conforms to ES6. */ base.supportsAsync = true; /* Generate code that supports async operations. */ base.emitJSDoc = true; /* */ base.emitModelMethods = true; /* */ base.excludeTests = false; /* Specifies that no tests are to be generated. */ base.generateApiDocs = true; /* Not user-configurable. System provided for use in templates. */ base.generateApiTests = true; /* Specifies that api tests are to be generated. */ base.generateModelDocs = true; /* Not user-configurable. System provided for use in templates. */ base.generateModelTests = true; /* Specifies that model tests are to be generated. */ base.hideGenerationTimestamp = false; /* Hides the generation timestamp when files are generated. */ base.generatePropertyChanged = true; /* Specifies that models support raising property changed events. */ base.nonPublicApi = false; /* Generates code with reduced access modifiers; allows embedding elsewhere without exposing non-public API calls to consumers. */ base.validatable = true; /* Generates self-validatable models. */ base.ignoreFileOverride = '.swagger-codegen-ignore'; /* Specifies an override location for the .swagger-codegen-ignore file. Most useful on initial generation. */ base.removeOperationIdPrefix = false; /* Remove prefix of operationId, e.g. config_getId => getId */ base.serverPort = 8000; base.newline = '\n'; base.apiDocPath = ''; base.modelDocPath = ''; base.classPrefix = 'cls'; //extensions base.modelNaming = 'original'; /* {camelCase, PascalCase, snake_case, original, UPPERCASE} */ return base; } function getPrime(api,defaults) { let prime = {}; prime.classname = api.info.title.toLowerCase().split(' ').join('_').split('-').join('_'); prime.projectName = prime.classname; prime.appVersion = api.info.version; prime.apiVersion = api.info.version; prime.packageVersion = api.info.version; prime.projectVersion = api.info.version; prime.version = api.info.version; prime.title = api.info.title; prime.swaggerVersion = '2.0'; prime.generatorVersion = require('./package.json').version; prime.swaggerCodegenVersion = 'openapi-codegen-v'+prime.generatorVersion; prime.appDescription = api.info.description||'No description'; prime.projectDescription = prime.appDescription; prime.classVarName = 'default'; // see issue #21 prime.exportedName = prime.classname; prime.packageTitle = prime.classname; /* Specifies an AssemblyTitle for the .NET Framework global assembly attributes stored in the AssemblyInfo file. */ prime.infoEmail = api.info.contact ? api.info.contact.email : null; prime.appContact = prime.infoEmail; prime.infoUrl = api.info.contact ? api.info.contact.url : null; prime.licenseInfo = api.info.license ? api.info.license.name : null; prime.licenseUrl = api.info.license ? api.info.license.url : null; prime.appName = api.info.title; prime.host = '' prime.basePath = '/'; prime.basePathWithoutHost = '/'; prime.contextPath = '/'; prime.packageName = 'IO.OpenAPI'; prime.apiPackage = prime.packageName; /* package for generated api classes */ prime.generatorPackage = 'IO.OpenAPI'; prime.invokerPackage = 'IO.OpenAPI'; /* root package for generated code */ prime.modelPackage = 'IO.OpenAPI'; /* package for generated models */ prime.package = 'IO.OpenAPI.Api'; prime.phpInvokerPackage = prime.invokerPackage; /* root package for generated php code */ prime.perlModuleName = prime.invokerPackage; /* root module name for generated perl code */ prime.podVersion = '1.0.0'; prime.pythonPackageName = prime.invokerPackage; /* package name for generated python code */ prime.clientPackage = 'IO.OpenAPI.Client'; prime.importPath = 'IO.OpenAPI.Api.Default'; prime.hasImport = true; prime.hasMore = true; prime.generatedDate = new Date().toString(); prime.generatorClass = defaults.configName; // 'class ' prefix? prime.fullyQualifiedGeneratorClass = prime.generatorPackage+'.'+prime.generatorClass; prime.imports = [ { "import": "IO.OpenAPI.Model.Default" } ]; prime.name = prime.classname; prime.classFilename = prime.classname; prime.jsModuleName = prime.classname; prime.moduleName = prime.classname; prime.jsProjectName = prime.classname; prime.baseNamespace = prime.packageName; prime.sourceFolder = './out/'+defaults.configName; /* source folder for generated code */ prime.templateDir = './templates/'+defaults.configName; prime.implFolder = prime.sourceFolder; /* folder for generated implementation code */ prime.library = ''; /* library template (sub-template) */ prime.packageGuid = uuidv4(); /* The GUID that will be associated with the C# project */ prime.optionalEmitDefaultValues = false; /* Set DataMember's EmitDefaultValue. */ prime.packageProductName = prime.projectName; /* Specifies an AssemblyProduct for the .NET Framework global assembly attributes stored in the AssemblyInfo file. */ prime.packageCompany = 'Smartbear Software'; /* Specifies an AssemblyCompany for the .NET Framework global assembly attributes stored in the AssemblyInfo file. */ prime.packageAuthors = 'Swagger-Codegen authors'; /* Specifies Authors property in the .NET Core project file. */ prime.packageCopyright = 'Copyright 2016 Smartbear Software'; /* Specifies an AssemblyCopyright for the .NET Framework global assembly attributes stored in the AssemblyInfo file. */ // prime.groupId = x; /* groupId in generated pom.xml */ // prime.artifactId = x; /* artifactId in generated pom.xml */ // prime.artifactVersion = x; /* artifact version in generated pom.xml */ // prime.artifactUrl = x; /* artifact URL in generated pom.xml */ // prime.scmConnection = x; /* SCM connection in generated pom.xml */ // prime.scmDeveloperConnection = x; /* SCM developer connection in generated pom.xml */ // prime.scmUrl = x; /* SCM URL in generated pom.xml */ prime.httpUserAgent = 'OpenAPI-Codegen/'+prime.packageVersion+'/'+defaults.configName; /* HTTP user agent, e.g. codegen_csharp_api_client, default to 'Swagger-Codegen/{packageVersion}}/{language}' */ return prime; } function transform(api, defaults, callback) { let base = getBase(); // defaults which are hard-coded let lang = (defaults.language||'').toLowerCase(); if (typeMaps[lang]) typeMap = typeMaps[lang]; if (reservedWords[lang]) reserved = reservedWords[lang]; let prime = getPrime(api,defaults); // defaults which depend in some way on the api definition let obj = Object.assign({},base,prime,defaults); if (defaults.swagger) { obj.swagger = defaults.swagger; } else { obj.swagger = downconverter(api); } obj["swagger-yaml"] = yaml.safeDump(obj.swagger, {lineWidth:-1}); // set to original if converted v2.0 obj["swagger-json"] = JSON.stringify(obj.swagger, null, 2); // set to original if converted 2.0 obj["openapi-yaml"] = yaml.safeDump(api, {lineWidth:-1}); obj["openapi-json"] = JSON.stringify(api, null, 2); // openapi3 extensions obj.openapi = {}; obj.openapi.operationCounter = 1; obj.openapi.version = api.openapi; obj.openapi.servers = api.servers; // helper functions (seen in erlang-client) obj.qsEncode = function() { thisFunc = encodeURIComponent; return function(template,context){ console.warn(util.inspect(template)); console.warn(util.inspect(this)); }; }; obj.this = function() { console.warn('this called'); return thisFunc(this.paramName); }; obj.length = function() { arrayMode = 'length'; return true; }; obj.capitalize = function() { //? seen in akka-scala return true; }; let allSecurity = []; if (api.components && api.components.securitySchemes) { for (let s in api.components.securitySchemes) { let entry = {}; entry[s] = api.components.securitySchemes[s]; allSecurity.push(entry); } } let authData = getAuthData(allSecurity,api); obj = Object.assign(obj,authData); api = deref(api,api,{$ref:'x-oldref'}); obj.messages = []; let message = {}; let vOptions = {lint:defaults.lint}; if (defaults.stools && defaults.swagger) { stools.specs.v2_0.validate(defaults.swagger,function(err,result){ if (err) console.error(util.inspect(err)); if (result.errors) { for (let e of result.errors) { let message = {}; message.level = 'Error'; message.elementType = 'Path'; message.message = e.message; message.elementId = e.path.join('/'); obj.messages.push(message); if (defaults.verbose) console.log(message); } for (let w of result.warnings) { let message = {}; message.level = 'Warning'; message.elementType = 'Path'; message.message = w.message; message.elementId = w.path.join('/'); obj.messages.push(message); if (defaults.verbose) console.log(message); } } }); } else try { validator(api,vOptions); message.level = 'Valid'; message.elementType = 'Context'; message.elementId = 'None'; message.message = 'No validation errors detected'; obj.messages.push(message); if (defaults.verbose) console.log(message); } catch (ex) { message.level = 'Error'; message.elementType = 'Context'; message.elementId = vOptions.context.pop(); message.message = ex.message; obj.messages.push(message); console.error(message); } if (api.servers && api.servers.length) { let u = api.servers[0].url; let up = url.parse(u); obj.host = up.host; obj.basePath = up.path; obj.basePathWithoutHost = up.path; } obj.consumes = []; obj.produces = []; obj.apiInfo = {}; obj.apiInfo.apis = convertToApis(api,obj,defaults); obj.produces = convertArray(obj.produces); obj.consumes = convertArray(obj.consumes); if (defaults.debug) obj.debugOperations = JSON.stringify(obj,null,2); obj.models = []; if (api.components) { for (let s in api.components.schemas) { let schema = api.components.schemas[s]; if (schema !== null) { let container = {}; let model = {}; model.name = s; if (obj.modelNaming === 'snake_case') { model.name = Case.snake(model.name); } model.classname = model.name; model.classVarName = s; model.modelJson = safeJson(schema,null,2); model.title = schema.title; model.unescapedDescription = schema.description; model.classFilename = obj.classPrefix+model.name; model.modelPackage = model.name; model.hasEnums = false; model.vars = []; walkSchema(schema,{},wsGetState,function(schema,parent,state){ let entry = {}; entry.name = schema.name || schema.title; if (!entry.name && state.property && (state.property.startsWith('properties') || state.property.startsWith('additionalProperties'))) { entry.name = state.property.split('/')[1]; } if (obj.modelPropertyNaming === 'snake_case') { entry.name = Case.snake(entry.name); } if (reserved.indexOf(entry.name)>=0) { entry.name = Case.pascal(entry.name); } if (entry.name) { entry.baseName = entry.name.toLowerCase(); } entry.getter = Case.camel('get_'+entry.name); entry.setter = Case.camel('set_'+entry.name); entry.description = schema.description||''; entry.unescapedDescription = entry.description; entry.type = schema.type; entry.required = (parent.required && parent.required.indexOf(entry.name)>=0)||false; entry.isNotRequired = !entry.required; entry.readOnly = !!schema.readOnly; entry.type = typeMap(entry.type,entry.required,schema); entry.datatype = entry.type; //? entry.jsonSchema = safeJson(schema,null,2); for (let p in schemaProperties) { if (typeof schema[p] !== 'undefined') entry[p] = schema[p]; } entry.isEnum = !!schema.enum; entry.isPrimitiveType = ((schema.type !== 'object') && (schema.type !== 'array')); entry.isNotContainer = entry.isPrimitiveType; if (entry.isEnum) entry.isNotContainer = false; entry.isContainer = !entry.isNotContainer; if ((schema.type === 'object') && schema.properties && schema.properties["x-oldref"]) { entry.complexType = schema.properties["x-oldref"].replace('#/components/schemas/',''); } entry.dataFormat = schema.format; entry.defaultValue = schema.default; if (entry.isEnum) { model.allowableValues = {}; model.allowableValues.enumVars = []; model["allowableValues.values"] = schema.enum; for (let v of schema.enum) { let e = { name: v, value: '"'+v+'"' }; // insane, why aren't the quotes in the template? model.allowableValues.enumVars.push(e); } model.allowableValues.enumVars = convertArray(model.allowableValues.enumVars); } if (entry.name && state.depth<=1) { entry.nameInCamelCase = Case.pascal(entry.name); // for erlang-client entry.datatypeWithEnum = s+'.'+entry.name+'Enum'; entry.enumName = entry.name+'Enum'; model.hasEnums = true; model.vars.push(entry); } }); model.vars = convertArray(model.vars); container.model = model; container.importPath = model.name; obj.models.push(container); } } } if (obj.models.length === 0) { obj.models = { isEmpty: true }; } else { Object.defineProperty(obj.models, 'isEmpty', { enumerable: true, value: false }); } obj.orderedModels = {}; Object.keys(obj.models).sort().forEach(function(key) { obj.orderedModels[key] = obj.models[key]; }); if (defaults.debug) obj.debugModels = JSON.stringify(obj.models,null,2); if (callback) callback(null,obj); return obj; } module.exports = { getBase : getBase, getPrime : getPrime, transform : transform };