UNPKG

mares-swagger-spec-maker

Version:

mares framework ���조에 맞춰서openapi spec을 combine 해주는 모듈입니다.

382 lines (308 loc) 9.96 kB
module.exports = { /** * tcomb를 wrapping한 도메인 class의 schema를 읽어서 swagger의 schema로 변환해준다. 단 required반영은 아직 하지 않았음. * @param {Object} Domain - tcomb형태의 schema struct 객체 * @return {Object} swagger에서 사용하는 schema객체 - {key: {type: 'string'}} */ convertDomainToSchema: (Domain) => { // Domain = require('../../message-context/domains/test').Root; let parent = {} let isRoot = true let recursive = (wrapper, prop, maybe = false, required = null) => { let isSearched = true // 최초 기본 프리미티브 데이터 타입을 찾는다. switch (prop.displayName) { case 'String': break case 'Number': break case 'Integer': break case 'Boolean': break case 'Object': break case 'Date': break default: // 프리미티브 데이터 타입을 찾지 못한 경우 list, enums, maybe, struct 인지를 찾아야 한다. isSearched = false } // 프리미티브일 경우 if (isSearched === true) { if (prop.displayName === 'Date') { // Date인 경우 type: string, format: date로 해야한다. wrapper.type = 'string' wrapper.format = 'date' } else if (prop.displayName === 'Object') { wrapper.type = 'object' wrapper.additionalProperties = true } else { // type을 소문자로 바꾸고, 필수값 여부를 체크한다. wrapper.type = prop.displayName.toLowerCase() } if (maybe === true) wrapper.type = '?' + wrapper.type } // 프리미티브가 아닐경우, struct, enums, maybe, list인경우, 그외 오류 else { let meta = prop.meta switch (prop.meta.kind) { case 'enums': // enums는 예외 프리미티브로 취급 wrapper.type = 'string' wrapper.enum = Object.values(meta.map) if (maybe === true) wrapper.type = '?' + wrapper.type break case 'maybe': recursive(wrapper, meta.type, true) break case 'list': wrapper.type = 'array' wrapper.items = {} if (maybe === true) wrapper.type = '?' + wrapper.type isRoot = false recursive(wrapper.items, meta.type) break case 'struct': // 이름을 지정하지 않았거나 root인 경우 다시 재귀호출 if (isRoot === true || prop.displayName.indexOf('Struct{') > -1 && prop.displayName.substr(prop.displayName.length - 1, 1) === '}') { wrapper.type = 'object' wrapper.properties = {} wrapper.required = [] if (maybe === true) wrapper.type = '?' + wrapper.type for (let k in meta.props) { wrapper.properties[k] = {} isRoot = false recursive(wrapper.properties[k], meta.props[k]) } } // 이름을 지정한 경우 components로 대체 else { wrapper['$ref'] = '#/components/schemas/' + prop.displayName } break } } } recursive(parent, Domain) return parent }, /** * convertDomainToSchema로 만들어진 객체에 ? 를 검사하여 required에 반영해준다. * @param {Object} schema - swagger schema spec - {key: {type: 'string'}} * @return {Object} required가 추가된 swagger schema. */ updateRequiredType: (schema) => { let recursive = (schema) => { if (schema.type === 'object') { let properties = schema.properties if (!schema.required) schema.required = [] for (let k in properties) { let type = properties[k].type // $ref가 아니라면 if (type) { if (type.indexOf('?') === -1) { schema.required.push(k) } else { properties[k].type = type.replace('?', '') } recursive(properties[k]) } } } else if (schema.type === 'array') { recursive(schema.items) } } recursive(schema) return schema }, /** * components.schemas에 해당 스키마를 추가한다. (같은 키값이면 덮어쓴다.) * @param {Object} components - swagger root.components spec * @param {Object} schemas - swagger schema spec - {key: {type: 'string'}} * @return {Object} schema가 추가된 swagger components spec */ addSchemasToComponents: (components, schemas) => { let compSchemas = components.schemas for (let schemaKey in schemas) { if (schemas.hasOwnProperty(schemaKey)) { compSchemas[schemaKey] = schemas[schemaKey] } } components.schemas = compSchemas return components }, /** * path안에 각 method 부분에 security를 추가한다. * @param {Object} path - 버전을 추가할 bounded-context의 api path spec. * @param {string} securitySchemes - swagger securitySchemes spec * @return {Object} version이 추가된 swagger path spec. */ addSecurityToPath: (path, securitySchemes) => { let ret = path for (let key in path) { for (let key2 in ret[key]) { let methodObj = ret[key][key2] if (!methodObj.security) methodObj.security = [] for (let security in securitySchemes) { let securityObj = {} securityObj[security] = [] methodObj.security.push(securityObj) } } } return ret }, /** * path안에 각 method 부분에 security를 추가한다. * @param {Object} path - 버전을 추가할 bounded-context의 api path spec. * @param {Object} version - version spec. * @return {Object} version이 추가된 swagger path spec. */ addVersionToPath: (path, version) => { let ret = path for (let key in path) { for (let key2 in ret[key]) { let methodObj = ret[key][key2] if (!methodObj.parameters) methodObj.parameters = [] let isFoundVersion = false for (let i = 0; i < methodObj.parameters.length; ++i) { if (methodObj.parameters[i].name === 'version') { isFoundVersion = true } } if (!isFoundVersion) { methodObj.parameters.push(version) } } } return ret }, /** * root spec과 bc spec을 merge한다. bc spec에는 components와 tags가 있다. 키값이 같으면 나중에 호출한 쪽에서 덮어쓴다. * @param {Object} root - swagger root spec * @param {Object} bc - swagger bounded-context spec * @return {Object} schema가 추가된 swagger root spec */ mergeRootAndBoundedContext: (root, bc) => { let ret = root if (!ret.components) ret.components = {} if (!ret.tags) ret.tags = [] for (let key in ret) { if (key === 'components' && bc && bc.components) { for (let compKey in bc.components) { let compSort = bc.components[compKey] for (let sortKey in compSort) { if (!ret.components[compKey]) ret.components[compKey] = {} ret.components[compKey][sortKey] = compSort[sortKey] } } } if (key === 'tags' && bc && bc.tags) { for (let i = 0; i < bc.tags.length; ++i) { let bcTagName = bc.tags[i].name let searchIdx = -1 for (let j = 0; j < ret.tags.length; ++j) { if (ret.tags[j].name === bcTagName) { searchIdx = j break } } if (searchIdx > -1) { ret.tags[searchIdx] = bc.tags[i] } else { ret.tags.push(bc.tags[i]) } } } } return ret }, /** * root spec과 path spec을 merge한다. * @param {Object} root - swagger root spec * @param {Object} paths - swagger path spec - {url: {get: {}, post: {}}, url2: {get: {}, post: {}}} * @return {Object} path가 추가된 swagger root spec */ mergeRootAndPath: (root, paths) => { let ret = root if (!ret.paths) ret.paths = {} for (let res in paths) { for (let method in paths[res]) { if (!ret.paths[res]) ret.paths[res] = {} ret.paths[res][method] = paths[res][method] } } return ret }, /** * 모든 paths의 리소스명을 돌아서 앞에 prefix를 붙여준다. * @param {Object} paths - swagger paths spec * @param {string} prefix - 리소스명 앞의 접두어, '/' 문자 포함 해야함 * @return {Object} path에 prefix가 추가된 swagger root spec */ addResourcePrefixInPaths: (paths, prefix) => { if (prefix.indexOf('/') !== 0) { prefix = '/' + prefix } let ret = {} for(let prop in paths){ ret[prefix + prop] = paths[prop] } return ret }, /** * swagger3.0 spec에서 components의 값을 실제 스펙안으로 대치시킨다. * @param {Object} rootSpec - swagger 3.0 spec, */ replaceAllComponents: (rootSpec) => { let spec = _.cloneDeep(rootSpec) var cnt = 0 var responsesCnt = 0 /** * 컴포넌트 스트링을 찾아서 실제 객체로 변환해준다. (예 - {$ref: '#/components/parameters/AccessToken'} -> {...real schema}) * @param {Object | Array} parent - 부모객체 * @param {*} obj - 변환할객체 * @param {Object} components - swagger components */ const doRecursive = (parent, obj, components, isResponses = false) => { cnt++ if (obj instanceof Object || obj instanceof Array) { for (let key in obj) { let inner = obj[key] if (inner instanceof Object) { if (key === 'responses'){ doRecursive(obj, inner, components, true) } else if (inner['$ref']) { let componentString = inner['$ref'] let splitedString = componentString.split('/') let componentName = splitedString[2] let componentKey = splitedString[3] if(isResponses){ if (components[componentName]) { delete obj[key]['$ref'] obj[key] = {description: `check ${componentName} ${componentKey}`} } } else { if (components[componentName]) { let realSpecObject = components[componentName][componentKey] delete obj[key]['$ref'] obj[key] = {...obj[key], ..._.cloneDeep(realSpecObject)} } } } else { doRecursive(obj, inner, components, isResponses) } } else { doRecursive(obj, inner, components, isResponses) } } } } doRecursive(null, spec.paths, spec.components, false) return spec } }