mares-swagger-spec-maker
Version:
mares framework ���조에 맞춰서openapi spec을 combine 해주는 모듈입니다.
134 lines (102 loc) • 4.35 kB
JavaScript
const utils = require('./utils')
const coreUtils = require('mares-welder-utils')
const path = require('path')
const fs = require('fs')
const API_SPEC_NAME = '.spec.js'
const BOUNDED_CONTEXT_API_SPEC_NAME = '.context-api-spec.js'
const VERSION_API_SPEC_NAME = '.version-api-spec.js'
const DOMAIN_NAME = 'domain/aggregates'
module.exports = () => {
return {
/**
* paths spec 안에 있는 모든 리소스명 앞이 prefix를 추가해준다.
* @param {Object} paths - root spec 안에 있는 paths spec 객체
* @param {string} prefix - prefix 문자열
* @return {Object} 리소스에 prefix가 추가된 root spec 객체
*/
addPrefix: (paths, prefix) => {
return utils.addResourcePrefixInPaths(paths, prefix)
},
/**
* root spec에 bounded-context spec, api spec, api version spec, components.schemas(도메인을 읽어서 추가)를 머지한다.
* @param {Object} rootSpec - root spec, root project 폴더에 위치함. 현재 이름은 .root-api-spec.js로 고정
* @param {string} boundedContextPath - boundedContext의 root 경로 문자열
* @return {Object} bounded-context에 있는 모든 스펙(version spec, api spec, bounded-context spec)이 merge된 root spec 객체
*/
merge: (rootSpec, boundedContextPath) => {
// version 스펙
let versionPaths = coreUtils.findFile(boundedContextPath, VERSION_API_SPEC_NAME, ['node_modules'])
// api 스펙
let specPaths = coreUtils.findFile(boundedContextPath, API_SPEC_NAME, ['node_modules'])
// bounded-context 폴더 경로
let bcPath = path.resolve(boundedContextPath, './', BOUNDED_CONTEXT_API_SPEC_NAME)
// domain 폴더 경로
let domainPath = path.resolve(boundedContextPath, './', DOMAIN_NAME)
// components.schemas에 추가될 객체
let schemas = {}
let bcSpec = null
let Aggregates = null
if (fs.existsSync(bcPath)) {
bcSpec = require(bcPath)
}
if (fs.existsSync(domainPath)) {
Aggregates = require(domainPath)
}
for (let k in Aggregates) {
let domain = Aggregates[k]
let schemaName = domain.meta.name
let schema = utils.convertDomainToSchema(domain)
schema = utils.updateRequiredType(schema)
schemas[schemaName] = schema
}
if (!rootSpec.components) rootSpec.components = {}
rootSpec.components = utils.addSchemasToComponents(rootSpec.components, schemas)
// 루트 보안스키마가 없으면 빈객체 생성
if (!rootSpec.components.securitySchemes) rootSpec.components.securitySchemes = {}
specPaths.forEach((specPath) => {
let securitySchemes = bcSpec.components.securitySchemes
securitySchemes = {...securitySchemes, ...rootSpec.components.securitySchemes}
let spec = require(specPath)
if (bcSpec) {
spec = utils.addSecurityToPath(spec, securitySchemes)
}
versionPaths.forEach((versionPath) => {
let versionDirname = path.dirname(versionPath)
let pathDirname = path.dirname(specPath)
if (pathDirname.indexOf(versionDirname) > -1) {
let version = require(versionPath)
spec = utils.addVersionToPath(spec, version)
}
})
rootSpec = utils.mergeRootAndPath(rootSpec, spec)
})
rootSpec = utils.mergeRootAndBoundedContext(rootSpec, bcSpec)
return rootSpec
},
/**
* swagger3.0 spec에서 components의 값을 실제 스펙안으로 대치시킨다.
* @param {Object} rootSpec - swagger 3.0 spec,
* @returns {Object} replacedRootSpec - {'$ref':'#/components/*'}가 components의 value로 대치된 스펙
*/
replaceAllComponents: (rootSpec) => {
// ref 참조 단계 (ex: ref->ref->ref 면 3depth)
let refDepth = 0
// 최대 ref 참조 단계는 우선 10으로 설정
const MAX_REF_DEPTH = 10
let stringSpecPaths = JSON.stringify(rootSpec.paths)
let loopSpec = rootSpec
while (stringSpecPaths.indexOf('$ref') !== -1) {
refDepth++
if (refDepth > MAX_REF_DEPTH) {
let idx = stringSpecPaths.indexOf('$ref')
let sub = stringSpecPaths.substr(idx, 1000)
console.warn('스웨거 파싱중에 문제가 발견되었습니다. 선언은 했으나 쓰지 않는 컴포넌트가 있을 수 있으니 확인바랍니다.', sub)
break
}
loopSpec = utils.replaceAllComponents(loopSpec)
stringSpecPaths = JSON.stringify(loopSpec.paths)
}
return loopSpec
}
}
}