dobo 2.38.0 → 2.39.1
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.
- package/.bootorder +0 -0
- package/.github/FUNDING.yml +0 -0
- package/.github/workflows/repo-lockdown.yml +0 -0
- package/.jsdoc.conf.json +0 -0
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/docs/Dobo.html +0 -0
- package/docs/DoboAction.html +0 -0
- package/docs/DoboAdapter.html +0 -0
- package/docs/DoboConnection.html +0 -0
- package/docs/DoboFeature.html +0 -0
- package/docs/DoboModel.html +0 -0
- package/docs/data/search.json +0 -0
- package/docs/extend_bajo_hook-docs.js.html +0 -0
- package/docs/external-Tools.html +0 -0
- package/docs/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/global.html +0 -0
- package/docs/index.html +0 -0
- package/docs/index.js.html +0 -0
- package/docs/lib_factory_action.js.html +0 -0
- package/docs/lib_factory_adapter.js.html +0 -0
- package/docs/lib_factory_connection.js.html +0 -0
- package/docs/lib_factory_feature.js.html +0 -0
- package/docs/lib_factory_model.js.html +0 -0
- package/docs/lib_factory_model_build.js.html +0 -0
- package/docs/lib_factory_model_clear-record.js.html +0 -0
- package/docs/lib_factory_model_count-record.js.html +0 -0
- package/docs/lib_factory_model_create-record.js.html +0 -0
- package/docs/lib_factory_model_drop.js.html +0 -0
- package/docs/lib_factory_model_exists.js.html +0 -0
- package/docs/lib_factory_model_find-all-record.js.html +0 -0
- package/docs/lib_factory_model_find-one-record.js.html +0 -0
- package/docs/lib_factory_model_find-record.js.html +0 -0
- package/docs/lib_factory_model_get-record.js.html +0 -0
- package/docs/lib_factory_model_helper.js.html +0 -0
- package/docs/lib_factory_model_remove-record.js.html +0 -0
- package/docs/lib_factory_model_sanitize-body.js.html +0 -0
- package/docs/lib_factory_model_sanitize-fixture.js.html +0 -0
- package/docs/lib_factory_model_sanitize-record.js.html +0 -0
- package/docs/lib_factory_model_update-record.js.html +0 -0
- package/docs/lib_factory_model_upsert-record.js.html +0 -0
- package/docs/lib_factory_model_validate.js.html +0 -0
- package/docs/lib_helper.js.html +0 -0
- package/docs/module-Helper.html +0 -0
- package/docs/module-Helper_Model.html +0 -0
- package/docs/module-Hook.html +0 -0
- package/docs/scripts/core.js +0 -0
- package/docs/scripts/core.min.js +0 -0
- package/docs/scripts/resize.js +0 -0
- package/docs/scripts/search.js +0 -0
- package/docs/scripts/search.min.js +5 -5
- package/docs/scripts/third-party/Apache-License-2.0.txt +0 -0
- package/docs/scripts/third-party/fuse.js +0 -0
- package/docs/scripts/third-party/hljs-line-num-original.js +0 -0
- package/docs/scripts/third-party/hljs-line-num.js +0 -0
- package/docs/scripts/third-party/hljs-original.js +0 -0
- package/docs/scripts/third-party/hljs.js +0 -0
- package/docs/scripts/third-party/popper.js +0 -0
- package/docs/scripts/third-party/tippy.js +0 -0
- package/docs/scripts/third-party/tocbot.js +0 -0
- package/docs/scripts/third-party/tocbot.min.js +0 -0
- package/docs/static/bitcoin.jpeg +0 -0
- package/docs/static/home.md +0 -0
- package/docs/static/logo-ecosystem.png +0 -0
- package/docs/static/logo.png +0 -0
- package/docs/styles/clean-jsdoc-theme-base.css +0 -0
- package/docs/styles/clean-jsdoc-theme-dark.css +0 -0
- package/docs/styles/clean-jsdoc-theme-light.css +0 -0
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -0
- package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
- package/docs/styles/clean-jsdoc-theme.min.css +0 -0
- package/extend/bajo/hook.js +236 -236
- package/extend/bajo/intl/en-US.json +0 -0
- package/extend/bajo/intl/id.json +0 -0
- package/extend/bajoCli/applet/aggregate.js +0 -0
- package/extend/bajoCli/applet/clear-record.js +0 -0
- package/extend/bajoCli/applet/connection.js +0 -0
- package/extend/bajoCli/applet/count-record.js +0 -0
- package/extend/bajoCli/applet/create-record.js +0 -0
- package/extend/bajoCli/applet/find-record.js +0 -0
- package/extend/bajoCli/applet/get-record.js +0 -0
- package/extend/bajoCli/applet/histogram.js +0 -0
- package/extend/bajoCli/applet/lib/post-process.js +0 -0
- package/extend/bajoCli/applet/model.js +0 -0
- package/extend/bajoCli/applet/rebuild-model.js +0 -0
- package/extend/bajoCli/applet/remove-record.js +0 -0
- package/extend/bajoCli/applet/update-record.js +0 -0
- package/extend/bajoCli/applet.js +0 -0
- package/extend/dobo/adapter/memory.js +175 -175
- package/extend/dobo/feature/created-at.js +0 -0
- package/extend/dobo/feature/dt.js +0 -0
- package/extend/dobo/feature/image.js +0 -0
- package/extend/dobo/feature/immutable.js +0 -0
- package/extend/dobo/feature/removed-at.js +0 -0
- package/extend/dobo/feature/unique.js +0 -0
- package/extend/dobo/feature/updated-at.js +0 -0
- package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +0 -0
- package/extend/waibuStatic/virtual.json +0 -0
- package/index.js +0 -0
- package/lib/factory/action.js +287 -287
- package/lib/factory/adapter.js +1055 -1055
- package/lib/factory/connection.js +109 -109
- package/lib/factory/feature.js +52 -52
- package/lib/factory/model/aggregate.js +23 -23
- package/lib/factory/model/build.js +25 -25
- package/lib/factory/model/bulk-create-record.js +36 -36
- package/lib/factory/model/clear-record.js +30 -30
- package/lib/factory/model/count-record.js +42 -42
- package/lib/factory/model/create-attachment.js +36 -36
- package/lib/factory/model/create-record.js +49 -49
- package/lib/factory/model/drop.js +26 -26
- package/lib/factory/model/exists.js +26 -26
- package/lib/factory/model/find-all-record.js +112 -112
- package/lib/factory/model/find-attachment.js +28 -28
- package/lib/factory/model/find-one-record.js +38 -38
- package/lib/factory/model/find-record.js +82 -82
- package/lib/factory/model/get-attachment.js +15 -15
- package/lib/factory/model/get-record.js +52 -52
- package/lib/factory/model/helper.js +575 -576
- package/lib/factory/model/histogram.js +23 -23
- package/lib/factory/model/list-attachment.js +40 -40
- package/lib/factory/model/load-fixtures.js +63 -63
- package/lib/factory/model/remove-attachment.js +24 -24
- package/lib/factory/model/remove-record.js +44 -44
- package/lib/factory/model/sanitize-body.js +57 -57
- package/lib/factory/model/sanitize-fixture.js +56 -56
- package/lib/factory/model/sanitize-record.js +80 -80
- package/lib/factory/model/transaction.js +15 -15
- package/lib/factory/model/update-attachment.js +9 -9
- package/lib/factory/model/update-record.js +58 -58
- package/lib/factory/model/upsert-record.js +71 -71
- package/lib/factory/model/validate.js +0 -0
- package/lib/factory/model.js +503 -503
- package/lib/helper.js +463 -463
- package/package.json +1 -1
- package/test/e2e/_run.js +12 -12
- package/test/e2e/e2e-factory-process.test.js +25 -25
- package/test/e2e/e2e-model-adapter-process.test.js +52 -52
- package/test/integration/aspect-01-start-connections.test.js +33 -33
- package/test/integration/aspect-02-query-regex.test.js +34 -34
- package/test/integration/aspect-03-model-adapter-flow.test.js +88 -88
- package/test/unit/_stub.js +244 -244
- package/test/unit/dobo-action.test.js +58 -58
- package/test/unit/dobo-connection.test.js +37 -37
- package/test/unit/dobo-core.test.js +136 -136
- package/test/unit/dobo-feature.test.js +25 -25
- package/test/unit/dobo-model.test.js +115 -115
- package/test/unit/helper-sanitize-all.test.js +40 -40
- package/test/unit/helper-sanitize-ref.test.js +64 -64
- package/wiki/APPLETS.md +0 -0
- package/wiki/CHANGES.md +477 -469
- package/wiki/CONFIG.md +0 -0
- package/wiki/CONTRIBUTING.md +0 -0
- package/wiki/DEV-GUIDE.md +0 -0
- package/wiki/ECOSYSTEM.md +0 -0
- package/wiki/GETTING-STARTED.md +0 -0
- package/wiki/QUERY-LANGUAGE.md +0 -0
- package/wiki/USER-GUIDE.md +0 -0
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
/* global describe, it, beforeEach */
|
|
2
|
-
|
|
3
|
-
import { expect } from 'chai'
|
|
4
|
-
import connectionFactory from '../../lib/factory/connection.js'
|
|
5
|
-
import { createAppStub } from './_stub.js'
|
|
6
|
-
|
|
7
|
-
describe('dobo connection class (unit)', () => {
|
|
8
|
-
let app
|
|
9
|
-
let plugin
|
|
10
|
-
|
|
11
|
-
beforeEach(async () => {
|
|
12
|
-
app = createAppStub('/tmp/dobo-connection')
|
|
13
|
-
app.baseClass.DoboNullAdapter = class DoboNullAdapter {}
|
|
14
|
-
app.baseClass.DoboAdapter = class DoboAdapter {}
|
|
15
|
-
plugin = {
|
|
16
|
-
app,
|
|
17
|
-
getAdapter: () => ({ sanitizeConnection: async () => {}, connect: async () => ({ client: true }) })
|
|
18
|
-
}
|
|
19
|
-
await connectionFactory.call(plugin)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('creates DoboConnection, initializes adapter and connects', async () => {
|
|
23
|
-
const conn = new app.baseClass.DoboConnection(plugin, { name: 'default', adapter: 'dobo:memory', models: ['User'] })
|
|
24
|
-
await conn.initAdapter('dobo:memory')
|
|
25
|
-
expect(conn.adapter).to.be.an('object')
|
|
26
|
-
await conn.connect(true)
|
|
27
|
-
expect(conn.connected).to.equal(true)
|
|
28
|
-
expect(conn.client).to.deep.equal({ client: true })
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('dispose clears adapter reference', async () => {
|
|
32
|
-
const conn = new app.baseClass.DoboConnection(plugin, { name: 'default', adapter: 'dobo:memory' })
|
|
33
|
-
conn.adapter = { ok: true }
|
|
34
|
-
await conn.dispose()
|
|
35
|
-
expect(conn.adapter).to.equal(null)
|
|
36
|
-
})
|
|
37
|
-
})
|
|
1
|
+
/* global describe, it, beforeEach */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import connectionFactory from '../../lib/factory/connection.js'
|
|
5
|
+
import { createAppStub } from './_stub.js'
|
|
6
|
+
|
|
7
|
+
describe('dobo connection class (unit)', () => {
|
|
8
|
+
let app
|
|
9
|
+
let plugin
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
app = createAppStub('/tmp/dobo-connection')
|
|
13
|
+
app.baseClass.DoboNullAdapter = class DoboNullAdapter {}
|
|
14
|
+
app.baseClass.DoboAdapter = class DoboAdapter {}
|
|
15
|
+
plugin = {
|
|
16
|
+
app,
|
|
17
|
+
getAdapter: () => ({ sanitizeConnection: async () => {}, connect: async () => ({ client: true }) })
|
|
18
|
+
}
|
|
19
|
+
await connectionFactory.call(plugin)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('creates DoboConnection, initializes adapter and connects', async () => {
|
|
23
|
+
const conn = new app.baseClass.DoboConnection(plugin, { name: 'default', adapter: 'dobo:memory', models: ['User'] })
|
|
24
|
+
await conn.initAdapter('dobo:memory')
|
|
25
|
+
expect(conn.adapter).to.be.an('object')
|
|
26
|
+
await conn.connect(true)
|
|
27
|
+
expect(conn.connected).to.equal(true)
|
|
28
|
+
expect(conn.client).to.deep.equal({ client: true })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('dispose clears adapter reference', async () => {
|
|
32
|
+
const conn = new app.baseClass.DoboConnection(plugin, { name: 'default', adapter: 'dobo:memory' })
|
|
33
|
+
conn.adapter = { ok: true }
|
|
34
|
+
await conn.dispose()
|
|
35
|
+
expect(conn.adapter).to.equal(null)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
/* global describe, it, beforeEach */
|
|
2
|
-
|
|
3
|
-
import { expect } from 'chai'
|
|
4
|
-
import factory from '../../index.js'
|
|
5
|
-
import { createAppStub } from './_stub.js'
|
|
6
|
-
|
|
7
|
-
describe('dobo core class (unit)', () => {
|
|
8
|
-
let app
|
|
9
|
-
let Dobo
|
|
10
|
-
let dobo
|
|
11
|
-
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
app = createAppStub('/tmp/dobo-unit')
|
|
14
|
-
Dobo = await factory.call({ app }, 'dobo')
|
|
15
|
-
dobo = new Dobo()
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('creates plugin class with expected static values and defaults', () => {
|
|
19
|
-
expect(Dobo.aggregateTypes).to.include('count')
|
|
20
|
-
expect(Dobo.histogramTypes).to.include('yearly')
|
|
21
|
-
expect(Dobo.idTypes).to.include('string')
|
|
22
|
-
expect(dobo.config.default.filter.limit).to.equal(25)
|
|
23
|
-
expect(dobo.adapters).to.deep.equal([])
|
|
24
|
-
expect(dobo.connections).to.deep.equal([])
|
|
25
|
-
expect(dobo.features).to.deep.equal([])
|
|
26
|
-
expect(dobo.models).to.deep.equal([])
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('property key helpers include type-specific fields', () => {
|
|
30
|
-
expect(dobo.getPropertyKeysByType('string')).to.include('maxLength')
|
|
31
|
-
expect(dobo.getPropertyKeysByType('text')).to.include('textType')
|
|
32
|
-
expect(dobo.getPropertyKeysByType('smallint')).to.include('autoInc')
|
|
33
|
-
|
|
34
|
-
const keys = dobo.getAllPropertyKeys({ constructor: { propertyKeys: ['xFlag'] } })
|
|
35
|
-
expect(keys).to.include('name')
|
|
36
|
-
expect(keys).to.include('validator')
|
|
37
|
-
expect(keys).to.include('xFlag')
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('connection, adapter, feature and model lookups work', () => {
|
|
41
|
-
dobo.connections = [{ name: 'default' }, { name: 'report' }]
|
|
42
|
-
expect(dobo.getConnection('report').name).to.equal('report')
|
|
43
|
-
expect(dobo.getConnection('none', true)).to.equal(undefined)
|
|
44
|
-
|
|
45
|
-
dobo.adapters = [
|
|
46
|
-
{ name: 'memory', plugin: { ns: 'dobo' } },
|
|
47
|
-
{ name: 'proxy', plugin: { ns: 'ext' } }
|
|
48
|
-
]
|
|
49
|
-
expect(dobo.getAdapter('memory').name).to.equal('memory')
|
|
50
|
-
expect(dobo.getAdapter('ext:proxy').name).to.equal('proxy')
|
|
51
|
-
expect(dobo.getAdapter('missing', true)).to.equal(undefined)
|
|
52
|
-
|
|
53
|
-
dobo.features = [{ name: 'createdAt', plugin: { ns: 'dobo' } }]
|
|
54
|
-
expect(dobo.getFeature('createdAt').name).to.equal('createdAt')
|
|
55
|
-
|
|
56
|
-
dobo.models = [{ name: 'Order' }, { name: 'Customer' }]
|
|
57
|
-
expect(dobo.getModel('Order').name).to.equal('Order')
|
|
58
|
-
expect(dobo.getModel('order').name).to.equal('Order')
|
|
59
|
-
expect(dobo.getModel('none', true)).to.equal(undefined)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('sanitize helpers and aggregate calculators work', () => {
|
|
63
|
-
expect(dobo.sanitizeBoolean('true')).to.equal(true)
|
|
64
|
-
expect(dobo.sanitizeBoolean(false)).to.equal(false)
|
|
65
|
-
expect(dobo.sanitizeFloat('12.4')).to.equal(12.4)
|
|
66
|
-
expect(dobo.sanitizeInt('12')).to.equal(12)
|
|
67
|
-
expect(dobo.sanitizeString(44)).to.equal('44')
|
|
68
|
-
expect(dobo.sanitizeObject('{"a":1}')).to.deep.equal({ a: 1 })
|
|
69
|
-
|
|
70
|
-
const rows = [
|
|
71
|
-
{ grp: 'a', price: 10 },
|
|
72
|
-
{ grp: 'a', price: 30 },
|
|
73
|
-
{ grp: 'b', price: 20 }
|
|
74
|
-
]
|
|
75
|
-
const agg = dobo.calcAggregate({ data: rows, group: 'grp', field: 'price', aggregates: ['count', 'avg', 'min', 'max'] })
|
|
76
|
-
expect(agg).to.have.length(2)
|
|
77
|
-
const itemA = agg.find(i => i.grp === 'a')
|
|
78
|
-
expect(itemA.count).to.equal(2)
|
|
79
|
-
expect(itemA.max).to.equal(30)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('histogram and query parsers work with valid input', () => {
|
|
83
|
-
const rows = [
|
|
84
|
-
{ createdAt: '2026-01-01T00:00:00Z', score: 5 },
|
|
85
|
-
{ createdAt: '2026-01-01T12:00:00Z', score: 7 },
|
|
86
|
-
{ createdAt: '2026-01-02T00:00:00Z', score: 10 }
|
|
87
|
-
]
|
|
88
|
-
const hist = dobo.calcHistogram({ data: rows, type: 'daily', group: 'createdAt', field: 'score', aggregates: ['count'] })
|
|
89
|
-
expect(hist).to.have.length(2)
|
|
90
|
-
expect(hist[0]).to.have.property('date')
|
|
91
|
-
|
|
92
|
-
const model = {
|
|
93
|
-
scanables: ['name'],
|
|
94
|
-
sortables: ['name'],
|
|
95
|
-
properties: [{ name: 'name', type: 'string' }],
|
|
96
|
-
adapter: { idField: { name: '_id' } }
|
|
97
|
-
}
|
|
98
|
-
const parsed = dobo.parseQuery('name:~\'john\'', model)
|
|
99
|
-
expect(parsed).to.be.an('object')
|
|
100
|
-
|
|
101
|
-
const parsedAny = dobo.parseQuery('john*', model)
|
|
102
|
-
expect(parsedAny).to.be.an('object')
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
it('regex json helpers and model hooks work', async () => {
|
|
106
|
-
const input = { n: /abc/gi }
|
|
107
|
-
const text = dobo.replaceRegexInJson(input, true)
|
|
108
|
-
const revived = dobo.reviveRegexInJson(text, true)
|
|
109
|
-
expect(revived.n).to.be.instanceOf(RegExp)
|
|
110
|
-
expect(revived.n.flags).to.include('g')
|
|
111
|
-
|
|
112
|
-
const calls = []
|
|
113
|
-
const model = {
|
|
114
|
-
hooks: [
|
|
115
|
-
{ name: 'beforeCreateRecord', level: 2, handler: async () => calls.push(2) },
|
|
116
|
-
{ name: 'beforeCreateRecord', level: 1, handler: async () => calls.push(1) }
|
|
117
|
-
]
|
|
118
|
-
}
|
|
119
|
-
await dobo.runModelHook(model, 'beforeCreateRecord', {})
|
|
120
|
-
expect(calls).to.deep.equal([1, 2])
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('default values and hard-cap last page handler work', () => {
|
|
124
|
-
dobo.config.default.filter = { limit: 10, maxLimit: 50, maxPage: 30, sort: [] }
|
|
125
|
-
dobo.config.default.hardCap = 100
|
|
126
|
-
dobo.config.default.warnings = true
|
|
127
|
-
|
|
128
|
-
const def = dobo.getDefaultValues()
|
|
129
|
-
expect(def.limit).to.equal(10)
|
|
130
|
-
expect(def.hardCap).to.equal(100)
|
|
131
|
-
|
|
132
|
-
const result = dobo.handleLastPage({ count: 300, limit: 10, page: 25 })
|
|
133
|
-
expect(result.count).to.equal(100)
|
|
134
|
-
expect(result.data).to.deep.equal([])
|
|
135
|
-
})
|
|
136
|
-
})
|
|
1
|
+
/* global describe, it, beforeEach */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import factory from '../../index.js'
|
|
5
|
+
import { createAppStub } from './_stub.js'
|
|
6
|
+
|
|
7
|
+
describe('dobo core class (unit)', () => {
|
|
8
|
+
let app
|
|
9
|
+
let Dobo
|
|
10
|
+
let dobo
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
app = createAppStub('/tmp/dobo-unit')
|
|
14
|
+
Dobo = await factory.call({ app }, 'dobo')
|
|
15
|
+
dobo = new Dobo()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('creates plugin class with expected static values and defaults', () => {
|
|
19
|
+
expect(Dobo.aggregateTypes).to.include('count')
|
|
20
|
+
expect(Dobo.histogramTypes).to.include('yearly')
|
|
21
|
+
expect(Dobo.idTypes).to.include('string')
|
|
22
|
+
expect(dobo.config.default.filter.limit).to.equal(25)
|
|
23
|
+
expect(dobo.adapters).to.deep.equal([])
|
|
24
|
+
expect(dobo.connections).to.deep.equal([])
|
|
25
|
+
expect(dobo.features).to.deep.equal([])
|
|
26
|
+
expect(dobo.models).to.deep.equal([])
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('property key helpers include type-specific fields', () => {
|
|
30
|
+
expect(dobo.getPropertyKeysByType('string')).to.include('maxLength')
|
|
31
|
+
expect(dobo.getPropertyKeysByType('text')).to.include('textType')
|
|
32
|
+
expect(dobo.getPropertyKeysByType('smallint')).to.include('autoInc')
|
|
33
|
+
|
|
34
|
+
const keys = dobo.getAllPropertyKeys({ constructor: { propertyKeys: ['xFlag'] } })
|
|
35
|
+
expect(keys).to.include('name')
|
|
36
|
+
expect(keys).to.include('validator')
|
|
37
|
+
expect(keys).to.include('xFlag')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('connection, adapter, feature and model lookups work', () => {
|
|
41
|
+
dobo.connections = [{ name: 'default' }, { name: 'report' }]
|
|
42
|
+
expect(dobo.getConnection('report').name).to.equal('report')
|
|
43
|
+
expect(dobo.getConnection('none', true)).to.equal(undefined)
|
|
44
|
+
|
|
45
|
+
dobo.adapters = [
|
|
46
|
+
{ name: 'memory', plugin: { ns: 'dobo' } },
|
|
47
|
+
{ name: 'proxy', plugin: { ns: 'ext' } }
|
|
48
|
+
]
|
|
49
|
+
expect(dobo.getAdapter('memory').name).to.equal('memory')
|
|
50
|
+
expect(dobo.getAdapter('ext:proxy').name).to.equal('proxy')
|
|
51
|
+
expect(dobo.getAdapter('missing', true)).to.equal(undefined)
|
|
52
|
+
|
|
53
|
+
dobo.features = [{ name: 'createdAt', plugin: { ns: 'dobo' } }]
|
|
54
|
+
expect(dobo.getFeature('createdAt').name).to.equal('createdAt')
|
|
55
|
+
|
|
56
|
+
dobo.models = [{ name: 'Order' }, { name: 'Customer' }]
|
|
57
|
+
expect(dobo.getModel('Order').name).to.equal('Order')
|
|
58
|
+
expect(dobo.getModel('order').name).to.equal('Order')
|
|
59
|
+
expect(dobo.getModel('none', true)).to.equal(undefined)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('sanitize helpers and aggregate calculators work', () => {
|
|
63
|
+
expect(dobo.sanitizeBoolean('true')).to.equal(true)
|
|
64
|
+
expect(dobo.sanitizeBoolean(false)).to.equal(false)
|
|
65
|
+
expect(dobo.sanitizeFloat('12.4')).to.equal(12.4)
|
|
66
|
+
expect(dobo.sanitizeInt('12')).to.equal(12)
|
|
67
|
+
expect(dobo.sanitizeString(44)).to.equal('44')
|
|
68
|
+
expect(dobo.sanitizeObject('{"a":1}')).to.deep.equal({ a: 1 })
|
|
69
|
+
|
|
70
|
+
const rows = [
|
|
71
|
+
{ grp: 'a', price: 10 },
|
|
72
|
+
{ grp: 'a', price: 30 },
|
|
73
|
+
{ grp: 'b', price: 20 }
|
|
74
|
+
]
|
|
75
|
+
const agg = dobo.calcAggregate({ data: rows, group: 'grp', field: 'price', aggregates: ['count', 'avg', 'min', 'max'] })
|
|
76
|
+
expect(agg).to.have.length(2)
|
|
77
|
+
const itemA = agg.find(i => i.grp === 'a')
|
|
78
|
+
expect(itemA.count).to.equal(2)
|
|
79
|
+
expect(itemA.max).to.equal(30)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('histogram and query parsers work with valid input', () => {
|
|
83
|
+
const rows = [
|
|
84
|
+
{ createdAt: '2026-01-01T00:00:00Z', score: 5 },
|
|
85
|
+
{ createdAt: '2026-01-01T12:00:00Z', score: 7 },
|
|
86
|
+
{ createdAt: '2026-01-02T00:00:00Z', score: 10 }
|
|
87
|
+
]
|
|
88
|
+
const hist = dobo.calcHistogram({ data: rows, type: 'daily', group: 'createdAt', field: 'score', aggregates: ['count'] })
|
|
89
|
+
expect(hist).to.have.length(2)
|
|
90
|
+
expect(hist[0]).to.have.property('date')
|
|
91
|
+
|
|
92
|
+
const model = {
|
|
93
|
+
scanables: ['name'],
|
|
94
|
+
sortables: ['name'],
|
|
95
|
+
properties: [{ name: 'name', type: 'string' }],
|
|
96
|
+
adapter: { idField: { name: '_id' } }
|
|
97
|
+
}
|
|
98
|
+
const parsed = dobo.parseQuery('name:~\'john\'', model)
|
|
99
|
+
expect(parsed).to.be.an('object')
|
|
100
|
+
|
|
101
|
+
const parsedAny = dobo.parseQuery('john*', model)
|
|
102
|
+
expect(parsedAny).to.be.an('object')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('regex json helpers and model hooks work', async () => {
|
|
106
|
+
const input = { n: /abc/gi }
|
|
107
|
+
const text = dobo.replaceRegexInJson(input, true)
|
|
108
|
+
const revived = dobo.reviveRegexInJson(text, true)
|
|
109
|
+
expect(revived.n).to.be.instanceOf(RegExp)
|
|
110
|
+
expect(revived.n.flags).to.include('g')
|
|
111
|
+
|
|
112
|
+
const calls = []
|
|
113
|
+
const model = {
|
|
114
|
+
hooks: [
|
|
115
|
+
{ name: 'beforeCreateRecord', level: 2, handler: async () => calls.push(2) },
|
|
116
|
+
{ name: 'beforeCreateRecord', level: 1, handler: async () => calls.push(1) }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
await dobo.runModelHook(model, 'beforeCreateRecord', {})
|
|
120
|
+
expect(calls).to.deep.equal([1, 2])
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('default values and hard-cap last page handler work', () => {
|
|
124
|
+
dobo.config.default.filter = { limit: 10, maxLimit: 50, maxPage: 30, sort: [] }
|
|
125
|
+
dobo.config.default.hardCap = 100
|
|
126
|
+
dobo.config.default.warnings = true
|
|
127
|
+
|
|
128
|
+
const def = dobo.getDefaultValues()
|
|
129
|
+
expect(def.limit).to.equal(10)
|
|
130
|
+
expect(def.hardCap).to.equal(100)
|
|
131
|
+
|
|
132
|
+
const result = dobo.handleLastPage({ count: 300, limit: 10, page: 25 })
|
|
133
|
+
expect(result.count).to.equal(100)
|
|
134
|
+
expect(result.data).to.deep.equal([])
|
|
135
|
+
})
|
|
136
|
+
})
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
/* global describe, it, beforeEach */
|
|
2
|
-
|
|
3
|
-
import { expect } from 'chai'
|
|
4
|
-
import featureFactory from '../../lib/factory/feature.js'
|
|
5
|
-
import { createAppStub } from './_stub.js'
|
|
6
|
-
|
|
7
|
-
describe('dobo feature class (unit)', () => {
|
|
8
|
-
let app
|
|
9
|
-
let plugin
|
|
10
|
-
|
|
11
|
-
beforeEach(async () => {
|
|
12
|
-
app = createAppStub('/tmp/dobo-feature')
|
|
13
|
-
plugin = { app }
|
|
14
|
-
await featureFactory.call(plugin)
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('creates DoboFeature and disposes cleanly', async () => {
|
|
18
|
-
const feat = new app.baseClass.DoboFeature(plugin, { name: 'createdAt', handler: async () => ({}) })
|
|
19
|
-
expect(feat.name).to.equal('createdAt')
|
|
20
|
-
expect(feat.handler).to.be.a('function')
|
|
21
|
-
await feat.dispose()
|
|
22
|
-
expect(feat.name).to.equal(null)
|
|
23
|
-
expect(feat.handler).to.equal(null)
|
|
24
|
-
})
|
|
25
|
-
})
|
|
1
|
+
/* global describe, it, beforeEach */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import featureFactory from '../../lib/factory/feature.js'
|
|
5
|
+
import { createAppStub } from './_stub.js'
|
|
6
|
+
|
|
7
|
+
describe('dobo feature class (unit)', () => {
|
|
8
|
+
let app
|
|
9
|
+
let plugin
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
app = createAppStub('/tmp/dobo-feature')
|
|
13
|
+
plugin = { app }
|
|
14
|
+
await featureFactory.call(plugin)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('creates DoboFeature and disposes cleanly', async () => {
|
|
18
|
+
const feat = new app.baseClass.DoboFeature(plugin, { name: 'createdAt', handler: async () => ({}) })
|
|
19
|
+
expect(feat.name).to.equal('createdAt')
|
|
20
|
+
expect(feat.handler).to.be.a('function')
|
|
21
|
+
await feat.dispose()
|
|
22
|
+
expect(feat.name).to.equal(null)
|
|
23
|
+
expect(feat.handler).to.equal(null)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
/* global describe, it, beforeEach */
|
|
2
|
-
|
|
3
|
-
import { expect } from 'chai'
|
|
4
|
-
import modelFactory from '../../lib/factory/model.js'
|
|
5
|
-
import { createAppStub, Base } from './_stub.js'
|
|
6
|
-
|
|
7
|
-
describe('dobo model class (unit)', () => {
|
|
8
|
-
let app
|
|
9
|
-
let plugin
|
|
10
|
-
let model
|
|
11
|
-
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
app = createAppStub('/tmp/dobo-model-unit')
|
|
14
|
-
app.dobo = {
|
|
15
|
-
models: [],
|
|
16
|
-
sanitizeDate: (v) => v,
|
|
17
|
-
sanitizeByType: (v) => v,
|
|
18
|
-
checkAggregateParams: () => {},
|
|
19
|
-
checkHistogramParams: () => {},
|
|
20
|
-
runModelHook: async () => {},
|
|
21
|
-
parseQuery: (q) => q
|
|
22
|
-
}
|
|
23
|
-
app.baseClass.DoboAction = class DoboAction {
|
|
24
|
-
constructor (m, name, ...args) {
|
|
25
|
-
this.model = m
|
|
26
|
-
this.name = name
|
|
27
|
-
this.args = args
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
plugin = new Base('dobo', app)
|
|
32
|
-
await modelFactory.call(plugin)
|
|
33
|
-
|
|
34
|
-
const options = {
|
|
35
|
-
name: 'Order',
|
|
36
|
-
baseName: 'order',
|
|
37
|
-
connection: {
|
|
38
|
-
name: 'default',
|
|
39
|
-
adapter: { idField: { name: '_id', type: 'string', maxLength: 50 } }
|
|
40
|
-
},
|
|
41
|
-
properties: [
|
|
42
|
-
{ name: 'id', type: 'string', maxLength: 50 },
|
|
43
|
-
{ name: 'title', type: 'string', maxLength: 50 },
|
|
44
|
-
{ name: 'total', type: 'double' },
|
|
45
|
-
{ name: 'virtualTitle', type: 'string', virtual: true }
|
|
46
|
-
],
|
|
47
|
-
indexes: [{ name: 'idx_title', type: 'index', fields: ['title'] }],
|
|
48
|
-
hooks: []
|
|
49
|
-
}
|
|
50
|
-
model = new app.baseClass.DoboModel(plugin, options)
|
|
51
|
-
app.dobo.models.push(model)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('builds aliases, action and property getters correctly', () => {
|
|
55
|
-
const act = model.action('getRecord', 'id-1')
|
|
56
|
-
expect(act.name).to.equal('getRecord')
|
|
57
|
-
expect(model.getProperty('title').type).to.equal('string')
|
|
58
|
-
expect(model.getField('title').name).to.equal('title')
|
|
59
|
-
expect(model.hasProperty('title')).to.equal(true)
|
|
60
|
-
expect(model.hasField('ghost')).to.equal(false)
|
|
61
|
-
expect(model.getIndexes()).to.have.length(1)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('returns filtered property lists (all, virtual, non-virtual)', () => {
|
|
65
|
-
expect(model.getProperties().length).to.equal(4)
|
|
66
|
-
expect(model.getProperties({ noVirtual: true }).length).to.equal(3)
|
|
67
|
-
expect(model.getProperties({ noVirtual: true, namesOnly: true })).to.include('title')
|
|
68
|
-
expect(model.getVirtualProperties(true)).to.deep.equal(['virtualTitle'])
|
|
69
|
-
expect(model.getNonVirtualProperties(true)).to.include('id')
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('sanitizes and syncs id field', () => {
|
|
73
|
-
model.properties[0].type = 'integer'
|
|
74
|
-
expect(model.sanitizeId('12')).to.equal(12)
|
|
75
|
-
|
|
76
|
-
model.syncIdField({ name: 'id', type: 'string', maxLength: 20 })
|
|
77
|
-
expect(model.properties[0].maxLength).to.equal(20)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('buildPropValues supports array and req translation hooks', async () => {
|
|
81
|
-
const prop = {
|
|
82
|
-
name: 'status',
|
|
83
|
-
values: ['draft', { value: 'done', text: 'done' }]
|
|
84
|
-
}
|
|
85
|
-
const req = {
|
|
86
|
-
te: (k) => k === 'statusDone',
|
|
87
|
-
t: () => 'Selesai'
|
|
88
|
-
}
|
|
89
|
-
const vals = await model.buildPropValues(prop, { req })
|
|
90
|
-
expect(vals).to.have.length(2)
|
|
91
|
-
expect(vals[1].text).to.equal('Selesai')
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('_simpleLookup handles string/object/array definitions', async () => {
|
|
95
|
-
const userModel = {
|
|
96
|
-
findOneRecord: async () => ({ id: 'u1', username: 'john' })
|
|
97
|
-
}
|
|
98
|
-
app.dobo.getModel = () => userModel
|
|
99
|
-
|
|
100
|
-
const a = await model._simpleLookup('User:username:id=u1', {})
|
|
101
|
-
expect(a).to.equal('john')
|
|
102
|
-
|
|
103
|
-
const b = await model._simpleLookup({ model: 'User', field: 'id', query: 'id={id}' }, { id: 'u9' })
|
|
104
|
-
expect(b).to.equal('u1')
|
|
105
|
-
|
|
106
|
-
const c = await model._simpleLookup(['User', 'id', 'id=u5'], {})
|
|
107
|
-
expect(c).to.equal('u1')
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('dispose clears connection and adapter references', async () => {
|
|
111
|
-
await model.dispose()
|
|
112
|
-
expect(model.connection).to.equal(null)
|
|
113
|
-
expect(model.adapter).to.equal(null)
|
|
114
|
-
})
|
|
115
|
-
})
|
|
1
|
+
/* global describe, it, beforeEach */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import modelFactory from '../../lib/factory/model.js'
|
|
5
|
+
import { createAppStub, Base } from './_stub.js'
|
|
6
|
+
|
|
7
|
+
describe('dobo model class (unit)', () => {
|
|
8
|
+
let app
|
|
9
|
+
let plugin
|
|
10
|
+
let model
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
app = createAppStub('/tmp/dobo-model-unit')
|
|
14
|
+
app.dobo = {
|
|
15
|
+
models: [],
|
|
16
|
+
sanitizeDate: (v) => v,
|
|
17
|
+
sanitizeByType: (v) => v,
|
|
18
|
+
checkAggregateParams: () => {},
|
|
19
|
+
checkHistogramParams: () => {},
|
|
20
|
+
runModelHook: async () => {},
|
|
21
|
+
parseQuery: (q) => q
|
|
22
|
+
}
|
|
23
|
+
app.baseClass.DoboAction = class DoboAction {
|
|
24
|
+
constructor (m, name, ...args) {
|
|
25
|
+
this.model = m
|
|
26
|
+
this.name = name
|
|
27
|
+
this.args = args
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
plugin = new Base('dobo', app)
|
|
32
|
+
await modelFactory.call(plugin)
|
|
33
|
+
|
|
34
|
+
const options = {
|
|
35
|
+
name: 'Order',
|
|
36
|
+
baseName: 'order',
|
|
37
|
+
connection: {
|
|
38
|
+
name: 'default',
|
|
39
|
+
adapter: { idField: { name: '_id', type: 'string', maxLength: 50 } }
|
|
40
|
+
},
|
|
41
|
+
properties: [
|
|
42
|
+
{ name: 'id', type: 'string', maxLength: 50 },
|
|
43
|
+
{ name: 'title', type: 'string', maxLength: 50 },
|
|
44
|
+
{ name: 'total', type: 'double' },
|
|
45
|
+
{ name: 'virtualTitle', type: 'string', virtual: true }
|
|
46
|
+
],
|
|
47
|
+
indexes: [{ name: 'idx_title', type: 'index', fields: ['title'] }],
|
|
48
|
+
hooks: []
|
|
49
|
+
}
|
|
50
|
+
model = new app.baseClass.DoboModel(plugin, options)
|
|
51
|
+
app.dobo.models.push(model)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('builds aliases, action and property getters correctly', () => {
|
|
55
|
+
const act = model.action('getRecord', 'id-1')
|
|
56
|
+
expect(act.name).to.equal('getRecord')
|
|
57
|
+
expect(model.getProperty('title').type).to.equal('string')
|
|
58
|
+
expect(model.getField('title').name).to.equal('title')
|
|
59
|
+
expect(model.hasProperty('title')).to.equal(true)
|
|
60
|
+
expect(model.hasField('ghost')).to.equal(false)
|
|
61
|
+
expect(model.getIndexes()).to.have.length(1)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('returns filtered property lists (all, virtual, non-virtual)', () => {
|
|
65
|
+
expect(model.getProperties().length).to.equal(4)
|
|
66
|
+
expect(model.getProperties({ noVirtual: true }).length).to.equal(3)
|
|
67
|
+
expect(model.getProperties({ noVirtual: true, namesOnly: true })).to.include('title')
|
|
68
|
+
expect(model.getVirtualProperties(true)).to.deep.equal(['virtualTitle'])
|
|
69
|
+
expect(model.getNonVirtualProperties(true)).to.include('id')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('sanitizes and syncs id field', () => {
|
|
73
|
+
model.properties[0].type = 'integer'
|
|
74
|
+
expect(model.sanitizeId('12')).to.equal(12)
|
|
75
|
+
|
|
76
|
+
model.syncIdField({ name: 'id', type: 'string', maxLength: 20 })
|
|
77
|
+
expect(model.properties[0].maxLength).to.equal(20)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('buildPropValues supports array and req translation hooks', async () => {
|
|
81
|
+
const prop = {
|
|
82
|
+
name: 'status',
|
|
83
|
+
values: ['draft', { value: 'done', text: 'done' }]
|
|
84
|
+
}
|
|
85
|
+
const req = {
|
|
86
|
+
te: (k) => k === 'statusDone',
|
|
87
|
+
t: () => 'Selesai'
|
|
88
|
+
}
|
|
89
|
+
const vals = await model.buildPropValues(prop, { req })
|
|
90
|
+
expect(vals).to.have.length(2)
|
|
91
|
+
expect(vals[1].text).to.equal('Selesai')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('_simpleLookup handles string/object/array definitions', async () => {
|
|
95
|
+
const userModel = {
|
|
96
|
+
findOneRecord: async () => ({ id: 'u1', username: 'john' })
|
|
97
|
+
}
|
|
98
|
+
app.dobo.getModel = () => userModel
|
|
99
|
+
|
|
100
|
+
const a = await model._simpleLookup('User:username:id=u1', {})
|
|
101
|
+
expect(a).to.equal('john')
|
|
102
|
+
|
|
103
|
+
const b = await model._simpleLookup({ model: 'User', field: 'id', query: 'id={id}' }, { id: 'u9' })
|
|
104
|
+
expect(b).to.equal('u1')
|
|
105
|
+
|
|
106
|
+
const c = await model._simpleLookup(['User', 'id', 'id=u5'], {})
|
|
107
|
+
expect(c).to.equal('u1')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('dispose clears connection and adapter references', async () => {
|
|
111
|
+
await model.dispose()
|
|
112
|
+
expect(model.connection).to.equal(null)
|
|
113
|
+
expect(model.adapter).to.equal(null)
|
|
114
|
+
})
|
|
115
|
+
})
|