mythix-orm 1.7.2 → 1.8.0
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/lib/connection/connection-base.d.ts +4 -0
- package/lib/connection/connection-base.js +80 -2
- package/lib/field.js +9 -1
- package/lib/model.d.ts +16 -4
- package/lib/model.js +203 -20
- package/lib/proxy-class/proxy-class.js +3 -0
- package/lib/types/concrete/date-type.js +4 -0
- package/lib/types/concrete/datetime-type.js +4 -0
- package/lib/types/concrete/serialized-type.js +1 -1
- package/lib/types/concrete/string-type.js +88 -0
- package/lib/types/concrete/text-type.js +97 -2
- package/lib/types/concrete/uuid-v1-type.js +133 -0
- package/lib/types/concrete/uuid-v3-type.js +129 -0
- package/lib/types/concrete/uuid-v4-type.js +129 -0
- package/lib/types/concrete/uuid-v5-type.js +129 -0
- package/lib/types/concrete/xid-type.js +89 -0
- package/lib/utils/async-store.d.ts +3 -0
- package/lib/utils/async-store.js +55 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.js +13 -0
- package/package.json +1 -1
|
@@ -83,6 +83,10 @@ declare class ConnectionBase extends EventEmitter {
|
|
|
83
83
|
public toQueryEngine(queryEngineLike: any): QueryEngine | undefined;
|
|
84
84
|
public registerModel<T = ModelClass>(Model: T): T;
|
|
85
85
|
public registerModels(models: Models | Array<ModelClass>): Models | undefined;
|
|
86
|
+
public getContextValue(key: any, defaultValue?: any): any;
|
|
87
|
+
public setContextValue(key: any, value: any): any;
|
|
88
|
+
public buildConnectionContext(connection?: ConnectionBase): Map<any, any>;
|
|
89
|
+
public createContext(callback: Function, connection?: ConnectionBase, thisArg?: any): Promise<any>;
|
|
86
90
|
public findModelField(finder: IterateFieldsCallback): Array<Field>;
|
|
87
91
|
public parseQualifiedName(fullyQualifiedName: string): FullyQualifiedFieldDefinition;
|
|
88
92
|
public getModels(): Models;
|
|
@@ -227,7 +227,7 @@ class ConnectionBase extends EventEmitter {
|
|
|
227
227
|
return;
|
|
228
228
|
|
|
229
229
|
if (!QueryEngine.isQuery(queryEngine)) {
|
|
230
|
-
if (
|
|
230
|
+
if ('where' in queryEngine)
|
|
231
231
|
queryEngine = queryEngine.where(this);
|
|
232
232
|
else
|
|
233
233
|
queryEngine = undefined;
|
|
@@ -260,6 +260,84 @@ class ConnectionBase extends EventEmitter {
|
|
|
260
260
|
return this._models;
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
/// Get a value from the `AsyncLocalStorage` context.
|
|
264
|
+
///
|
|
265
|
+
/// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
|
|
266
|
+
/// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
|
|
267
|
+
/// pass a transaction connection down through the call stack. However, it can
|
|
268
|
+
/// also be used for any "context" level values the user wishes to store.
|
|
269
|
+
///
|
|
270
|
+
/// Note:
|
|
271
|
+
/// An `AsyncLocalStorage` context might not exist when this call is made,
|
|
272
|
+
/// in which case this method will return `undefined`, or the `defaultValue`
|
|
273
|
+
/// if any was provided.
|
|
274
|
+
///
|
|
275
|
+
/// Return: any
|
|
276
|
+
///
|
|
277
|
+
/// Arguments:
|
|
278
|
+
/// key: any
|
|
279
|
+
/// The key for the value you wish to fetch.
|
|
280
|
+
/// The underlying "context" provided by `AsyncLocalStorage`
|
|
281
|
+
/// is a `Map`, so the "key" can be any value.
|
|
282
|
+
/// defaultValue?: any
|
|
283
|
+
/// The default value to return if there is no current `AsyncLocalStorage`
|
|
284
|
+
/// context, or if the requested key is not found.
|
|
285
|
+
getContextValue(key, defaultValue) {
|
|
286
|
+
return Utils.getContextValue(key, defaultValue);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/// Set a value onto the `AsyncLocalStorage` context.
|
|
290
|
+
///
|
|
291
|
+
/// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
|
|
292
|
+
/// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
|
|
293
|
+
/// pass a transaction connection down through the call stack. However, it can
|
|
294
|
+
/// also be used for any "context" level values the user wishes to store.
|
|
295
|
+
///
|
|
296
|
+
/// Note:
|
|
297
|
+
/// An `AsyncLocalStorage` context might not exist when this call is made,
|
|
298
|
+
/// in which case this method will do nothing, and silently return.
|
|
299
|
+
///
|
|
300
|
+
/// Return: any
|
|
301
|
+
///
|
|
302
|
+
/// Arguments:
|
|
303
|
+
/// key: any
|
|
304
|
+
/// The key for the value you wish to set.
|
|
305
|
+
/// The underlying "context" provided by `AsyncLocalStorage`
|
|
306
|
+
/// is a `Map`, so the "key" can be any value.
|
|
307
|
+
/// value: any
|
|
308
|
+
/// The value to set to the specified key.
|
|
309
|
+
setContextValue(key, value) {
|
|
310
|
+
return Utils.setContextValue(key, value);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
buildConnectionContext(_connection) {
|
|
314
|
+
let connection = _connection || this;
|
|
315
|
+
let models = connection._models;
|
|
316
|
+
let modelNames = Object.keys(models);
|
|
317
|
+
let newContext = new Map();
|
|
318
|
+
|
|
319
|
+
newContext.set('connection', connection);
|
|
320
|
+
|
|
321
|
+
for (let i = 0, il = modelNames.length; i < il; i++) {
|
|
322
|
+
let modelName = modelNames[i];
|
|
323
|
+
let Model = models[modelName];
|
|
324
|
+
let currentModelScope = Model.getModelContext();
|
|
325
|
+
|
|
326
|
+
newContext.set(modelName, { ...currentModelScope, connection });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return newContext;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async createContext(callback, _connection, thisArg) {
|
|
333
|
+
let context = this.buildConnectionContext(_connection);
|
|
334
|
+
let connection = _connection || this;
|
|
335
|
+
|
|
336
|
+
return await Utils.runInContext(context, async () => {
|
|
337
|
+
return await callback.call(thisArg, connection);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
263
341
|
findModelField(finder) {
|
|
264
342
|
let modelMap = this.getModels();
|
|
265
343
|
let modelNames = Object.keys(modelMap);
|
|
@@ -1248,7 +1326,7 @@ class ConnectionBase extends EventEmitter {
|
|
|
1248
1326
|
|
|
1249
1327
|
// eslint-disable-next-line no-unused-vars
|
|
1250
1328
|
async query(sql, options) {
|
|
1251
|
-
throw new Error(`${this.constructor.name}::
|
|
1329
|
+
throw new Error(`${this.constructor.name}::query: This operation is not supported for this connection type.`);
|
|
1252
1330
|
}
|
|
1253
1331
|
|
|
1254
1332
|
// eslint-disable-next-line no-unused-vars
|
package/lib/field.js
CHANGED
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
/// Interface:
|
|
68
68
|
/// interface DefaultValueContext {
|
|
69
69
|
/// _initial: boolean; // If `true`, then this is the initial set of the field's value.
|
|
70
|
+
/// _static: boolean; // If `true`, then this will be used in a "static" context, such as the `DEFAULT` of the DB.
|
|
70
71
|
/// connection: Connection; // The connection from the model.
|
|
71
72
|
/// data: object | undefined; // All attributes provided to the model upon instantiation.
|
|
72
73
|
/// field: Field; // The field descriptor where this `defaultValue` is defined.
|
|
@@ -113,8 +114,15 @@
|
|
|
113
114
|
/// This has no effect client-side in JavaScript. Even if `allowNull`
|
|
114
115
|
/// is `false`, the field property on your model may still have a
|
|
115
116
|
/// `null` value in JavaScript.
|
|
116
|
-
/// index: boolean = false
|
|
117
|
+
/// index: Array<boolean | string> | boolean = false
|
|
117
118
|
/// If `true`, then this field will be indexed in the database.
|
|
119
|
+
/// If an array is provided, then it must be an array containing
|
|
120
|
+
/// other field names to combine with this field to create a combined
|
|
121
|
+
/// index. For example: `index: [ true, 'firstName', 'lastName', [ 'firstName', 'lastName' ] ]`
|
|
122
|
+
/// would create four indexes: `true` means index this field, `firstName` would create
|
|
123
|
+
/// the combination index `this_field_first_name`, `lastName` would create the
|
|
124
|
+
/// combination index `this_field_last_name`, and `[ 'firstName', 'lastName ]` would
|
|
125
|
+
/// create the combination index `this_field_first_name_last_name`.
|
|
118
126
|
/// unique: boolean = false
|
|
119
127
|
/// If `true`, then add a unique constraint to this field in
|
|
120
128
|
/// the underlying database.
|
package/lib/model.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare interface ModelOptions extends GenericObject {
|
|
|
15
15
|
export declare type ModelClass = typeof Model;
|
|
16
16
|
|
|
17
17
|
export declare interface Models {
|
|
18
|
-
[
|
|
18
|
+
[key: string]: ModelClass;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export declare interface HookContext {
|
|
@@ -25,11 +25,11 @@ export declare interface HookContext {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export declare interface DirtyChanges {
|
|
28
|
-
[
|
|
28
|
+
[key: string]: { previous: any; current: any };
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export declare type LooseFields = Array<Field | FieldDefinition> | { [
|
|
32
|
-
export declare type Fields = Array<Field> | { [
|
|
31
|
+
export declare type LooseFields = Array<Field | FieldDefinition> | { [key: string]: Field | FieldDefinition } | Map<string, Field | FieldDefinition> | Set<Field | FieldDefinition>;
|
|
32
|
+
export declare type Fields = Array<Field> | { [key: string]: Field } | Map<string, Field> | Set<Field>;
|
|
33
33
|
|
|
34
34
|
export declare interface IterateFieldsContext {
|
|
35
35
|
field: Field;
|
|
@@ -54,6 +54,18 @@ export declare class Model {
|
|
|
54
54
|
public static isModel(value: any): boolean;
|
|
55
55
|
public static toString(showFields: boolean): string;
|
|
56
56
|
|
|
57
|
+
static getContextValue(key: any, defaultValue?: any): any;
|
|
58
|
+
getContextValue(key: any, defaultValue?: any): any;
|
|
59
|
+
|
|
60
|
+
static setContextValue(key: any, value: any): void;
|
|
61
|
+
setContextValue(key: any, value: any): void;
|
|
62
|
+
|
|
63
|
+
public static getModelContext(): GenericObject;
|
|
64
|
+
getModelContext(): GenericObject;
|
|
65
|
+
|
|
66
|
+
static updateModelContext(value: GenericObject): void;
|
|
67
|
+
updateModelContext(value: GenericObject): void;
|
|
68
|
+
|
|
57
69
|
public static _getConnection(connection?: ConnectionBase): ConnectionBase;
|
|
58
70
|
public _getConnection(connection?: ConnectionBase): ConnectionBase;
|
|
59
71
|
|
package/lib/model.js
CHANGED
|
@@ -5,6 +5,7 @@ const Inflection = require('inflection');
|
|
|
5
5
|
const Type = require('./types/type');
|
|
6
6
|
const DefaultHelpers = require('./types/helpers/default-helpers');
|
|
7
7
|
const Field = require('./field');
|
|
8
|
+
const AsyncStore = require('./utils/async-store');
|
|
8
9
|
const ModelUtils = require('./utils/model-utils');
|
|
9
10
|
|
|
10
11
|
/// Used as a unique key for cache.
|
|
@@ -45,24 +46,21 @@ function bindStaticWhereToModelClass(ModelClass) {
|
|
|
45
46
|
set: function() {},
|
|
46
47
|
get: function() {
|
|
47
48
|
const self = this;
|
|
49
|
+
let connection = self._getConnection();
|
|
50
|
+
if (connection)
|
|
51
|
+
return self.getQueryEngine(connection);
|
|
48
52
|
|
|
49
53
|
function unboundWhere(_connection, _propName) {
|
|
50
54
|
let connection = _connection;
|
|
51
|
-
|
|
52
|
-
if (arguments.length === 0) {
|
|
53
|
-
connection = self._getConnection();
|
|
54
|
-
if (!connection)
|
|
55
|
-
throw new Error(`${self.getModelName()}::where: Model has no bound connection. You need to provide a connection by calling "${self.getModelName()}.where(connection)" instead.`);
|
|
56
|
-
|
|
57
|
-
return self.getQueryEngine(connection);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
55
|
if (!connection) {
|
|
61
56
|
connection = self._getConnection();
|
|
62
57
|
if (!connection)
|
|
63
58
|
throw new Error(`${self.getModelName()}::where: Model has no bound connection. You need to provide a connection by calling "${self.getModelName()}.where(connection)" instead.`);
|
|
64
59
|
}
|
|
65
60
|
|
|
61
|
+
if (arguments.length === 0)
|
|
62
|
+
return self.getQueryEngine(connection);
|
|
63
|
+
|
|
66
64
|
let propName = _propName;
|
|
67
65
|
|
|
68
66
|
if (connection.constructor && !connection.constructor._isMythixConnection) {
|
|
@@ -94,7 +92,12 @@ function bindStaticWhereToModelClass(ModelClass) {
|
|
|
94
92
|
// This will throw an exception
|
|
95
93
|
// if no connection is available
|
|
96
94
|
let queryEngine = self.getQueryEngine(connection);
|
|
97
|
-
|
|
95
|
+
let prop = queryEngine[propName];
|
|
96
|
+
|
|
97
|
+
if (typeof prop === 'function' && typeof prop.bind === 'function')
|
|
98
|
+
prop = prop.bind(queryEngine);
|
|
99
|
+
|
|
100
|
+
return prop;
|
|
98
101
|
},
|
|
99
102
|
});
|
|
100
103
|
},
|
|
@@ -223,6 +226,146 @@ class Model {
|
|
|
223
226
|
return this.toString((depth !== 0));
|
|
224
227
|
}
|
|
225
228
|
|
|
229
|
+
/// Get a value from the `AsyncLocalStorage` context.
|
|
230
|
+
///
|
|
231
|
+
/// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
|
|
232
|
+
/// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
|
|
233
|
+
/// pass a transaction connection down through the call stack. However, it can
|
|
234
|
+
/// also be used for any "context" level values the user wishes to store.
|
|
235
|
+
///
|
|
236
|
+
/// Note:
|
|
237
|
+
/// An `AsyncLocalStorage` context might not exist when this call is made,
|
|
238
|
+
/// in which case this method will return `undefined`, or the `defaultValue`
|
|
239
|
+
/// if any was provided.
|
|
240
|
+
///
|
|
241
|
+
/// Return: any
|
|
242
|
+
///
|
|
243
|
+
/// Arguments:
|
|
244
|
+
/// key: any
|
|
245
|
+
/// The key for the value you wish to fetch.
|
|
246
|
+
/// The underlying "context" provided by `AsyncLocalStorage`
|
|
247
|
+
/// is a `Map`, so the "key" can be any value.
|
|
248
|
+
/// defaultValue?: any
|
|
249
|
+
/// The default value to return if there is no current `AsyncLocalStorage`
|
|
250
|
+
/// context, or if the requested key is not found.
|
|
251
|
+
static getContextValue(key, defaultValue) {
|
|
252
|
+
return AsyncStore.getContextValue(key, defaultValue);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
getContextValue(key, defaultValue) {
|
|
256
|
+
return this.constructor.getContextValue(key, defaultValue);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/// Set a value onto the `AsyncLocalStorage` context.
|
|
260
|
+
///
|
|
261
|
+
/// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
|
|
262
|
+
/// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
|
|
263
|
+
/// pass a transaction connection down through the call stack. However, it can
|
|
264
|
+
/// also be used for any "context" level values the user wishes to store.
|
|
265
|
+
///
|
|
266
|
+
/// Note:
|
|
267
|
+
/// An `AsyncLocalStorage` context might not exist when this call is made,
|
|
268
|
+
/// in which case this method will do nothing, and silently return.
|
|
269
|
+
///
|
|
270
|
+
/// Return: any
|
|
271
|
+
///
|
|
272
|
+
/// Arguments:
|
|
273
|
+
/// key: any
|
|
274
|
+
/// The key for the value you wish to set.
|
|
275
|
+
/// The underlying "context" provided by `AsyncLocalStorage`
|
|
276
|
+
/// is a `Map`, so the "key" can be any value.
|
|
277
|
+
/// value: any
|
|
278
|
+
/// The value to set to the specified key.
|
|
279
|
+
static setContextValue(key, value) {
|
|
280
|
+
return AsyncStore.setContextValue(key, value);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
setContextValue(key, value) {
|
|
284
|
+
return this.constructor.setContextValue(key, value);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/// Get this Model class's "model context" from the `AsyncLocalStorage` context.
|
|
288
|
+
///
|
|
289
|
+
/// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
|
|
290
|
+
/// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
|
|
291
|
+
/// pass a transaction connection down through the call stack. However, it can
|
|
292
|
+
/// also be used for any "context" level values the user wishes to store.
|
|
293
|
+
///
|
|
294
|
+
/// When a <see>Connection.createContext</see> or <see>Connection.transaction</see>
|
|
295
|
+
/// call is made, a "model context" (that is just an Object instance) is created for each
|
|
296
|
+
/// model the connection is aware of, and the `connection` instance is added to this
|
|
297
|
+
/// context, allowing models to lookup the "context connection" when a call to
|
|
298
|
+
/// <see>Model._getConnection</see> is made. By default, these "model contexts"
|
|
299
|
+
/// that are stored inside the `AsyncLocalStorage` context only contain a "connection"
|
|
300
|
+
/// property. However, these model contexts were designed as Object instances so that
|
|
301
|
+
/// the user may also use them, or so they can be used for future features. Each model
|
|
302
|
+
/// also has its own "model context" so that if the application is using more than
|
|
303
|
+
/// one connection, then a different connection can be specified for each model and
|
|
304
|
+
/// stored in the same `AsyncLocalStorage` context.
|
|
305
|
+
///
|
|
306
|
+
/// Note:
|
|
307
|
+
/// An `AsyncLocalStorage` context might not exist when this call is made,
|
|
308
|
+
/// in which case this method will simply return an empty object.
|
|
309
|
+
///
|
|
310
|
+
/// Return: object
|
|
311
|
+
/// The model class's "model context" stored in `AsyncLocalStorage`.
|
|
312
|
+
/// This will be an empty object if no `AsyncLocalStorage` context is
|
|
313
|
+
/// available.
|
|
314
|
+
static getModelContext() {
|
|
315
|
+
return AsyncStore.getContextValue(this.getModelName(), {});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
getModelContext() {
|
|
319
|
+
return this.constructor.getModelContext();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/// Update this Model class's "model context" from the `AsyncLocalStorage` context.
|
|
323
|
+
///
|
|
324
|
+
/// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
|
|
325
|
+
/// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
|
|
326
|
+
/// pass a transaction connection down through the call stack. However, it can
|
|
327
|
+
/// also be used for any "context" level values the user wishes to store.
|
|
328
|
+
///
|
|
329
|
+
/// When a <see>Connection.createContext</see> or <see>Connection.transaction</see>
|
|
330
|
+
/// call is made, a "model context" (that is just an Object instance) is created for each
|
|
331
|
+
/// model the connection is aware of, and the `connection` instance is added to this
|
|
332
|
+
/// context, allowing models to lookup the "context connection" when a call to
|
|
333
|
+
/// <see>Model._getConnection</see> is made. By default, these "model contexts"
|
|
334
|
+
/// that are stored inside the `AsyncLocalStorage` context only contain a "connection"
|
|
335
|
+
/// property. However, these model contexts were designed as Object instances so that
|
|
336
|
+
/// the user may also use them, or so they can be used for future features. Each model
|
|
337
|
+
/// also has its own "model context" so that if the application is using more than
|
|
338
|
+
/// one connection, then a different connection can be specified for each model and
|
|
339
|
+
/// stored in the same `AsyncLocalStorage` context.
|
|
340
|
+
///
|
|
341
|
+
/// This method allows you to update this "model context" for the model. The `value`
|
|
342
|
+
/// argument **must** be an Object instance. You can specify any properties you want.
|
|
343
|
+
/// Just be cautious if you set a `connection` property, as you might overwrite any
|
|
344
|
+
/// connection currently set on the context. Any properties you set here will be available
|
|
345
|
+
/// in any child function calls (inside this context scope), and can be accessed via a
|
|
346
|
+
/// call to <see>Model.getModelContext</see>.
|
|
347
|
+
///
|
|
348
|
+
/// Note:
|
|
349
|
+
/// An `AsyncLocalStorage` context might not exist when this call is made,
|
|
350
|
+
/// in which case this method will simply return an empty object.
|
|
351
|
+
///
|
|
352
|
+
/// Return: undefined
|
|
353
|
+
///
|
|
354
|
+
/// Arguments:
|
|
355
|
+
/// value: object
|
|
356
|
+
/// The new properties you wish to merge with the current "model context".
|
|
357
|
+
static updateModelContext(value) {
|
|
358
|
+
let context = AsyncStore.getContextValue(this.getModelName());
|
|
359
|
+
if (!context)
|
|
360
|
+
return;
|
|
361
|
+
|
|
362
|
+
Object.assign(context, value);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
updateModelContext(value) {
|
|
366
|
+
return this.constructor.updateModelContext(value);
|
|
367
|
+
}
|
|
368
|
+
|
|
226
369
|
/// Get the underlying connection bound to
|
|
227
370
|
/// the model's class. Connection binding is
|
|
228
371
|
/// optional, so this method can also be provided
|
|
@@ -233,6 +376,32 @@ class Model {
|
|
|
233
376
|
/// or fallback to a "bound" connection (if one can
|
|
234
377
|
/// be found).
|
|
235
378
|
///
|
|
379
|
+
/// This method will return a connection in the following
|
|
380
|
+
/// order: 1) The provided `connection` argument, if supplied,
|
|
381
|
+
/// 2) The `connection` property on the "model context" from
|
|
382
|
+
/// <see>Model.getModelContext</see> from `AsyncLocalStorage`,
|
|
383
|
+
/// if one is available, and 3) Finally the bound connection, if
|
|
384
|
+
/// available, which is found by searching the `static Model._mythixBoundConnection`
|
|
385
|
+
/// property of the class.
|
|
386
|
+
///
|
|
387
|
+
/// Note:
|
|
388
|
+
/// This is a low-level "global fallback" method. Any connection for a model
|
|
389
|
+
/// will first be searched by calling <see>Model.getConnection</see>,
|
|
390
|
+
/// which will call this static method if no connection can be found.
|
|
391
|
+
/// The <see>Model.getConnection</see> will also search the model instance
|
|
392
|
+
/// for a `connection` property, which can be supplied to any model when
|
|
393
|
+
/// it is instantiated.
|
|
394
|
+
///
|
|
395
|
+
/// Note:
|
|
396
|
+
// A provided `connection`, either via the model instance `model.connection`,
|
|
397
|
+
/// or via the provided `connection` argument to the <see>Model.getConnection</see>
|
|
398
|
+
/// method (and eventually this method) will take priority over any `connection`
|
|
399
|
+
/// provided by the "model context" from `AsyncLocalStorage`. This can be an important
|
|
400
|
+
/// detail when using <see>Connection.transaction</see> or <see>Connection.createContext</see>,
|
|
401
|
+
/// because if a connection is directly specified by the user, the transaction connection
|
|
402
|
+
/// **will not** take priority. So if you are using transactions or a connection context,
|
|
403
|
+
/// be careful how you directly specify connections.
|
|
404
|
+
///
|
|
236
405
|
/// Return: <see>Connection</see>
|
|
237
406
|
///
|
|
238
407
|
/// Arguments:
|
|
@@ -243,7 +412,18 @@ class Model {
|
|
|
243
412
|
/// then attempt to fallback to the bound connection,
|
|
244
413
|
/// if any is available.
|
|
245
414
|
static _getConnection(_connection) {
|
|
246
|
-
|
|
415
|
+
if (_connection)
|
|
416
|
+
return _connection;
|
|
417
|
+
|
|
418
|
+
let { connection } = this.getModelContext();
|
|
419
|
+
if (connection)
|
|
420
|
+
return connection;
|
|
421
|
+
|
|
422
|
+
connection = AsyncStore.getContextValue('connection');
|
|
423
|
+
if (connection)
|
|
424
|
+
return connection;
|
|
425
|
+
|
|
426
|
+
return this._mythixBoundConnection;
|
|
247
427
|
}
|
|
248
428
|
|
|
249
429
|
/// See <see>Model.static _getConnection</see>
|
|
@@ -343,17 +523,19 @@ class Model {
|
|
|
343
523
|
|
|
344
524
|
let connectionOptions = connection.getOptions();
|
|
345
525
|
if (connectionOptions && connectionOptions.bindModels === false) {
|
|
346
|
-
let modelName = ModelClass.getModelName();
|
|
526
|
+
// let modelName = ModelClass.getModelName();
|
|
347
527
|
|
|
348
|
-
ModelClass = class BoundModel extends ModelClass {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
};
|
|
528
|
+
// ModelClass = class BoundModel extends ModelClass {
|
|
529
|
+
// static getModelName() {
|
|
530
|
+
// return modelName;
|
|
531
|
+
// }
|
|
532
|
+
// };
|
|
353
533
|
|
|
354
|
-
ModelClass.fields = ModelClass.mergeFields();
|
|
355
|
-
ModelClass._sortedFields = null; // Clear cache
|
|
356
|
-
|
|
534
|
+
// ModelClass.fields = ModelClass.mergeFields();
|
|
535
|
+
// ModelClass._sortedFields = null; // Clear cache
|
|
536
|
+
|
|
537
|
+
return ModelClass;
|
|
538
|
+
} else if (connectionOptions.forceConnectionBinding !== true) {
|
|
357
539
|
if (Object.prototype.hasOwnProperty.call(ModelClass, '_mythixBoundConnection') && ModelClass._mythixBoundConnection)
|
|
358
540
|
throw new Error(`${this.getModelName()}:bindConnection: Model is already bound to a connection. You can not bind a model to a connection more than once.`);
|
|
359
541
|
}
|
|
@@ -1424,6 +1606,7 @@ class Model {
|
|
|
1424
1606
|
/// Prefix a field name with `+` to specify ASCending order, i.e.
|
|
1425
1607
|
/// `[ "+User:id" ]`. Prefix the field name with `-` to specify
|
|
1426
1608
|
/// DESCending order, i.e. `[ "-User:id" ]`.
|
|
1609
|
+
// eslint-disable-next-line no-unused-vars
|
|
1427
1610
|
static defaultOrder(options) {
|
|
1428
1611
|
}
|
|
1429
1612
|
|
|
@@ -27,6 +27,9 @@ const AUTO_CALL_CALLED = Symbol.for('@__autoCallCalled');
|
|
|
27
27
|
const AUTO_CALL = Symbol.for('@__autoCall');
|
|
28
28
|
|
|
29
29
|
function shouldSkipProxy(prop) {
|
|
30
|
+
if (prop === 'bind' || prop === 'call' || prop === 'apply')
|
|
31
|
+
return true;
|
|
32
|
+
|
|
30
33
|
if (prop === PROXY || prop === TARGET || prop === SELF || prop === AUTO_CALL_CALLER || prop === AUTO_CALL_CALLED || prop === AUTO_CALL)
|
|
31
34
|
return true;
|
|
32
35
|
|
|
@@ -20,6 +20,10 @@ const MiscUtils = require('../../utils/misc-utils');
|
|
|
20
20
|
/// it is used to parse provided date strings, and when serializing the
|
|
21
21
|
/// field (for example via <see>Model.toJSON</see>).
|
|
22
22
|
///
|
|
23
|
+
/// Note:
|
|
24
|
+
/// A `BigInt` timestamp is used to store the date
|
|
25
|
+
/// in the underlying database.
|
|
26
|
+
///
|
|
23
27
|
/// Example:
|
|
24
28
|
/// class Dates extends Model {
|
|
25
29
|
/// static fields = {
|
|
@@ -20,6 +20,10 @@ const MiscUtils = require('../../utils/misc-utils');
|
|
|
20
20
|
/// it is used to parse provided date strings, and when serializing the
|
|
21
21
|
/// field (for example via <see>Model.toJSON</see>).
|
|
22
22
|
///
|
|
23
|
+
/// Note:
|
|
24
|
+
/// A `BigInt` timestamp is used to store the date
|
|
25
|
+
/// and time in the underlying database.
|
|
26
|
+
///
|
|
23
27
|
/// Example:
|
|
24
28
|
/// class DatesWithTimes extends Model {
|
|
25
29
|
/// static fields = {
|
|
@@ -27,7 +27,7 @@ const Type = require('../type');
|
|
|
27
27
|
/// then the side-effect will be that this field may be serialized
|
|
28
28
|
/// and stored more often than actually needed. This system was
|
|
29
29
|
/// designed this way because it is likely better to get a false
|
|
30
|
-
/// positive and save your data
|
|
30
|
+
/// positive and save your data than to not check at all and lose data.
|
|
31
31
|
/// If needed, you can always create your own child type of
|
|
32
32
|
/// `SerializedType`, and overload the `isDirty` method, or provided
|
|
33
33
|
/// an `isDirty` method to the field `options` to check
|
|
@@ -5,17 +5,74 @@ const Type = require('../type');
|
|
|
5
5
|
|
|
6
6
|
const DEFAULT_STRING_LENGTH = 256;
|
|
7
7
|
|
|
8
|
+
/// `STRING` type.
|
|
9
|
+
///
|
|
10
|
+
/// This represents a "string" of any size for the
|
|
11
|
+
/// underlying database driver, usually a "VARCHAR" type.
|
|
12
|
+
///
|
|
13
|
+
/// If no "length" is specified when you create the type,
|
|
14
|
+
/// then a default length of `256` is assumed.
|
|
15
|
+
///
|
|
16
|
+
/// Example:
|
|
17
|
+
/// class Strings extends Model {
|
|
18
|
+
/// static fields = {
|
|
19
|
+
/// string1: Types.STRING(16),
|
|
20
|
+
/// string2: Types.STRING, // default length = 256
|
|
21
|
+
/// string3: new Types.StringType(64),
|
|
22
|
+
/// };
|
|
23
|
+
/// }
|
|
24
|
+
///
|
|
25
|
+
/// See: Type
|
|
8
26
|
class StringType extends Type {
|
|
27
|
+
/// Get the "display" name for this type.
|
|
28
|
+
///
|
|
29
|
+
/// This method is called from <see>Model.toString</see>
|
|
30
|
+
/// when stringifying the model for representation.
|
|
31
|
+
///
|
|
32
|
+
/// Note:
|
|
33
|
+
/// This is also an instance method that can be called from
|
|
34
|
+
/// an instance of the type.
|
|
35
|
+
///
|
|
36
|
+
/// Return: string
|
|
37
|
+
/// Return the string value `'STRING'`
|
|
9
38
|
static getDisplayName() {
|
|
10
39
|
return 'STRING';
|
|
11
40
|
}
|
|
12
41
|
|
|
42
|
+
/// Construct a new `STRING` type.
|
|
43
|
+
///
|
|
44
|
+
/// The `length` argument specifies
|
|
45
|
+
/// the number of characters to use for this string
|
|
46
|
+
/// in the database.
|
|
47
|
+
///
|
|
48
|
+
/// Return: StringType
|
|
49
|
+
///
|
|
50
|
+
/// Arguments:
|
|
51
|
+
/// length?: number
|
|
52
|
+
/// How many characters to use in the underlying database to store the value.
|
|
13
53
|
constructor(length) {
|
|
14
54
|
super(length);
|
|
15
55
|
|
|
16
56
|
this.length = length || DEFAULT_STRING_LENGTH;
|
|
17
57
|
}
|
|
18
58
|
|
|
59
|
+
/// Cast provided value to underlying type.
|
|
60
|
+
///
|
|
61
|
+
/// This will cast the incoming value to the
|
|
62
|
+
/// underlying type of this field, a `string`
|
|
63
|
+
/// primitive. A `null` or `undefined`
|
|
64
|
+
/// value will simply be returned.
|
|
65
|
+
///
|
|
66
|
+
/// See <see>Type.castToType</see> for a more
|
|
67
|
+
/// detailed description.
|
|
68
|
+
///
|
|
69
|
+
/// Return: string | null | undefined
|
|
70
|
+
/// Return the incoming `value`, cast to this
|
|
71
|
+
/// type. `null` and `undefined` are simply
|
|
72
|
+
/// returned without casting.
|
|
73
|
+
///
|
|
74
|
+
/// Arguments:
|
|
75
|
+
/// context: <see name="CastToTypeContext">Type.castToType</see>
|
|
19
76
|
castToType({ value }) {
|
|
20
77
|
if (value == null)
|
|
21
78
|
return value;
|
|
@@ -23,10 +80,41 @@ class StringType extends Type {
|
|
|
23
80
|
return ('' + value);
|
|
24
81
|
}
|
|
25
82
|
|
|
83
|
+
/// Check if the provided value is valid.
|
|
84
|
+
///
|
|
85
|
+
/// This will check if the provided value is
|
|
86
|
+
/// a `string` (or `String`) instance.
|
|
87
|
+
/// Both an empty string, and a string that is nothing
|
|
88
|
+
/// except whitespace are considered "valid".
|
|
89
|
+
///
|
|
90
|
+
/// Return: boolean
|
|
91
|
+
///
|
|
92
|
+
/// Arguments:
|
|
93
|
+
/// value: any
|
|
94
|
+
/// The value to check.
|
|
26
95
|
isValidValue(value) {
|
|
27
96
|
return Nife.instanceOf(value, 'string');
|
|
28
97
|
}
|
|
29
98
|
|
|
99
|
+
/// Stringify the type itself.
|
|
100
|
+
///
|
|
101
|
+
/// If a `connection` argument is provided, then this
|
|
102
|
+
/// will go through the connection to generate the type
|
|
103
|
+
/// for the underlying database. If no connection is
|
|
104
|
+
/// provided, then a "standard" SQL type will be returned
|
|
105
|
+
/// for this type instead. The "standard" type returned
|
|
106
|
+
/// when no `connection` is provided is `'VARCHAR'`.
|
|
107
|
+
///
|
|
108
|
+
/// Return: string
|
|
109
|
+
///
|
|
110
|
+
/// Arguments:
|
|
111
|
+
/// connection?: <see>Connection</see>
|
|
112
|
+
/// An optional connection. If provided, send this
|
|
113
|
+
/// type through <see>Type.toConnectionType</see>
|
|
114
|
+
/// to have the connection itself generate the underlying
|
|
115
|
+
/// type for the database. If `connection` is not provided,
|
|
116
|
+
/// then this will simply return a "standard" generic matching
|
|
117
|
+
/// SQL type.
|
|
30
118
|
toString(connection) {
|
|
31
119
|
return (connection)
|
|
32
120
|
? this.toConnectionType(connection)
|