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.
Files changed (160) hide show
  1. package/.bootorder +0 -0
  2. package/.github/FUNDING.yml +0 -0
  3. package/.github/workflows/repo-lockdown.yml +0 -0
  4. package/.jsdoc.conf.json +0 -0
  5. package/LICENSE +0 -0
  6. package/README.md +0 -0
  7. package/docs/Dobo.html +0 -0
  8. package/docs/DoboAction.html +0 -0
  9. package/docs/DoboAdapter.html +0 -0
  10. package/docs/DoboConnection.html +0 -0
  11. package/docs/DoboFeature.html +0 -0
  12. package/docs/DoboModel.html +0 -0
  13. package/docs/data/search.json +0 -0
  14. package/docs/extend_bajo_hook-docs.js.html +0 -0
  15. package/docs/external-Tools.html +0 -0
  16. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  17. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  18. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  19. package/docs/global.html +0 -0
  20. package/docs/index.html +0 -0
  21. package/docs/index.js.html +0 -0
  22. package/docs/lib_factory_action.js.html +0 -0
  23. package/docs/lib_factory_adapter.js.html +0 -0
  24. package/docs/lib_factory_connection.js.html +0 -0
  25. package/docs/lib_factory_feature.js.html +0 -0
  26. package/docs/lib_factory_model.js.html +0 -0
  27. package/docs/lib_factory_model_build.js.html +0 -0
  28. package/docs/lib_factory_model_clear-record.js.html +0 -0
  29. package/docs/lib_factory_model_count-record.js.html +0 -0
  30. package/docs/lib_factory_model_create-record.js.html +0 -0
  31. package/docs/lib_factory_model_drop.js.html +0 -0
  32. package/docs/lib_factory_model_exists.js.html +0 -0
  33. package/docs/lib_factory_model_find-all-record.js.html +0 -0
  34. package/docs/lib_factory_model_find-one-record.js.html +0 -0
  35. package/docs/lib_factory_model_find-record.js.html +0 -0
  36. package/docs/lib_factory_model_get-record.js.html +0 -0
  37. package/docs/lib_factory_model_helper.js.html +0 -0
  38. package/docs/lib_factory_model_remove-record.js.html +0 -0
  39. package/docs/lib_factory_model_sanitize-body.js.html +0 -0
  40. package/docs/lib_factory_model_sanitize-fixture.js.html +0 -0
  41. package/docs/lib_factory_model_sanitize-record.js.html +0 -0
  42. package/docs/lib_factory_model_update-record.js.html +0 -0
  43. package/docs/lib_factory_model_upsert-record.js.html +0 -0
  44. package/docs/lib_factory_model_validate.js.html +0 -0
  45. package/docs/lib_helper.js.html +0 -0
  46. package/docs/module-Helper.html +0 -0
  47. package/docs/module-Helper_Model.html +0 -0
  48. package/docs/module-Hook.html +0 -0
  49. package/docs/scripts/core.js +0 -0
  50. package/docs/scripts/core.min.js +0 -0
  51. package/docs/scripts/resize.js +0 -0
  52. package/docs/scripts/search.js +0 -0
  53. package/docs/scripts/search.min.js +5 -5
  54. package/docs/scripts/third-party/Apache-License-2.0.txt +0 -0
  55. package/docs/scripts/third-party/fuse.js +0 -0
  56. package/docs/scripts/third-party/hljs-line-num-original.js +0 -0
  57. package/docs/scripts/third-party/hljs-line-num.js +0 -0
  58. package/docs/scripts/third-party/hljs-original.js +0 -0
  59. package/docs/scripts/third-party/hljs.js +0 -0
  60. package/docs/scripts/third-party/popper.js +0 -0
  61. package/docs/scripts/third-party/tippy.js +0 -0
  62. package/docs/scripts/third-party/tocbot.js +0 -0
  63. package/docs/scripts/third-party/tocbot.min.js +0 -0
  64. package/docs/static/bitcoin.jpeg +0 -0
  65. package/docs/static/home.md +0 -0
  66. package/docs/static/logo-ecosystem.png +0 -0
  67. package/docs/static/logo.png +0 -0
  68. package/docs/styles/clean-jsdoc-theme-base.css +0 -0
  69. package/docs/styles/clean-jsdoc-theme-dark.css +0 -0
  70. package/docs/styles/clean-jsdoc-theme-light.css +0 -0
  71. package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -0
  72. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
  73. package/docs/styles/clean-jsdoc-theme.min.css +0 -0
  74. package/extend/bajo/hook.js +236 -236
  75. package/extend/bajo/intl/en-US.json +0 -0
  76. package/extend/bajo/intl/id.json +0 -0
  77. package/extend/bajoCli/applet/aggregate.js +0 -0
  78. package/extend/bajoCli/applet/clear-record.js +0 -0
  79. package/extend/bajoCli/applet/connection.js +0 -0
  80. package/extend/bajoCli/applet/count-record.js +0 -0
  81. package/extend/bajoCli/applet/create-record.js +0 -0
  82. package/extend/bajoCli/applet/find-record.js +0 -0
  83. package/extend/bajoCli/applet/get-record.js +0 -0
  84. package/extend/bajoCli/applet/histogram.js +0 -0
  85. package/extend/bajoCli/applet/lib/post-process.js +0 -0
  86. package/extend/bajoCli/applet/model.js +0 -0
  87. package/extend/bajoCli/applet/rebuild-model.js +0 -0
  88. package/extend/bajoCli/applet/remove-record.js +0 -0
  89. package/extend/bajoCli/applet/update-record.js +0 -0
  90. package/extend/bajoCli/applet.js +0 -0
  91. package/extend/dobo/adapter/memory.js +175 -175
  92. package/extend/dobo/feature/created-at.js +0 -0
  93. package/extend/dobo/feature/dt.js +0 -0
  94. package/extend/dobo/feature/image.js +0 -0
  95. package/extend/dobo/feature/immutable.js +0 -0
  96. package/extend/dobo/feature/removed-at.js +0 -0
  97. package/extend/dobo/feature/unique.js +0 -0
  98. package/extend/dobo/feature/updated-at.js +0 -0
  99. package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +0 -0
  100. package/extend/waibuStatic/virtual.json +0 -0
  101. package/index.js +0 -0
  102. package/lib/factory/action.js +287 -287
  103. package/lib/factory/adapter.js +1055 -1055
  104. package/lib/factory/connection.js +109 -109
  105. package/lib/factory/feature.js +52 -52
  106. package/lib/factory/model/aggregate.js +23 -23
  107. package/lib/factory/model/build.js +25 -25
  108. package/lib/factory/model/bulk-create-record.js +36 -36
  109. package/lib/factory/model/clear-record.js +30 -30
  110. package/lib/factory/model/count-record.js +42 -42
  111. package/lib/factory/model/create-attachment.js +36 -36
  112. package/lib/factory/model/create-record.js +49 -49
  113. package/lib/factory/model/drop.js +26 -26
  114. package/lib/factory/model/exists.js +26 -26
  115. package/lib/factory/model/find-all-record.js +112 -112
  116. package/lib/factory/model/find-attachment.js +28 -28
  117. package/lib/factory/model/find-one-record.js +38 -38
  118. package/lib/factory/model/find-record.js +82 -82
  119. package/lib/factory/model/get-attachment.js +15 -15
  120. package/lib/factory/model/get-record.js +52 -52
  121. package/lib/factory/model/helper.js +575 -576
  122. package/lib/factory/model/histogram.js +23 -23
  123. package/lib/factory/model/list-attachment.js +40 -40
  124. package/lib/factory/model/load-fixtures.js +63 -63
  125. package/lib/factory/model/remove-attachment.js +24 -24
  126. package/lib/factory/model/remove-record.js +44 -44
  127. package/lib/factory/model/sanitize-body.js +57 -57
  128. package/lib/factory/model/sanitize-fixture.js +56 -56
  129. package/lib/factory/model/sanitize-record.js +80 -80
  130. package/lib/factory/model/transaction.js +15 -15
  131. package/lib/factory/model/update-attachment.js +9 -9
  132. package/lib/factory/model/update-record.js +58 -58
  133. package/lib/factory/model/upsert-record.js +71 -71
  134. package/lib/factory/model/validate.js +0 -0
  135. package/lib/factory/model.js +503 -503
  136. package/lib/helper.js +463 -463
  137. package/package.json +1 -1
  138. package/test/e2e/_run.js +12 -12
  139. package/test/e2e/e2e-factory-process.test.js +25 -25
  140. package/test/e2e/e2e-model-adapter-process.test.js +52 -52
  141. package/test/integration/aspect-01-start-connections.test.js +33 -33
  142. package/test/integration/aspect-02-query-regex.test.js +34 -34
  143. package/test/integration/aspect-03-model-adapter-flow.test.js +88 -88
  144. package/test/unit/_stub.js +244 -244
  145. package/test/unit/dobo-action.test.js +58 -58
  146. package/test/unit/dobo-connection.test.js +37 -37
  147. package/test/unit/dobo-core.test.js +136 -136
  148. package/test/unit/dobo-feature.test.js +25 -25
  149. package/test/unit/dobo-model.test.js +115 -115
  150. package/test/unit/helper-sanitize-all.test.js +40 -40
  151. package/test/unit/helper-sanitize-ref.test.js +64 -64
  152. package/wiki/APPLETS.md +0 -0
  153. package/wiki/CHANGES.md +477 -469
  154. package/wiki/CONFIG.md +0 -0
  155. package/wiki/CONTRIBUTING.md +0 -0
  156. package/wiki/DEV-GUIDE.md +0 -0
  157. package/wiki/ECOSYSTEM.md +0 -0
  158. package/wiki/GETTING-STARTED.md +0 -0
  159. package/wiki/QUERY-LANGUAGE.md +0 -0
  160. 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
+ })