waibu-db 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,6 +32,9 @@
32
32
  "checkAll": "Check All",
33
33
  "uncheckAll": "Uncheck All",
34
34
  "attachment": "Attachment",
35
+ "exportInQueue": "Data export in queue. Please check your download list for progress",
36
+ "tsv": "TSV",
37
+ "ndjson": "NDJSON",
35
38
  "op": {
36
39
  "equals": "Equals",
37
40
  "notEquals": "Not Equals",
package/bajo/intl/id.json CHANGED
@@ -32,6 +32,9 @@
32
32
  "checkAll": "Cek Semua",
33
33
  "uncheckAll": "Uncheck Semua",
34
34
  "attachment": "Attachment",
35
+ "exportInQueue": "Ekspor data sedang dalam antrian. Silahkan cek daftar unduh Anda untuk perkembangan terakhir",
36
+ "tsv": "TSV",
37
+ "ndjson": "NDJSON",
35
38
  "op": {
36
39
  "equals": "Sama Dengan",
37
40
  "notEquals": "Tidak Sama Dengan",
@@ -16,7 +16,7 @@
16
16
  <c:wdb-btn-clone href="<%= schema.view.control.cloneHref ?? 'undefined' %>" />
17
17
  <% } %>
18
18
  <% if (!schema.view.control.noExportBtn) { %>
19
- <c:wdb-btn-export selector="#main-form" handler="details" launch-on-end/>
19
+ <c:wdb-btn-export selector="#main-form" handler="details" launch-on-end no-save/>
20
20
  <% } %>
21
21
  </c:btn-group>
22
22
  <% } %>
@@ -10,7 +10,7 @@
10
10
  <% if (!schema.view.control.noCloneBtn) { %>
11
11
  <c:wdb-btn-clone href="<%= schema.view.control.cloneHref ?? 'undefined' %>" />
12
12
  <% } %>
13
- <c:wdb-btn-export selector="#main-form" handler="edit" launch-on-end/>
13
+ <c:wdb-btn-export selector="#main-form" handler="edit" launch-on-end no-save />
14
14
  </c:btn-group>
15
15
  </c:grid-col>
16
16
  <c:grid-col col="6-lg" flex="justify-content:end-lg align-items:center">
package/index.js CHANGED
@@ -5,7 +5,7 @@ async function factory (pkgName) {
5
5
  constructor () {
6
6
  super(pkgName, me.app)
7
7
  this.alias = 'wdb'
8
- this.dependencies = ['dobo', 'waibu']
8
+ this.dependencies = ['dobo', 'waibu', 'bajo-queue', 'dobo-extra']
9
9
  this.config = {
10
10
  waibu: {
11
11
  prefix: 'db',
@@ -25,6 +25,33 @@ async function factory (pkgName) {
25
25
  modelRestApi: false
26
26
  }
27
27
  }
28
+
29
+ exportData = async (params) => {
30
+ const { getPlugin } = this.app.bajo
31
+ const { get } = this.lib._
32
+ const { fs } = this.lib
33
+ const { recordUpdate } = this.app.dobo
34
+ const { exportTo } = this.app.doboExtra
35
+ const { downloadDir } = getPlugin('sumba')
36
+ const model = get(params, 'payload.data.name')
37
+ const fields = get(params, 'payload.data.opts.fields')
38
+ const { id, file } = get(params, 'payload.data.download', {})
39
+ const dest = `${downloadDir}/${file}`
40
+ const options = {
41
+ filter: get(params, 'payload.data.filter', {}),
42
+ ensureDir: true,
43
+ fields
44
+ }
45
+ const dmodel = 'SumbaDownload'
46
+ try {
47
+ await recordUpdate(dmodel, id, { status: 'PROCESSING' })
48
+ await exportTo(model, dest, options)
49
+ const { size } = fs.statSync(dest)
50
+ await recordUpdate(dmodel, id, { size, status: 'COMPLETE' })
51
+ } catch (err) {
52
+ await recordUpdate(dmodel, id, { status: 'FAIL' })
53
+ }
54
+ }
28
55
  }
29
56
  }
30
57
 
@@ -7,7 +7,7 @@ async function addHandler ({ req, reply, model, params = {}, template, addOnsHan
7
7
  const options = {}
8
8
  model = model ?? pascalCase(req.params.model)
9
9
  const { schema } = await getSchemaExt(model, 'add', { params })
10
- if (schema.disabled.includes('create')) return reply.view(templateDisabled, { action: 'add' })
10
+ if (schema.disabled.includes('create')) return await reply.view(templateDisabled, { action: 'add' })
11
11
  // req.query.attachment = true
12
12
  options.fields = schema.view.fields
13
13
  let def = {}
@@ -34,7 +34,9 @@ async function addHandler ({ req, reply, model, params = {}, template, addOnsHan
34
34
  }
35
35
  const addOns = addOnsHandler ? await addOnsHandler.call(this.app[req.ns], { req, reply, params, data: resp, schema, error }) : undefined
36
36
  merge(params, { form, schema, error, addOns })
37
- return reply.view(template, params)
37
+ if (schema.template) template = schema.template
38
+ if (schema.layout) params.page.layout = schema.layout
39
+ return await reply.view(template, params)
38
40
  }
39
41
 
40
42
  export default addHandler
@@ -6,7 +6,7 @@ async function deleteHandler ({ req, reply, model, params = {}, templateDisabled
6
6
  const options = {}
7
7
  model = model ?? pascalCase(req.params.model)
8
8
  const { schema } = await getSchemaExt(model, 'add', { params })
9
- if (schema.disabled.includes('remove')) return reply.view(templateDisabled, { action: 'delete' })
9
+ if (schema.disabled.includes('remove')) return await reply.view(templateDisabled, { action: 'delete' })
10
10
  options.fields = schema.view.fields
11
11
  const ids = (req.body.ids ?? '').split(',')
12
12
  if (ids.length > 0) {
@@ -7,7 +7,7 @@ async function detailsHandler ({ req, reply, model, params = {}, id, template, a
7
7
  const options = {}
8
8
  model = model ?? pascalCase(req.params.model)
9
9
  const { schema } = await getSchemaExt(model, 'details', { params })
10
- if (schema.disabled.includes('get')) return reply.view(templateDisabled, { action: 'details' })
10
+ if (schema.disabled.includes('get')) return await reply.view(templateDisabled, { action: 'details' })
11
11
  // req.query.attachment = true
12
12
  options.fields = schema.view.fields
13
13
  id = id ?? req.params.id ?? req.query.id
@@ -16,7 +16,9 @@ async function detailsHandler ({ req, reply, model, params = {}, id, template, a
16
16
  const addOns = addOnsHandler ? await addOnsHandler.call(this.app[req.ns], { req, reply, params, data: resp, schema }) : undefined
17
17
  const attachments = await attachmentHandler.call(this, { schema, id })
18
18
  merge(params, { form, schema, addOns, attachments })
19
- return reply.view(template, params)
19
+ if (schema.template) template = schema.template
20
+ if (schema.layout) params.page.layout = schema.layout
21
+ return await reply.view(template, params)
20
22
  }
21
23
 
22
24
  export default detailsHandler
@@ -14,7 +14,7 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
14
14
  let form
15
15
  model = model ?? pascalCase(req.params.model)
16
16
  const { schema } = await getSchemaExt(model, 'edit', options, { params })
17
- if (schema.disabled.includes('update')) return reply.view(templateDisabled, { action: 'edit' })
17
+ if (schema.disabled.includes('update')) return await reply.view(templateDisabled, { action: 'edit' })
18
18
  // req.query.attachment = true
19
19
  options.fields = schema.view.fields
20
20
  id = id ?? req.params.id ?? req.query.id
@@ -45,7 +45,9 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
45
45
  const addOns = addOnsHandler ? await addOnsHandler.call(this.app[req.ns], { req, reply, params, data: resp, schema, error }) : undefined
46
46
  const attachments = await attachmentHandler.call(this, { schema, id })
47
47
  merge(params, { form, schema, error, addOns, attachments })
48
- return reply.view(template, params)
48
+ if (schema.template) template = schema.template
49
+ if (schema.layout) params.page.layout = schema.layout
50
+ return await reply.view(template, params)
49
51
  }
50
52
 
51
53
  export default editHandler
@@ -1,14 +1,27 @@
1
+ import prepCrud from '../prep-crud.js'
2
+
1
3
  async function exportHandler ({ req, reply, model, params = {}, templateDisabled = 'waibuDb.template:/disabled.html' } = {}) {
4
+ const { getPlugin } = this.app.bajo
5
+ const { dayjs } = this.lib
6
+ const { omit, kebabCase, get } = this.lib._
2
7
  const { pascalCase } = this.lib.aneka
3
8
  const { getSchemaExt } = this.app.waibuDb
4
9
  const { buildUrl } = this.app.waibuMpa
5
- const options = {}
10
+ const { pushDownload } = getPlugin('sumba')
6
11
  model = model ?? pascalCase(req.params.model)
7
- const { schema } = await getSchemaExt(model, 'add', options, { params })
8
- if (schema.disabled.includes('find')) return reply.view(templateDisabled, { action: 'list' })
9
- options.fields = schema.view.fields
12
+ const { schema } = await getSchemaExt(model, 'add', { params })
13
+ if (schema.disabled.includes('find')) return await reply.view(templateDisabled, { action: 'list' })
14
+ const data = prepCrud.call(this, { model, req, reply, args: ['model'] })
15
+ data.opts = omit(data.opts, ['req', 'reply'])
16
+ const source = `${this.name}:/export-handler`
17
+ const worker = 'waibuDb:exportData'
18
+ const type = get(data, 'input.ftype', 'json')
19
+ const settings = get(data, 'input.options', '').split(',')
20
+ const ext = settings.includes('zip') ? `${type}.gz` : type
21
+ const file = `${kebabCase(model)}_${dayjs().format('YYYYMMDDhhmmss')}.${ext}`
22
+ await pushDownload({ file, type, worker, source, data, req })
23
+ req.flash('notify', req.t('exportInQueue'))
10
24
  const url = buildUrl({ url: req.url, base: req.body.handler })
11
- req.flash('notify', req.t('Data export in queue. You\'ll be notified once completed'))
12
25
  return reply.redirectTo(url)
13
26
  }
14
27
 
@@ -6,7 +6,7 @@ async function listHandler ({ req, reply, model, template, params = {}, addOnsHa
6
6
  const options = { count: true, rels: '*' }
7
7
  model = model ?? pascalCase(req.params.model)
8
8
  const { schema } = await getSchemaExt(model, 'list', { params })
9
- if (schema.disabled.includes('find')) return reply.view(templateDisabled, { action: 'list' })
9
+ if (schema.disabled.includes('find')) return await reply.view(templateDisabled, { action: 'list' })
10
10
  for (const key of ['sort', 'limit', 'fields']) {
11
11
  const sessKey = `wdb${model}${upperFirst(key)}`
12
12
  if (!req.query[qsKey[key]]) req.query[qsKey[key]] = req.session[sessKey] ?? get(schema, `view.qs.${key}`)
@@ -21,7 +21,9 @@ async function listHandler ({ req, reply, model, template, params = {}, addOnsHa
21
21
  if (!isArray(addOns)) addOns = [addOns]
22
22
  }
23
23
  merge(params, { list, schema, addOns })
24
- return reply.view(template, params)
24
+ if (schema.template) template = schema.template
25
+ if (schema.layout) params.page.layout = schema.layout
26
+ return await reply.view(template, params)
25
27
  }
26
28
 
27
29
  export default listHandler
package/lib/prep-crud.js CHANGED
@@ -1,5 +1,6 @@
1
1
  function prepCrud ({ model, body, id, req, reply, options = {}, args }) {
2
2
  const { parseFilter } = this.app.waibu
3
+ const { buildQuery, getInfo } = this.app.dobo
3
4
  const { pascalCase } = this.lib.aneka
4
5
  const { cloneDeep, has } = this.lib._
5
6
  const cfgWeb = this.app.waibu.getConfig()
@@ -20,6 +21,7 @@ function prepCrud ({ model, body, id, req, reply, options = {}, args }) {
20
21
 
21
22
  const recId = id ?? params.id ?? req.query.id
22
23
  const name = pascalCase(model ?? params.model)
24
+ const { schema } = getInfo(name)
23
25
  const input = body ?? params.body
24
26
  opts.bboxLatField = req.query[cfgWeb.qsKey.bboxLatField]
25
27
  opts.bboxLngField = req.query[cfgWeb.qsKey.bboxLngField]
@@ -28,6 +30,7 @@ function prepCrud ({ model, body, id, req, reply, options = {}, args }) {
28
30
  if (options.limit) filter.limit = options.limit
29
31
  if (options.sort) filter.sort = options.sort
30
32
  if (options.page) filter.page = options.page
33
+ filter.query = buildQuery({ filter, schema })
31
34
  return { name, recId, input, opts, filter, attachment, stats, mimeType }
32
35
  }
33
36
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waibu-db",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "DB Helper",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -29,15 +29,22 @@ async function btnExport () {
29
29
  ftype: 'json',
30
30
  toggle (val) {
31
31
  if (val === 'clipboard') {
32
+ $refs.fkey.removeAttribute('disabled')
33
+ $refs.fvalue.removeAttribute('disabled')
32
34
  $refs.zip.setAttribute('disabled', '')
33
35
  $refs.xlsx.setAttribute('disabled', '')
34
- $refs.xml.setAttribute('disabled', '')
36
+ $refs.tsv.setAttribute('disabled', '')
37
+ $refs.ndjson.setAttribute('disabled', '')
35
38
  _.pull(this.options, 'zip')
36
39
  if (!['json', 'csv'].includes(this.ftype)) this.ftype = 'json'
37
40
  } else {
41
+ $refs.fkey.setAttribute('disabled', '')
42
+ $refs.fvalue.setAttribute('disabled', '')
38
43
  $refs.zip.removeAttribute('disabled')
39
44
  $refs.xlsx.removeAttribute('disabled')
40
- $refs.xml.removeAttribute('disabled')
45
+ $refs.tsv.removeAttribute('disabled')
46
+ $refs.ndjson.removeAttribute('disabled')
47
+ _.pull(this.options, 'fkey', 'fvalue')
41
48
  }
42
49
  },
43
50
  extractForm (selector) {
@@ -117,8 +124,8 @@ async function btnExport () {
117
124
  <c:grid-row gutter="2">
118
125
  <c:grid-col col="6-md">
119
126
  <c:fieldset t:legend="delivery" legend-type="6">
120
- <c:form-radio x-model="delivery" value="file" t:label="saveAsFile" />
121
127
  <c:form-radio x-model="delivery" value="clipboard" t:label="copyClipboard" />
128
+ <c:form-radio x-model="delivery" value="file" t:label="saveAsFile" ${this.params.attr.noSave ? 'disabled' : ''} />
122
129
  </c:fieldset>
123
130
  <c:fieldset t:legend="options" legend-type="6" margin="top-2">
124
131
  <c:form-check x-ref="fkey" x-model="options" value="fkey" t:label="formattedField" />
@@ -130,8 +137,9 @@ async function btnExport () {
130
137
  <c:fieldset t:legend="fileType" legend-type="6">
131
138
  <c:form-radio x-ref="xlsx" x-model="ftype" value="xlsx" t:label="excelXlsx" />
132
139
  <c:form-radio x-ref="csv" x-model="ftype" value="csv" t:label="csv" />
133
- <c:form-radio x-ref="xml" x-model="ftype" value="xml" t:label="xml" />
140
+ <c:form-radio x-ref="tsv" x-model="ftype" value="tsv" t:label="tsv" />
134
141
  <c:form-radio x-ref="json" x-model="ftype" value="json" t:label="json" />
142
+ <c:form-radio x-ref="ndjson" x-model="ftype" value="ndjson" t:label="ndjson" />
135
143
  </c:fieldset />
136
144
  </c:grid-col>
137
145
  </c:grid-row>
@@ -10,7 +10,6 @@ async function query () {
10
10
  const { find, get, without, isEmpty, filter, upperFirst } = this.plugin.app.bajo.lib._
11
11
  const qsKey = this.plugin.app.waibu.config.qsKey
12
12
  const schema = get(this, 'component.locals.schema', {})
13
- const count = get(this, 'component.locals.list.count', 0)
14
13
  if (schema.view.disabled.includes('find')) {
15
14
  this.params.html = ''
16
15
  return
@@ -21,7 +20,6 @@ async function query () {
21
20
  const id = generateId('alpha')
22
21
  const columns = []
23
22
  const models = []
24
- const selects = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'contains', 'starts', 'ends', '!in', '!contains', '!starts', '!ends']
25
23
  for (const f of schema.view.fields) {
26
24
  if (!fields.includes(f)) continue
27
25
  const prop = find(schema.properties, { name: f })
@@ -29,7 +27,7 @@ async function query () {
29
27
  if (['float', 'double', 'integer', 'smallint'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
30
28
  else if (['datetime', 'date', 'time'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
31
29
  else if (['boolean'].includes(prop.type)) ops.push('eq', 'neq')
32
- else ops.push(...selects)
30
+ else ops.push('eq', 'neq', 'in', 'contains', 'starts', 'ends', '!in', '!contains', '!starts', '!ends')
33
31
  if (ops.length === 0) continue
34
32
  const sels = ops.map(o => `<c:option>${o}</c:option>`)
35
33
  models.push(`${f}Op: 'eq'`, `${f}Val: ''`)
@@ -51,12 +49,12 @@ async function query () {
51
49
  this.params.noTag = true
52
50
  const container = this.params.attr.modal ? 'modal' : 'drawer'
53
51
  this.params.html = await this.component.buildSentence(`
54
- <c:form-input ${count === 0 ? 'disabled' : ''} type="search" t:placeholder="query" id="${id}" x-data="{ query: '' }" x-init="
52
+ <c:form-input type="search" t:placeholder="query" id="${id}" x-data="{ query: '' }" x-init="
55
53
  const url = new URL(window.location.href)
56
54
  query = url.searchParams.get('${qsKey.query}') ?? ''
57
55
  " x-model="query" @on-query.window="query = $event.detail ?? ''" @keyup.enter="$dispatch('on-submit')">
58
56
  <c:form-input-addon>
59
- <c:${container} ${count === 0 ? 'trigger-disabled' : ''} trigger-icon="${this.params.attr.icon ?? 'dotsThree'}" trigger-on-end t:title="queryBuilder" x-ref="query" x-data="{
57
+ <c:${container} trigger-icon="${this.params.attr.icon ?? 'dotsThree'}" trigger-on-end t:title="queryBuilder" x-ref="query" x-data="{
60
58
  fields: ${jsonStringify(fields, true)},
61
59
  builder: '',
62
60
  selected: [],
@@ -70,6 +68,7 @@ async function query () {
70
68
  },
71
69
  initBuilder () {
72
70
  this.builder = document.getElementById('${id}').value
71
+ if (!this.builder.includes(':')) this.builder = ''
73
72
  if (_.isEmpty(this.builder)) return
74
73
  const tokens = _.merge({}, this.ops, {
75
74
  in: ':[',
@@ -158,7 +157,7 @@ async function query () {
158
157
  </c:${container}>
159
158
  </c:form-input-addon>
160
159
  <c:form-input-addon>
161
- <c:btn ${count === 0 ? 'disabled' : ''} t:content="submit" x-data="{
160
+ <c:btn t:content="submit" x-data="{
162
161
  submit () {
163
162
  const val = document.getElementById('${id}').value ?? ''
164
163
  const url = new URL(window.location.href)
@@ -1,14 +0,0 @@
1
- const action = {
2
- method: ['GET', 'POST'],
3
- title: 'Database Export',
4
- handler: async function (req, reply) {
5
- const { importModule } = this.app.bajo
6
- const handler = await importModule('waibuDb:/lib/crud/all-handler.js')
7
- const model = 'SumbaUser'
8
- const { action } = req.params
9
- const template = `sumba.template:/crud/${action}.html`
10
- return handler.call(this, { model, req, reply, action, template })
11
- }
12
- }
13
-
14
- export default action