/**
* Plugin factory
*
* @param {string} pkgName - NPM package name
* @returns {class}
*/
async function factory (pkgName) {
const me = this
/**
* WaibuRestApi class
*
* @class
*/
class WaibuRestApi extends this.app.pluginClass.base {
static alias = 'wra'
static dependencies = ['waibu', 'waibu-db', 'bajo-extra']
constructor () {
super(pkgName, me.app)
this.routePathHandlers = ['restapi']
this.config = {
waibu: {
prefix: 'api'
},
mountMainAsRoot: true,
exposeHead: true,
enablePatch: false,
bodyLimit: 1048576,
intl: {
detectors: ['qs', 'header']
},
format: {
supported: ['json', 'xml'],
asExt: false,
xml: {
bodyParser: {
contentTypes: ['text/xml', 'application/xml', 'application/rss+xml'],
validate: false
},
response: {
wrapper: 'doc',
declaration: true,
valueAsAttributes: true
}
}
},
responseKey: {
data: 'data',
oldData: 'oldData',
page: 'page',
count: 'count',
pages: 'pages',
success: 'success',
statusCode: 'code',
error: 'error',
cached: 'cached',
message: 'message',
details: 'details'
},
mapSlash: '~',
mapDot: ',',
multipart: {
},
cors: {},
helmet: {},
compress: false,
rateLimit: false,
disabled: [],
rerouted: {}
}
}
init = async () => {
const { uniq } = this.app.lib._
this.config.format.supported.push('json')
this.config.format.supported = uniq(this.config.format.supported)
}
routePath = (name, { defFormat = '.json', uriEncoded = true } = {}) => {
const { get, trimStart } = this.app.lib._
const { fullPath, ns } = this.app.bajo.breakNsPath(name)
const prefix = get(this.app[ns], 'config.waibu.prefix', this.app[ns].alias)
const format = defFormat === false ? '' : (this.config.format.asExt ? defFormat : '')
let path = `/${this.config.waibu.prefix}/${prefix}${fullPath}${format}`
if (uriEncoded) path = path.split('/').map(p => encodeURI(p)).join('/')
return '/' + trimStart(path, '/')
}
transformResult = ({ data, req, reply, options = {} }) => {
const reformat = ({ data, req, reply, options = {} }) => {
const { forOwn, get } = this.app.lib._
const newData = {}
forOwn(data, (v, k) => {
let key = get(this.config, `responseKey.${k}`, k)
if (options.forFind && k === 'data') key = get(this.config, 'responseKey.data')
newData[key] = v
})
return newData
}
const returnError = ({ data, req, reply, options = {} }) => {
const { pascalCase } = this.app.lib.aneka
const { last, map, kebabCase, upperFirst, keys, each, get, isEmpty } = this.app.lib._
const cfg = this.config
const cfgWdb = this.app.waibuDb.config
const errNames = kebabCase(data.constructor.name).split('-')
if (last(errNames) === 'error') errNames.pop()
data.error = this.t(map(errNames, s => upperFirst(s)).join(' '))
data.success = false
data.statusCode = data.statusCode ?? 500
if (reply && cfgWdb.dbModel.dataOnly) {
each(keys(data), k => {
const key = get(cfg, `responseKey.${k}`, k)
if (k === 'details' && !isEmpty(data[k])) data[k] = JSON.stringify(data[k])
reply.header(`X-${pascalCase(this.alias)}-${pascalCase(key)}`, data[k])
})
}
reply.code(data.statusCode)
const result = cfgWdb.dbModel.dataOnly ? { error: data.message } : data
return reformat.call(this, { data: result, req, reply, options })
}
const returnSuccess = ({ data, req, reply, options = {} }) => {
const { pascalCase } = this.app.lib.aneka
const { each, keys, omit, get } = this.app.lib._
const cfg = this.config
const cfgWdb = this.app.waibuDb.config
if (reply) {
reply.code(req.method.toUpperCase() === 'POST' ? 201 : 200)
if (cfgWdb.dbModel.dataOnly) {
each(keys(omit(data, ['data'])), k => {
const key = get(cfg, `responseKey.${k}`, k)
reply.header(`X-${pascalCase(this.alias)}-${pascalCase(key)}`, data[k])
})
return data.data
}
}
return reformat({ data, req, reply, options })
}
const isError = data instanceof Error
if (isError) return returnError({ data, req, reply, options })
return returnSuccess({ data, req, reply, options })
}
}
return WaibuRestApi
}
export default factory