import countRecord from './lib/method/count-record.js'
import createAggregate from './lib/method/create-aggregate.js'
import createHistogram from './lib/method/create-histogram.js'
import createRecord from './lib/method/create-record.js'
import findOneRecord from './lib/method/find-one-record.js'
import findRecord from './lib/method/find-record.js'
import getSchemaExt from './lib/method/get-schema-ext.js'
import getRecord from './lib/method/get-record.js'
import removeRecord from './lib/method/remove-record.js'
import updateRecord from './lib/method/update-record.js'
/**
* Plugin factory
*
* @param {string} pkgName - NPM package name
* @returns {class}
*/
async function factory (pkgName) {
const me = this
/**
* WaibuDb class
*
* @class
*/
class WaibuDb extends this.app.baseClass.Base {
static alias = 'wdb'
static dependencies = ['dobo', 'waibu', 'dobo-extra']
constructor () {
super(pkgName, me.app)
this.config = {
waibu: {
prefix: 'db',
title: 'dbModels'
},
waibuAdmin: {
menuCollapsible: true,
menuHandler: 'waibuDb:adminMenu'
},
waibuMpa: {
icon: 'database'
},
dbModel: {
count: false,
patchEnabled: false
},
enableRestApiForModel: false
}
this.methodMap = {
create: 'POST',
find: 'GET',
get: 'GET',
update: 'PUT',
remove: 'DELETE'
}
this.selfBind([
'countRecord',
'createAggregate',
'createHistogram',
'createRecord',
'findOneRecord',
'findRecord',
'getSchemaExt',
'getRecord',
'removeRecord',
'updateRecord'
])
}
exportData = async (params) => {
const { getPlugin } = this.app.bajo
const { get } = this.app.lib._
const { fs } = this.app.lib
const { exportTo } = this.app.doboExtra
const { downloadDir } = getPlugin('sumba')
const model = get(params, 'payload.data.name')
const fields = get(params, 'payload.data.opts.fields')
const { id, file } = get(params, 'payload.data.download', {})
const dest = `${downloadDir}/${file}`
const options = {
filter: get(params, 'payload.data.filter', {}),
ensureDir: true,
fields
}
options.filter.sort = 'id:1'
const dmodel = this.app.dobo.getModel('SumbaDownload')
try {
await dmodel.updateRecord(id, { status: 'PROCESSING' })
await exportTo(model, dest, options)
const { size } = fs.statSync(dest)
await dmodel.updateRecord(id, { size, status: 'COMPLETE' })
} catch (err) {
await dmodel.updateRecord(id, { status: 'FAIL' })
}
}
adminMenu = async (locals, req) => {
const { getPluginPrefix } = this.app.waibu
const { pascalCase } = this.app.lib.aneka
const { getAppTitle } = this.app.waibuMpa
const { camelCase, map, groupBy, keys, kebabCase, filter, get, isArray } = this.app.lib._
const prefix = getPluginPrefix(this.ns)
const allModels = this.app.dobo.models
const models = filter(allModels, s => {
const byModelFind = !s.disabled.includes('find')
const disabled = get(this, `app.${s.plugin.ns}.config.waibuAdmin.modelDisabled`, [])
let modelDisabled = []
if (['*', 'all'].includes(disabled)) modelDisabled = map(filter(allModels, m => m.plugin.ns === s.plugin.ns), 'name')
else if (isArray(disabled)) modelDisabled = map(disabled, m => pascalCase(`${this.app[s.plugin.ns].constructor.alias} ${m}`))
const byDbDisabled = !modelDisabled.includes(s.name)
return byModelFind && byDbDisabled
})
const omenu = groupBy(map(models, s => {
const item = { name: s.name, ns: s.plugin.ns }
item.nsTitle = getAppTitle(s.plugin.ns)
return item
}), 'nsTitle')
const menu = []
for (const k of keys(omenu).sort()) {
const items = omenu[k]
const plugin = this.app[items[0].ns]
menu.push({
title: k,
children: map(items, item => {
return {
title: camelCase(item.name.slice(plugin.constructor.alias.length)),
href: `waibuAdmin:/${prefix}/${kebabCase(item.name)}/list`
}
})
})
}
return menu
}
getParams = (req, ...items) => {
const { map, trim, get } = this.app.lib._
let fields
req.query = req.query ?? {}
req.params = req.params ?? {}
if (req.query.fields) fields = map((req.query.fields ?? '').split(','), i => trim(i))
const params = {
fields,
count: get(this, 'config.dbModel.count', false),
body: req.body
}
items.forEach(i => {
params[i] = req.params[i]
})
return params
}
getLookupData = async ({ model, req, data, id = 'id', field, query }) => {
const { set, map } = this.app.lib._
const $in = map(data, id)
const q = query ?? set({}, field, { $in })
const options = {
dataOnly: true,
limit: -1,
query: q
}
return await this.findRecord({ model, req, options })
}
formatRecord = async ({ data, req, schema, options = {} }) => {
const { isArray } = this.app.lib._
if (!isArray(data)) return await this.formatRow({ data, req, schema, options })
const items = []
for (const d of data) {
const item = await this.formatRow({ data: d, req, schema, options })
items.push(item)
}
return items
}
formatRow = async ({ data, req, schema, options = {} }) => {
const { get, find, isFunction, cloneDeep } = this.app.lib._
const { format, callHandler } = this.app.bajo
const { escape } = this.app.waibu
const fields = get(schema, 'view.fields', Object.keys(schema.properties))
const rec = cloneDeep(data)
for (const f of fields) {
if (f === '_rel') continue
let prop = find(schema.properties, { name: f })
if (!prop) prop = find(schema.view.calcFields, { name: f })
if (!prop) continue
const opts = {
lang: options.lang ?? (req ? req.lang : undefined),
longitude: ['lng', 'longitude'].includes(f),
latitude: ['lat', 'latitude'].includes(f),
speed: ['speed'].includes(f),
degree: ['course', 'heading'].includes(f),
distance: ['distance'].includes(f)
}
rec[f] = format(data[f], prop.type, opts)
const vf = get(schema, `view.valueFormatter.${f}`)
if (vf) {
if (isFunction(vf)) rec[f] = await vf.call(this, data[f], data)
else rec[f] = await callHandler(vf, { req, value: data[f], data })
} else if (['string', 'text'].includes(prop.type)) rec[f] = escape(rec[f])
}
return rec
}
countRecord = countRecord
createAggregate = createAggregate
createHistogram = createHistogram
createRecord = createRecord
findOneRecord = findOneRecord
findRecord = findRecord
getSchemaExt = getSchemaExt
getRecord = getRecord
removeRecord = removeRecord
updateRecord = updateRecord
}
return WaibuDb
}
export default factory