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
package/lib/factory/model.js
CHANGED
|
@@ -1,503 +1,503 @@
|
|
|
1
|
-
import { sanitizeAll, sanitizeRef } from '../helper.js'
|
|
2
|
-
import clearRecord from './model/clear-record.js'
|
|
3
|
-
import countRecord from './model/count-record.js'
|
|
4
|
-
import createRecord from './model/create-record.js'
|
|
5
|
-
import drop from './model/drop.js'
|
|
6
|
-
import findAllRecord from './model/find-all-record.js'
|
|
7
|
-
import findOneRecord from './model/find-one-record.js'
|
|
8
|
-
import findRecord from './model/find-record.js'
|
|
9
|
-
import getRecord from './model/get-record.js'
|
|
10
|
-
import histogram from './model/histogram.js'
|
|
11
|
-
import aggregate from './model/aggregate.js'
|
|
12
|
-
import exists from './model/exists.js'
|
|
13
|
-
import build from './model/build.js'
|
|
14
|
-
import loadFixtures from './model/load-fixtures.js'
|
|
15
|
-
import removeRecord from './model/remove-record.js'
|
|
16
|
-
import updateRecord from './model/update-record.js'
|
|
17
|
-
import createAttachment from './model/create-attachment.js'
|
|
18
|
-
import updateAttachment from './model/update-attachment.js'
|
|
19
|
-
import getAttachment from './model/get-attachment.js'
|
|
20
|
-
import removeAttachment from './model/remove-attachment.js'
|
|
21
|
-
import findAttachment from './model/find-attachment.js'
|
|
22
|
-
import sanitizeBody from './model/sanitize-body.js'
|
|
23
|
-
import sanitizeRecord from './model/sanitize-record.js'
|
|
24
|
-
import sanitizeFixture from './model/sanitize-fixture.js'
|
|
25
|
-
import upsertRecord from './model/upsert-record.js'
|
|
26
|
-
import bulkCreateRecord from './model/bulk-create-record.js'
|
|
27
|
-
import listAttachment from './model/list-attachment.js'
|
|
28
|
-
import transaction from './model/transaction.js'
|
|
29
|
-
import validate from './model/validate.js'
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @typedef {Object} TOptions
|
|
33
|
-
* @memberof DoboModel
|
|
34
|
-
* @type {Object}
|
|
35
|
-
* @property {boolean} [noHook=false] - If `true`, no model's hook will be executed
|
|
36
|
-
* @property {boolean} [noModelHook=false] - If `true`, no model's hook will be executed
|
|
37
|
-
* @property {boolean} [noDynHook=false] - If `true`, no dynamic hook will be executed
|
|
38
|
-
* @property {boolean} [noValidation=false] - If `true`, no validation of data payload performed
|
|
39
|
-
* @property {boolean} [noCheckUnique=false] - If `true`, no unique validation for ID performed
|
|
40
|
-
* @property {boolean} [noBodySanitizer=false] - If `true`, accept data payload as is without sanitization
|
|
41
|
-
* @property {boolean} [noRecordSanitizer=false] - If `true`, accept result payload as is without sanitization
|
|
42
|
-
* @property {boolean} [noResult=false] - If `true`, returns nothing
|
|
43
|
-
* @property {boolean} [truncateString=true] - If `true` (default), string is truncated to its model's `maxLength`
|
|
44
|
-
* @property {boolean} [partial] - If `true`, only updated values are saved. Otherwise replace all existing values with given payload. Defaults to `true` for `updateRecord`
|
|
45
|
-
* @property {boolean} [dataOnly=true] - If `true` (default) returns only record's object. Otherwise {@link DoboModel.TResult}
|
|
46
|
-
*/
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Sort order key used in pagination and sorting of records. It can be either:
|
|
50
|
-
* - `1` for ascending order (the default, if none is provided) or
|
|
51
|
-
* - `-1` for descending order.
|
|
52
|
-
* @typedef {string} TSortOrder
|
|
53
|
-
* @memberof DoboModel
|
|
54
|
-
*/
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Key value pairs used as sort order in pagination and sorting of records. It is an object where:
|
|
58
|
-
* - Key represents model's field name
|
|
59
|
-
* - Value represents its sort order
|
|
60
|
-
*
|
|
61
|
-
* @typedef {Object.<string, DoboModel.TSortOrder>} TSort
|
|
62
|
-
* @memberof DoboModel
|
|
63
|
-
* @example
|
|
64
|
-
* // to sort by firstName (ascending) and lastName (descending)
|
|
65
|
-
* const sort = {
|
|
66
|
-
* firstName: 1,
|
|
67
|
-
* lastName: -1
|
|
68
|
-
* }
|
|
69
|
-
*/
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @typedef {Object} TPagination
|
|
73
|
-
* @memberof DoboModel
|
|
74
|
-
* @property {number} limit - Number of records per page
|
|
75
|
-
* @property {number} page - Page number
|
|
76
|
-
* @property {number} [skip] - Records to skip. If not provided, it will be calculated based on `limit` and `page`
|
|
77
|
-
*/
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Property value type definition. Used for building property `values` in a model which is an array of this object type.
|
|
81
|
-
* @typedef TPropertyValue
|
|
82
|
-
* @memberof DoboModel
|
|
83
|
-
* @type {Object}
|
|
84
|
-
* @property {string} value - The actual value of the property
|
|
85
|
-
* @property {string} text - The display text for the property value, which can be translated based on the request context
|
|
86
|
-
*/
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Formatted version of a record's property values. It is an object where:
|
|
90
|
-
* - Key represents the property name
|
|
91
|
-
* - Value represents its formatted value according to the property's data type or formatting function. Data type is ALWAYS in string.
|
|
92
|
-
*
|
|
93
|
-
* This record is then wrapped in a `_fmt` property.
|
|
94
|
-
* @typedef TRecordFormatted
|
|
95
|
-
* @memberof DoboModel
|
|
96
|
-
* @type {Object}
|
|
97
|
-
* @property {Object} [_fmt={}] - Wrapper
|
|
98
|
-
* @example
|
|
99
|
-
* {
|
|
100
|
-
* _fmt: {
|
|
101
|
-
* id: "1",
|
|
102
|
-
* item: "Laptop",
|
|
103
|
-
* createdAt: "Jan 1, 2024, 12:00 AM",
|
|
104
|
-
* isActive: "True",
|
|
105
|
-
* price: "2,500.00"
|
|
106
|
-
* }
|
|
107
|
-
* }
|
|
108
|
-
*/
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Reference record related to a record's result. It is an object where:
|
|
112
|
-
* - Key represents the name of the referenced record. Normally, it is the referenced model's name in camelCase format.
|
|
113
|
-
* - Value represents its reference record. It can be an object or an array of objects, depending on the relationship type.
|
|
114
|
-
*
|
|
115
|
-
* This record is then wrapped in a `_ref` property in the result object.
|
|
116
|
-
* @typedef TRecordRef
|
|
117
|
-
* @memberof DoboModel
|
|
118
|
-
* @type {Object}
|
|
119
|
-
* @property {Object} [_ref={}] - Wrapper
|
|
120
|
-
* @property {Object|Array} [_ref.<string>.<DoboModel.TRecord>] - Reference record
|
|
121
|
-
* @example
|
|
122
|
-
* {
|
|
123
|
-
* _ref: {
|
|
124
|
-
* user: {
|
|
125
|
-
* id: 1,
|
|
126
|
-
* name: "John Doe"
|
|
127
|
-
* },
|
|
128
|
-
* posts: [
|
|
129
|
-
* {
|
|
130
|
-
* id: 1,
|
|
131
|
-
* title: "Post Title"
|
|
132
|
-
* },
|
|
133
|
-
* {
|
|
134
|
-
* id: 2,
|
|
135
|
-
* title: "Another Post Title"
|
|
136
|
-
* }
|
|
137
|
-
* ]
|
|
138
|
-
* }
|
|
139
|
-
* }
|
|
140
|
-
*/
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* The record returned from a model's action. It is an object where:
|
|
144
|
-
* - Key represents the property name
|
|
145
|
-
* - Value represents its value according to the property's data type
|
|
146
|
-
*
|
|
147
|
-
* This record is then merged with {@link DoboModel.TRecordFormatted} and {@link DoboModel.TRecordRef} to form the final record returned from a model's action.
|
|
148
|
-
* @typedef TRecord
|
|
149
|
-
* @memberof DoboModel
|
|
150
|
-
* @type {Object & DoboModel.TRecordFormatted & DoboModel.TRecordRef}
|
|
151
|
-
* @example
|
|
152
|
-
* {
|
|
153
|
-
* id: 1,
|
|
154
|
-
* item: "Laptop",
|
|
155
|
-
* createdAt: "2024-01-01T00:00:00.000Z",
|
|
156
|
-
* isActive: true,
|
|
157
|
-
* price: 2500.00,
|
|
158
|
-
* _fmt: {
|
|
159
|
-
* id: "1",
|
|
160
|
-
* item: "Laptop",
|
|
161
|
-
* createdAt: "Jan 1, 2024, 12:00 AM",
|
|
162
|
-
* isActive: "True",
|
|
163
|
-
* price: "2,500.00"
|
|
164
|
-
* },
|
|
165
|
-
* _ref: {
|
|
166
|
-
* user: {
|
|
167
|
-
* id: 1,
|
|
168
|
-
* name: "John Doe"
|
|
169
|
-
* }
|
|
170
|
-
* }
|
|
171
|
-
* }
|
|
172
|
-
*/
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* The result returned from a model's action other than `find*`, `{create|update|remove}Record` if `dataOnly` is set to `false`.
|
|
176
|
-
* @typedef TResultRecord
|
|
177
|
-
* @memberof DoboModel
|
|
178
|
-
* @type {Object}
|
|
179
|
-
* @property {Array<DoboModel.TRecord>} data - The data returned from the action
|
|
180
|
-
*/
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* The result returned from a model's `find*` actions if `dataOnly` is set to `false`.
|
|
184
|
-
* @typedef TResultFindRecord
|
|
185
|
-
* @memberof DoboModel
|
|
186
|
-
* @type {Object}
|
|
187
|
-
* @property {Array<DoboModel.TRecord>} data - The data returned from the action
|
|
188
|
-
* @property {number} [count] - The total number of records found, if applicable
|
|
189
|
-
* @property {boolean} [cached] - Indicates if the result was retrieved from cache, if applicable. Only available if `bajoCache` is used
|
|
190
|
-
* @property {Array} [warnings] - Any warnings generated during the action, if applicable
|
|
191
|
-
*/
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* The result returned from a model's `getRecord` action if `dataOnly` is set to `false`.
|
|
195
|
-
* @typedef TResultGetRecord
|
|
196
|
-
* @memberof DoboModel
|
|
197
|
-
* @type {Object}
|
|
198
|
-
* @property {DoboModel.TRecord} data - The data returned from the action
|
|
199
|
-
*/
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* The result returned from a model's `createRecord` action if `dataOnly` is set to `false`.
|
|
203
|
-
* @typedef TResultCreateRecord
|
|
204
|
-
* @memberof DoboModel
|
|
205
|
-
* @type {Object}
|
|
206
|
-
* @property {DoboModel.TRecord} data - The data returned from the action
|
|
207
|
-
*/
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* The result returned from a model's `updateRecord` action if `dataOnly` is set to `false`.
|
|
211
|
-
* @typedef TResultUpdateRecord
|
|
212
|
-
* @memberof DoboModel
|
|
213
|
-
* @type {Object}
|
|
214
|
-
* @property {DoboModel.TRecord} data - The updated data returned from the action
|
|
215
|
-
* @property {DoboModel.TRecord} oldData - The previous data before the update
|
|
216
|
-
*/
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* The result returned from a model's `removeRecord` action if `dataOnly` is set to `false`.
|
|
220
|
-
* @typedef TResultRemoveRecord
|
|
221
|
-
* @memberof DoboModel
|
|
222
|
-
* @type {Object}
|
|
223
|
-
* @property {DoboModel.TRecord} oldData - The removed data returned from the action
|
|
224
|
-
*/
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Model factory.
|
|
228
|
-
*
|
|
229
|
-
* @async
|
|
230
|
-
* @returns {Promise<void>} Returns nothing
|
|
231
|
-
*/
|
|
232
|
-
async function modelFactory () {
|
|
233
|
-
const { Tools } = this.app.baseClass
|
|
234
|
-
const { defaults } = this.app.lib._
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Model class defintion.
|
|
238
|
-
*
|
|
239
|
-
* @class
|
|
240
|
-
*/
|
|
241
|
-
class DoboModel extends Tools {
|
|
242
|
-
/**
|
|
243
|
-
* Constructor
|
|
244
|
-
*/
|
|
245
|
-
constructor (plugin, options) {
|
|
246
|
-
super(plugin)
|
|
247
|
-
|
|
248
|
-
defaults(this, options)
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Model's adapter name
|
|
252
|
-
* @type {string}
|
|
253
|
-
*/
|
|
254
|
-
this.adapter = this.connection.adapter
|
|
255
|
-
|
|
256
|
-
this.bindThis(
|
|
257
|
-
build,
|
|
258
|
-
exists,
|
|
259
|
-
drop,
|
|
260
|
-
|
|
261
|
-
createRecord,
|
|
262
|
-
getRecord,
|
|
263
|
-
updateRecord,
|
|
264
|
-
upsertRecord,
|
|
265
|
-
removeRecord,
|
|
266
|
-
clearRecord,
|
|
267
|
-
findRecord,
|
|
268
|
-
findOneRecord,
|
|
269
|
-
findAllRecord,
|
|
270
|
-
|
|
271
|
-
transaction,
|
|
272
|
-
bulkCreateRecord,
|
|
273
|
-
|
|
274
|
-
aggregate,
|
|
275
|
-
histogram,
|
|
276
|
-
countRecord,
|
|
277
|
-
|
|
278
|
-
createAttachment,
|
|
279
|
-
getAttachment,
|
|
280
|
-
updateAttachment,
|
|
281
|
-
removeAttachment,
|
|
282
|
-
findAttachment,
|
|
283
|
-
listAttachment,
|
|
284
|
-
|
|
285
|
-
loadFixtures,
|
|
286
|
-
sanitizeRecord,
|
|
287
|
-
sanitizeBody,
|
|
288
|
-
sanitizeFixture,
|
|
289
|
-
validate
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
// aliases
|
|
293
|
-
this.deleteRecord = this.removeRecord
|
|
294
|
-
this.clearRecords = this.clearRecord
|
|
295
|
-
this.countRecords = this.countRecord
|
|
296
|
-
this.findRecords = this.findRecord
|
|
297
|
-
this.findAllRecords = this.findAllRecord
|
|
298
|
-
this.listAttachments = this.listAttachment
|
|
299
|
-
this.bulkCreateRecords = this.bulkCreateRecord
|
|
300
|
-
|
|
301
|
-
this.getField = this.getProperty
|
|
302
|
-
this.hasField = this.hasProperty
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Invoke a model's action. It will create a new instance of {@link DoboAction} and return it.
|
|
307
|
-
* @method
|
|
308
|
-
* @memberof DoboModel
|
|
309
|
-
* @param {string} name - The name of the action to invoke
|
|
310
|
-
* @param {...any} args - Arguments to pass to the action
|
|
311
|
-
* @returns {DoboAction} Returns an instance of {@link DoboAction}
|
|
312
|
-
*/
|
|
313
|
-
action = (name, ...args) => {
|
|
314
|
-
const action = new this.app.baseClass.DoboAction(this, name, ...args)
|
|
315
|
-
return action
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
sanitizeObjectDef = async (obj) => {
|
|
319
|
-
await sanitizeAll.call(this.app.dobo, obj)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Sanitize a reference object based on the model's properties and rules.
|
|
324
|
-
* @method
|
|
325
|
-
* @memberof DoboModel
|
|
326
|
-
* @async
|
|
327
|
-
* @param {*} obj - The reference object to sanitize
|
|
328
|
-
* @param {boolean} [fatal=true] - Whether to throw an error if sanitization fails
|
|
329
|
-
*/
|
|
330
|
-
sanitizeRef = async (obj, fatal = true) => {
|
|
331
|
-
await sanitizeRef.call(this.app.dobo, obj, this.app.dobo.models, fatal)
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Sanitize the given ID based on the model's properties.
|
|
336
|
-
* @method
|
|
337
|
-
* @memberof DoboModel
|
|
338
|
-
* @param {string|number} id - The ID to sanitize
|
|
339
|
-
* @returns {string|number} Returns the sanitized ID
|
|
340
|
-
*/
|
|
341
|
-
sanitizeId = (id) => {
|
|
342
|
-
const prop = this.properties.find(p => p.name === 'id')
|
|
343
|
-
if (prop.type === 'integer') id = parseInt(id)
|
|
344
|
-
return id
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Get a property by its name.
|
|
349
|
-
* @method
|
|
350
|
-
* @memberof DoboModel
|
|
351
|
-
* @param {string} name - The name of the property to retrieve
|
|
352
|
-
* @returns {Object|null} Returns the property object if found, otherwise `undefined`
|
|
353
|
-
*/
|
|
354
|
-
getProperty = (name) => {
|
|
355
|
-
return this.properties.find(prop => prop.name === name)
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Get the model's properties based on the given options.
|
|
360
|
-
* @method
|
|
361
|
-
* @memberof DoboModel
|
|
362
|
-
* @param {Object} [options={}] - Options object
|
|
363
|
-
* @param {boolean} [options.noVirtual=false] - If `true`, exclude virtual properties
|
|
364
|
-
* @param {boolean} [options.namesOnly=false] - If `true`, return only property names instead of full property objects
|
|
365
|
-
* @returns {Array<object|string>} Returns an array of properties or property names based on the options
|
|
366
|
-
*/
|
|
367
|
-
getProperties = (options = {}) => {
|
|
368
|
-
const { noVirtual, namesOnly } = options
|
|
369
|
-
const items = noVirtual ? this.properties.filter(prop => !prop.virtual) : this.properties
|
|
370
|
-
return namesOnly ? items.map(item => item.name) : items
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Get the model's virtual properties.
|
|
375
|
-
* @method
|
|
376
|
-
* @memberof DoboModel
|
|
377
|
-
* @param {boolean} [namesOnly=false] - If `true`, return only property names instead of full property objects
|
|
378
|
-
* @returns {Array<object|string>} Returns an array of virtual properties or property names based on the `namesOnly` option
|
|
379
|
-
*/
|
|
380
|
-
getVirtualProperties = (namesOnly) => {
|
|
381
|
-
const items = this.properties.filter(prop => prop.virtual)
|
|
382
|
-
return namesOnly ? items.map(item => item.name) : items
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Get the model's non-virtual properties.
|
|
387
|
-
* @method
|
|
388
|
-
* @memberof DoboModel
|
|
389
|
-
* @param {boolean} [namesOnly=false] - If `true`, return only property names instead of full property objects
|
|
390
|
-
* @returns {Array<object|string>} Returns an array of non-virtual properties or property names based on the `namesOnly` option
|
|
391
|
-
*/
|
|
392
|
-
getNonVirtualProperties = (namesOnly) => {
|
|
393
|
-
const items = this.properties.filter(prop => !prop.virtual)
|
|
394
|
-
return namesOnly ? items.map(item => item.name) : items
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Returns the model's indexes.
|
|
399
|
-
* @method
|
|
400
|
-
* @memberof DoboModel
|
|
401
|
-
* @returns {Array<object>} Returns an array of index objects defined in the model
|
|
402
|
-
*/
|
|
403
|
-
getIndexes = () => {
|
|
404
|
-
return this.indexes
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Checks if the model has a property with the given name.
|
|
409
|
-
* @method
|
|
410
|
-
* @memberof DoboModel
|
|
411
|
-
* @param {string} name - The name of the property to check
|
|
412
|
-
* @returns {boolean} Returns `true` if the property exists, otherwise `false`
|
|
413
|
-
*/
|
|
414
|
-
hasProperty = (name) => {
|
|
415
|
-
return !!this.getProperty(name)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
syncIdField = (idField) => {
|
|
419
|
-
const { cloneDeep, findIndex } = this.app.lib._
|
|
420
|
-
if (!this.adapter) return
|
|
421
|
-
if (!idField) idField = cloneDeep(this.adapter.idField)
|
|
422
|
-
const idx = findIndex(this.properties, { name: 'id' })
|
|
423
|
-
if (idx === -1) return
|
|
424
|
-
this.properties.splice(idx, 1)
|
|
425
|
-
this.properties.unshift(idField)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Internal method to perform a simple lookup based on the provided value and lookupValue.
|
|
430
|
-
* @method
|
|
431
|
-
* @internal
|
|
432
|
-
* @memberof DoboModel
|
|
433
|
-
* @async
|
|
434
|
-
* @param {string|object|Array} value - The value to look up
|
|
435
|
-
* @param {object} lookupValue - The lookup values to replace in the query
|
|
436
|
-
* @param {object} [options={}] - Additional options for the lookup
|
|
437
|
-
* @returns {*|null|undefined} Returns the result of the lookup or `null` if not found or `undefined` if the value is not a string, object, or array
|
|
438
|
-
*/
|
|
439
|
-
_simpleLookup = async (value, lookupValue, options = {}) => {
|
|
440
|
-
const { get, isEmpty, isString, isPlainObject, isArray } = this.app.lib._
|
|
441
|
-
let model
|
|
442
|
-
let field
|
|
443
|
-
let query
|
|
444
|
-
if (isString(value)) {
|
|
445
|
-
[model, field, ...query] = value.split(':')
|
|
446
|
-
query = query.join(':')
|
|
447
|
-
} else if (isPlainObject(value)) ({ model, field, query } = value)
|
|
448
|
-
else if (isArray(value)) [model, field, query] = value
|
|
449
|
-
else return
|
|
450
|
-
for (const key in lookupValue) {
|
|
451
|
-
query = query.replaceAll(`{${key}}`, lookupValue[key])
|
|
452
|
-
}
|
|
453
|
-
if (query.includes('{') && query.includes('}')) return null
|
|
454
|
-
if (isEmpty(field)) field = 'id'
|
|
455
|
-
const { getModel } = this.app.dobo
|
|
456
|
-
const ref = getModel(model)
|
|
457
|
-
const opts = { ...options, noCache: true, noMagic: true }
|
|
458
|
-
opts.dataOnly = true
|
|
459
|
-
const rec = await ref.findOneRecord({ query }, opts)
|
|
460
|
-
return get(rec, field, null)
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Builds the values for a given property if `prop.values` is set:
|
|
465
|
-
* - If a string, it will be treated as a handler and executed to get the values.
|
|
466
|
-
* - If a function, it will be executed to get the values.
|
|
467
|
-
* - If an array, it will be used as is.
|
|
468
|
-
*
|
|
469
|
-
* Values are returned as an array of {@link DoboModel.TPropertyValues}.
|
|
470
|
-
* If a request object is provided in the options, the `text` will be translated using
|
|
471
|
-
* the request's translation function.
|
|
472
|
-
* @async
|
|
473
|
-
* @method
|
|
474
|
-
* @memberof DoboModel
|
|
475
|
-
* @param {object} prop - The property definition
|
|
476
|
-
* @param {DoboModel.TOptions} opts - Additional options for building the property values
|
|
477
|
-
* @returns {Array<DoboModel.TPropertyValue>} Returns an array of property values
|
|
478
|
-
*/
|
|
479
|
-
buildPropValues = async (prop, opts) => {
|
|
480
|
-
const { isString, camelCase, isFunction } = this.app.lib._
|
|
481
|
-
const { callHandler } = this.app.bajo
|
|
482
|
-
const values = isString(prop.values) || isFunction(prop.values) ? await callHandler(this, prop.values, opts) : [...prop.values]
|
|
483
|
-
return values.map(v => {
|
|
484
|
-
if (isString(v)) v = { value: v, text: v }
|
|
485
|
-
if (opts.req) {
|
|
486
|
-
const key = camelCase(`${prop.name} ${v.text}`)
|
|
487
|
-
if (opts.req.te(key)) v.text = opts.req.t(key)
|
|
488
|
-
}
|
|
489
|
-
return v
|
|
490
|
-
})
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
dispose = async () => {
|
|
494
|
-
await super.dispose()
|
|
495
|
-
this.connection = null
|
|
496
|
-
this.adapter = null
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
this.app.baseClass.DoboModel = DoboModel
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
export default modelFactory
|
|
1
|
+
import { sanitizeAll, sanitizeRef } from '../helper.js'
|
|
2
|
+
import clearRecord from './model/clear-record.js'
|
|
3
|
+
import countRecord from './model/count-record.js'
|
|
4
|
+
import createRecord from './model/create-record.js'
|
|
5
|
+
import drop from './model/drop.js'
|
|
6
|
+
import findAllRecord from './model/find-all-record.js'
|
|
7
|
+
import findOneRecord from './model/find-one-record.js'
|
|
8
|
+
import findRecord from './model/find-record.js'
|
|
9
|
+
import getRecord from './model/get-record.js'
|
|
10
|
+
import histogram from './model/histogram.js'
|
|
11
|
+
import aggregate from './model/aggregate.js'
|
|
12
|
+
import exists from './model/exists.js'
|
|
13
|
+
import build from './model/build.js'
|
|
14
|
+
import loadFixtures from './model/load-fixtures.js'
|
|
15
|
+
import removeRecord from './model/remove-record.js'
|
|
16
|
+
import updateRecord from './model/update-record.js'
|
|
17
|
+
import createAttachment from './model/create-attachment.js'
|
|
18
|
+
import updateAttachment from './model/update-attachment.js'
|
|
19
|
+
import getAttachment from './model/get-attachment.js'
|
|
20
|
+
import removeAttachment from './model/remove-attachment.js'
|
|
21
|
+
import findAttachment from './model/find-attachment.js'
|
|
22
|
+
import sanitizeBody from './model/sanitize-body.js'
|
|
23
|
+
import sanitizeRecord from './model/sanitize-record.js'
|
|
24
|
+
import sanitizeFixture from './model/sanitize-fixture.js'
|
|
25
|
+
import upsertRecord from './model/upsert-record.js'
|
|
26
|
+
import bulkCreateRecord from './model/bulk-create-record.js'
|
|
27
|
+
import listAttachment from './model/list-attachment.js'
|
|
28
|
+
import transaction from './model/transaction.js'
|
|
29
|
+
import validate from './model/validate.js'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} TOptions
|
|
33
|
+
* @memberof DoboModel
|
|
34
|
+
* @type {Object}
|
|
35
|
+
* @property {boolean} [noHook=false] - If `true`, no model's hook will be executed
|
|
36
|
+
* @property {boolean} [noModelHook=false] - If `true`, no model's hook will be executed
|
|
37
|
+
* @property {boolean} [noDynHook=false] - If `true`, no dynamic hook will be executed
|
|
38
|
+
* @property {boolean} [noValidation=false] - If `true`, no validation of data payload performed
|
|
39
|
+
* @property {boolean} [noCheckUnique=false] - If `true`, no unique validation for ID performed
|
|
40
|
+
* @property {boolean} [noBodySanitizer=false] - If `true`, accept data payload as is without sanitization
|
|
41
|
+
* @property {boolean} [noRecordSanitizer=false] - If `true`, accept result payload as is without sanitization
|
|
42
|
+
* @property {boolean} [noResult=false] - If `true`, returns nothing
|
|
43
|
+
* @property {boolean} [truncateString=true] - If `true` (default), string is truncated to its model's `maxLength`
|
|
44
|
+
* @property {boolean} [partial] - If `true`, only updated values are saved. Otherwise replace all existing values with given payload. Defaults to `true` for `updateRecord`
|
|
45
|
+
* @property {boolean} [dataOnly=true] - If `true` (default) returns only record's object. Otherwise {@link DoboModel.TResult}
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Sort order key used in pagination and sorting of records. It can be either:
|
|
50
|
+
* - `1` for ascending order (the default, if none is provided) or
|
|
51
|
+
* - `-1` for descending order.
|
|
52
|
+
* @typedef {string} TSortOrder
|
|
53
|
+
* @memberof DoboModel
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Key value pairs used as sort order in pagination and sorting of records. It is an object where:
|
|
58
|
+
* - Key represents model's field name
|
|
59
|
+
* - Value represents its sort order
|
|
60
|
+
*
|
|
61
|
+
* @typedef {Object.<string, DoboModel.TSortOrder>} TSort
|
|
62
|
+
* @memberof DoboModel
|
|
63
|
+
* @example
|
|
64
|
+
* // to sort by firstName (ascending) and lastName (descending)
|
|
65
|
+
* const sort = {
|
|
66
|
+
* firstName: 1,
|
|
67
|
+
* lastName: -1
|
|
68
|
+
* }
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @typedef {Object} TPagination
|
|
73
|
+
* @memberof DoboModel
|
|
74
|
+
* @property {number} limit - Number of records per page
|
|
75
|
+
* @property {number} page - Page number
|
|
76
|
+
* @property {number} [skip] - Records to skip. If not provided, it will be calculated based on `limit` and `page`
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Property value type definition. Used for building property `values` in a model which is an array of this object type.
|
|
81
|
+
* @typedef TPropertyValue
|
|
82
|
+
* @memberof DoboModel
|
|
83
|
+
* @type {Object}
|
|
84
|
+
* @property {string} value - The actual value of the property
|
|
85
|
+
* @property {string} text - The display text for the property value, which can be translated based on the request context
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Formatted version of a record's property values. It is an object where:
|
|
90
|
+
* - Key represents the property name
|
|
91
|
+
* - Value represents its formatted value according to the property's data type or formatting function. Data type is ALWAYS in string.
|
|
92
|
+
*
|
|
93
|
+
* This record is then wrapped in a `_fmt` property.
|
|
94
|
+
* @typedef TRecordFormatted
|
|
95
|
+
* @memberof DoboModel
|
|
96
|
+
* @type {Object}
|
|
97
|
+
* @property {Object} [_fmt={}] - Wrapper
|
|
98
|
+
* @example
|
|
99
|
+
* {
|
|
100
|
+
* _fmt: {
|
|
101
|
+
* id: "1",
|
|
102
|
+
* item: "Laptop",
|
|
103
|
+
* createdAt: "Jan 1, 2024, 12:00 AM",
|
|
104
|
+
* isActive: "True",
|
|
105
|
+
* price: "2,500.00"
|
|
106
|
+
* }
|
|
107
|
+
* }
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Reference record related to a record's result. It is an object where:
|
|
112
|
+
* - Key represents the name of the referenced record. Normally, it is the referenced model's name in camelCase format.
|
|
113
|
+
* - Value represents its reference record. It can be an object or an array of objects, depending on the relationship type.
|
|
114
|
+
*
|
|
115
|
+
* This record is then wrapped in a `_ref` property in the result object.
|
|
116
|
+
* @typedef TRecordRef
|
|
117
|
+
* @memberof DoboModel
|
|
118
|
+
* @type {Object}
|
|
119
|
+
* @property {Object} [_ref={}] - Wrapper
|
|
120
|
+
* @property {Object|Array} [_ref.<string>.<DoboModel.TRecord>] - Reference record
|
|
121
|
+
* @example
|
|
122
|
+
* {
|
|
123
|
+
* _ref: {
|
|
124
|
+
* user: {
|
|
125
|
+
* id: 1,
|
|
126
|
+
* name: "John Doe"
|
|
127
|
+
* },
|
|
128
|
+
* posts: [
|
|
129
|
+
* {
|
|
130
|
+
* id: 1,
|
|
131
|
+
* title: "Post Title"
|
|
132
|
+
* },
|
|
133
|
+
* {
|
|
134
|
+
* id: 2,
|
|
135
|
+
* title: "Another Post Title"
|
|
136
|
+
* }
|
|
137
|
+
* ]
|
|
138
|
+
* }
|
|
139
|
+
* }
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The record returned from a model's action. It is an object where:
|
|
144
|
+
* - Key represents the property name
|
|
145
|
+
* - Value represents its value according to the property's data type
|
|
146
|
+
*
|
|
147
|
+
* This record is then merged with {@link DoboModel.TRecordFormatted} and {@link DoboModel.TRecordRef} to form the final record returned from a model's action.
|
|
148
|
+
* @typedef TRecord
|
|
149
|
+
* @memberof DoboModel
|
|
150
|
+
* @type {Object & DoboModel.TRecordFormatted & DoboModel.TRecordRef}
|
|
151
|
+
* @example
|
|
152
|
+
* {
|
|
153
|
+
* id: 1,
|
|
154
|
+
* item: "Laptop",
|
|
155
|
+
* createdAt: "2024-01-01T00:00:00.000Z",
|
|
156
|
+
* isActive: true,
|
|
157
|
+
* price: 2500.00,
|
|
158
|
+
* _fmt: {
|
|
159
|
+
* id: "1",
|
|
160
|
+
* item: "Laptop",
|
|
161
|
+
* createdAt: "Jan 1, 2024, 12:00 AM",
|
|
162
|
+
* isActive: "True",
|
|
163
|
+
* price: "2,500.00"
|
|
164
|
+
* },
|
|
165
|
+
* _ref: {
|
|
166
|
+
* user: {
|
|
167
|
+
* id: 1,
|
|
168
|
+
* name: "John Doe"
|
|
169
|
+
* }
|
|
170
|
+
* }
|
|
171
|
+
* }
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The result returned from a model's action other than `find*`, `{create|update|remove}Record` if `dataOnly` is set to `false`.
|
|
176
|
+
* @typedef TResultRecord
|
|
177
|
+
* @memberof DoboModel
|
|
178
|
+
* @type {Object}
|
|
179
|
+
* @property {Array<DoboModel.TRecord>} data - The data returned from the action
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The result returned from a model's `find*` actions if `dataOnly` is set to `false`.
|
|
184
|
+
* @typedef TResultFindRecord
|
|
185
|
+
* @memberof DoboModel
|
|
186
|
+
* @type {Object}
|
|
187
|
+
* @property {Array<DoboModel.TRecord>} data - The data returned from the action
|
|
188
|
+
* @property {number} [count] - The total number of records found, if applicable
|
|
189
|
+
* @property {boolean} [cached] - Indicates if the result was retrieved from cache, if applicable. Only available if `bajoCache` is used
|
|
190
|
+
* @property {Array} [warnings] - Any warnings generated during the action, if applicable
|
|
191
|
+
*/
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The result returned from a model's `getRecord` action if `dataOnly` is set to `false`.
|
|
195
|
+
* @typedef TResultGetRecord
|
|
196
|
+
* @memberof DoboModel
|
|
197
|
+
* @type {Object}
|
|
198
|
+
* @property {DoboModel.TRecord} data - The data returned from the action
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The result returned from a model's `createRecord` action if `dataOnly` is set to `false`.
|
|
203
|
+
* @typedef TResultCreateRecord
|
|
204
|
+
* @memberof DoboModel
|
|
205
|
+
* @type {Object}
|
|
206
|
+
* @property {DoboModel.TRecord} data - The data returned from the action
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The result returned from a model's `updateRecord` action if `dataOnly` is set to `false`.
|
|
211
|
+
* @typedef TResultUpdateRecord
|
|
212
|
+
* @memberof DoboModel
|
|
213
|
+
* @type {Object}
|
|
214
|
+
* @property {DoboModel.TRecord} data - The updated data returned from the action
|
|
215
|
+
* @property {DoboModel.TRecord} oldData - The previous data before the update
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* The result returned from a model's `removeRecord` action if `dataOnly` is set to `false`.
|
|
220
|
+
* @typedef TResultRemoveRecord
|
|
221
|
+
* @memberof DoboModel
|
|
222
|
+
* @type {Object}
|
|
223
|
+
* @property {DoboModel.TRecord} oldData - The removed data returned from the action
|
|
224
|
+
*/
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Model factory.
|
|
228
|
+
*
|
|
229
|
+
* @async
|
|
230
|
+
* @returns {Promise<void>} Returns nothing
|
|
231
|
+
*/
|
|
232
|
+
async function modelFactory () {
|
|
233
|
+
const { Tools } = this.app.baseClass
|
|
234
|
+
const { defaults } = this.app.lib._
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Model class defintion.
|
|
238
|
+
*
|
|
239
|
+
* @class
|
|
240
|
+
*/
|
|
241
|
+
class DoboModel extends Tools {
|
|
242
|
+
/**
|
|
243
|
+
* Constructor
|
|
244
|
+
*/
|
|
245
|
+
constructor (plugin, options) {
|
|
246
|
+
super(plugin)
|
|
247
|
+
|
|
248
|
+
defaults(this, options)
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Model's adapter name
|
|
252
|
+
* @type {string}
|
|
253
|
+
*/
|
|
254
|
+
this.adapter = this.connection.adapter
|
|
255
|
+
|
|
256
|
+
this.bindThis(
|
|
257
|
+
build,
|
|
258
|
+
exists,
|
|
259
|
+
drop,
|
|
260
|
+
|
|
261
|
+
createRecord,
|
|
262
|
+
getRecord,
|
|
263
|
+
updateRecord,
|
|
264
|
+
upsertRecord,
|
|
265
|
+
removeRecord,
|
|
266
|
+
clearRecord,
|
|
267
|
+
findRecord,
|
|
268
|
+
findOneRecord,
|
|
269
|
+
findAllRecord,
|
|
270
|
+
|
|
271
|
+
transaction,
|
|
272
|
+
bulkCreateRecord,
|
|
273
|
+
|
|
274
|
+
aggregate,
|
|
275
|
+
histogram,
|
|
276
|
+
countRecord,
|
|
277
|
+
|
|
278
|
+
createAttachment,
|
|
279
|
+
getAttachment,
|
|
280
|
+
updateAttachment,
|
|
281
|
+
removeAttachment,
|
|
282
|
+
findAttachment,
|
|
283
|
+
listAttachment,
|
|
284
|
+
|
|
285
|
+
loadFixtures,
|
|
286
|
+
sanitizeRecord,
|
|
287
|
+
sanitizeBody,
|
|
288
|
+
sanitizeFixture,
|
|
289
|
+
validate
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
// aliases
|
|
293
|
+
this.deleteRecord = this.removeRecord
|
|
294
|
+
this.clearRecords = this.clearRecord
|
|
295
|
+
this.countRecords = this.countRecord
|
|
296
|
+
this.findRecords = this.findRecord
|
|
297
|
+
this.findAllRecords = this.findAllRecord
|
|
298
|
+
this.listAttachments = this.listAttachment
|
|
299
|
+
this.bulkCreateRecords = this.bulkCreateRecord
|
|
300
|
+
|
|
301
|
+
this.getField = this.getProperty
|
|
302
|
+
this.hasField = this.hasProperty
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Invoke a model's action. It will create a new instance of {@link DoboAction} and return it.
|
|
307
|
+
* @method
|
|
308
|
+
* @memberof DoboModel
|
|
309
|
+
* @param {string} name - The name of the action to invoke
|
|
310
|
+
* @param {...any} args - Arguments to pass to the action
|
|
311
|
+
* @returns {DoboAction} Returns an instance of {@link DoboAction}
|
|
312
|
+
*/
|
|
313
|
+
action = (name, ...args) => {
|
|
314
|
+
const action = new this.app.baseClass.DoboAction(this, name, ...args)
|
|
315
|
+
return action
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
sanitizeObjectDef = async (obj) => {
|
|
319
|
+
await sanitizeAll.call(this.app.dobo, obj)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Sanitize a reference object based on the model's properties and rules.
|
|
324
|
+
* @method
|
|
325
|
+
* @memberof DoboModel
|
|
326
|
+
* @async
|
|
327
|
+
* @param {*} obj - The reference object to sanitize
|
|
328
|
+
* @param {boolean} [fatal=true] - Whether to throw an error if sanitization fails
|
|
329
|
+
*/
|
|
330
|
+
sanitizeRef = async (obj, fatal = true) => {
|
|
331
|
+
await sanitizeRef.call(this.app.dobo, obj, this.app.dobo.models, fatal)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Sanitize the given ID based on the model's properties.
|
|
336
|
+
* @method
|
|
337
|
+
* @memberof DoboModel
|
|
338
|
+
* @param {string|number} id - The ID to sanitize
|
|
339
|
+
* @returns {string|number} Returns the sanitized ID
|
|
340
|
+
*/
|
|
341
|
+
sanitizeId = (id) => {
|
|
342
|
+
const prop = this.properties.find(p => p.name === 'id')
|
|
343
|
+
if (prop.type === 'integer') id = parseInt(id)
|
|
344
|
+
return id
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Get a property by its name.
|
|
349
|
+
* @method
|
|
350
|
+
* @memberof DoboModel
|
|
351
|
+
* @param {string} name - The name of the property to retrieve
|
|
352
|
+
* @returns {Object|null} Returns the property object if found, otherwise `undefined`
|
|
353
|
+
*/
|
|
354
|
+
getProperty = (name) => {
|
|
355
|
+
return this.properties.find(prop => prop.name === name)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Get the model's properties based on the given options.
|
|
360
|
+
* @method
|
|
361
|
+
* @memberof DoboModel
|
|
362
|
+
* @param {Object} [options={}] - Options object
|
|
363
|
+
* @param {boolean} [options.noVirtual=false] - If `true`, exclude virtual properties
|
|
364
|
+
* @param {boolean} [options.namesOnly=false] - If `true`, return only property names instead of full property objects
|
|
365
|
+
* @returns {Array<object|string>} Returns an array of properties or property names based on the options
|
|
366
|
+
*/
|
|
367
|
+
getProperties = (options = {}) => {
|
|
368
|
+
const { noVirtual, namesOnly } = options
|
|
369
|
+
const items = noVirtual ? this.properties.filter(prop => !prop.virtual) : this.properties
|
|
370
|
+
return namesOnly ? items.map(item => item.name) : items
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Get the model's virtual properties.
|
|
375
|
+
* @method
|
|
376
|
+
* @memberof DoboModel
|
|
377
|
+
* @param {boolean} [namesOnly=false] - If `true`, return only property names instead of full property objects
|
|
378
|
+
* @returns {Array<object|string>} Returns an array of virtual properties or property names based on the `namesOnly` option
|
|
379
|
+
*/
|
|
380
|
+
getVirtualProperties = (namesOnly) => {
|
|
381
|
+
const items = this.properties.filter(prop => prop.virtual)
|
|
382
|
+
return namesOnly ? items.map(item => item.name) : items
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Get the model's non-virtual properties.
|
|
387
|
+
* @method
|
|
388
|
+
* @memberof DoboModel
|
|
389
|
+
* @param {boolean} [namesOnly=false] - If `true`, return only property names instead of full property objects
|
|
390
|
+
* @returns {Array<object|string>} Returns an array of non-virtual properties or property names based on the `namesOnly` option
|
|
391
|
+
*/
|
|
392
|
+
getNonVirtualProperties = (namesOnly) => {
|
|
393
|
+
const items = this.properties.filter(prop => !prop.virtual)
|
|
394
|
+
return namesOnly ? items.map(item => item.name) : items
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Returns the model's indexes.
|
|
399
|
+
* @method
|
|
400
|
+
* @memberof DoboModel
|
|
401
|
+
* @returns {Array<object>} Returns an array of index objects defined in the model
|
|
402
|
+
*/
|
|
403
|
+
getIndexes = () => {
|
|
404
|
+
return this.indexes
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Checks if the model has a property with the given name.
|
|
409
|
+
* @method
|
|
410
|
+
* @memberof DoboModel
|
|
411
|
+
* @param {string} name - The name of the property to check
|
|
412
|
+
* @returns {boolean} Returns `true` if the property exists, otherwise `false`
|
|
413
|
+
*/
|
|
414
|
+
hasProperty = (name) => {
|
|
415
|
+
return !!this.getProperty(name)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
syncIdField = (idField) => {
|
|
419
|
+
const { cloneDeep, findIndex } = this.app.lib._
|
|
420
|
+
if (!this.adapter) return
|
|
421
|
+
if (!idField) idField = cloneDeep(this.adapter.idField)
|
|
422
|
+
const idx = findIndex(this.properties, { name: 'id' })
|
|
423
|
+
if (idx === -1) return
|
|
424
|
+
this.properties.splice(idx, 1)
|
|
425
|
+
this.properties.unshift(idField)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Internal method to perform a simple lookup based on the provided value and lookupValue.
|
|
430
|
+
* @method
|
|
431
|
+
* @internal
|
|
432
|
+
* @memberof DoboModel
|
|
433
|
+
* @async
|
|
434
|
+
* @param {string|object|Array} value - The value to look up
|
|
435
|
+
* @param {object} lookupValue - The lookup values to replace in the query
|
|
436
|
+
* @param {object} [options={}] - Additional options for the lookup
|
|
437
|
+
* @returns {*|null|undefined} Returns the result of the lookup or `null` if not found or `undefined` if the value is not a string, object, or array
|
|
438
|
+
*/
|
|
439
|
+
_simpleLookup = async (value, lookupValue, options = {}) => {
|
|
440
|
+
const { get, isEmpty, isString, isPlainObject, isArray } = this.app.lib._
|
|
441
|
+
let model
|
|
442
|
+
let field
|
|
443
|
+
let query
|
|
444
|
+
if (isString(value)) {
|
|
445
|
+
[model, field, ...query] = value.split(':')
|
|
446
|
+
query = query.join(':')
|
|
447
|
+
} else if (isPlainObject(value)) ({ model, field, query } = value)
|
|
448
|
+
else if (isArray(value)) [model, field, query] = value
|
|
449
|
+
else return
|
|
450
|
+
for (const key in lookupValue) {
|
|
451
|
+
query = query.replaceAll(`{${key}}`, lookupValue[key])
|
|
452
|
+
}
|
|
453
|
+
if (query.includes('{') && query.includes('}')) return null
|
|
454
|
+
if (isEmpty(field)) field = 'id'
|
|
455
|
+
const { getModel } = this.app.dobo
|
|
456
|
+
const ref = getModel(model)
|
|
457
|
+
const opts = { ...options, noCache: true, noMagic: true }
|
|
458
|
+
opts.dataOnly = true
|
|
459
|
+
const rec = await ref.findOneRecord({ query }, opts)
|
|
460
|
+
return get(rec, field, null)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Builds the values for a given property if `prop.values` is set:
|
|
465
|
+
* - If a string, it will be treated as a handler and executed to get the values.
|
|
466
|
+
* - If a function, it will be executed to get the values.
|
|
467
|
+
* - If an array, it will be used as is.
|
|
468
|
+
*
|
|
469
|
+
* Values are returned as an array of {@link DoboModel.TPropertyValues}.
|
|
470
|
+
* If a request object is provided in the options, the `text` will be translated using
|
|
471
|
+
* the request's translation function.
|
|
472
|
+
* @async
|
|
473
|
+
* @method
|
|
474
|
+
* @memberof DoboModel
|
|
475
|
+
* @param {object} prop - The property definition
|
|
476
|
+
* @param {DoboModel.TOptions} opts - Additional options for building the property values
|
|
477
|
+
* @returns {Array<DoboModel.TPropertyValue>} Returns an array of property values
|
|
478
|
+
*/
|
|
479
|
+
buildPropValues = async (prop, opts) => {
|
|
480
|
+
const { isString, camelCase, isFunction } = this.app.lib._
|
|
481
|
+
const { callHandler } = this.app.bajo
|
|
482
|
+
const values = isString(prop.values) || isFunction(prop.values) ? await callHandler(this, prop.values, opts) : [...prop.values]
|
|
483
|
+
return values.map(v => {
|
|
484
|
+
if (isString(v)) v = { value: v, text: v }
|
|
485
|
+
if (opts.req) {
|
|
486
|
+
const key = camelCase(`${prop.name} ${v.text}`)
|
|
487
|
+
if (opts.req.te(key)) v.text = opts.req.t(key)
|
|
488
|
+
}
|
|
489
|
+
return v
|
|
490
|
+
})
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
dispose = async () => {
|
|
494
|
+
await super.dispose()
|
|
495
|
+
this.connection = null
|
|
496
|
+
this.adapter = null
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
this.app.baseClass.DoboModel = DoboModel
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
export default modelFactory
|