@rws-framework/db 3.0.0 → 3.1.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/README.md +74 -136
- package/dist/decorators/IdType.d.ts +0 -0
- package/dist/decorators/IdType.js +0 -0
- package/dist/decorators/InverseRelation.js +30 -2
- package/dist/decorators/TypeFunctions.d.ts +0 -0
- package/dist/decorators/TypeFunctions.js +0 -0
- package/dist/helper/DbHelper.d.ts +1 -0
- package/dist/helper/DbHelper.js +6 -0
- package/dist/helper/db/index.d.ts +0 -0
- package/dist/helper/db/index.js +0 -0
- package/dist/helper/db/relation-manager.d.ts +0 -0
- package/dist/helper/db/relation-manager.js +0 -0
- package/dist/helper/db/schema-generator.d.ts +0 -0
- package/dist/helper/db/schema-generator.js +16 -1
- package/dist/helper/db/type-converter.d.ts +0 -0
- package/dist/helper/db/type-converter.js +0 -0
- package/dist/helper/db/utils.d.ts +1 -1
- package/dist/helper/db/utils.js +11 -13
- package/dist/models/core/RWSModel.d.ts +5 -3
- package/dist/models/core/RWSModel.js +5 -66
- package/dist/models/interfaces/IDbOpts.d.ts +0 -0
- package/dist/models/interfaces/IDbOpts.js +0 -0
- package/dist/models/interfaces/IIdOpts.d.ts +0 -0
- package/dist/models/interfaces/IIdOpts.js +0 -0
- package/dist/models/interfaces/IIdTypeOpts.d.ts +0 -0
- package/dist/models/interfaces/IIdTypeOpts.js +0 -0
- package/dist/models/interfaces/ITrackerOpts.d.ts +0 -0
- package/dist/models/interfaces/ITrackerOpts.js +0 -0
- package/dist/models/interfaces/OpModelType.d.ts +1 -1
- package/dist/models/utils/HydrateUtils.d.ts +13 -0
- package/dist/models/utils/HydrateUtils.js +81 -0
- package/package.json +1 -1
- package/src/decorators/InverseRelation.ts +43 -2
- package/src/helper/DbHelper.ts +9 -0
- package/src/helper/db/index.ts +0 -0
- package/src/helper/db/relation-manager.ts +0 -0
- package/src/helper/db/schema-generator.ts +23 -3
- package/src/helper/db/type-converter.ts +0 -0
- package/src/helper/db/utils.ts +15 -15
- package/src/models/core/RWSModel.ts +15 -84
- package/src/models/interfaces/IDbOpts.ts +0 -0
- package/src/models/interfaces/IIdTypeOpts.ts +0 -0
- package/src/models/interfaces/ITrackerOpts.ts +0 -0
- package/src/models/interfaces/OpModelType.ts +1 -1
- package/src/models/utils/HydrateUtils.ts +102 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Models
|
|
2
2
|
|
|
3
|
-
RWS models are converted to Prisma schemas and are wrapping around generated PrismaClient providing complete typing and better Relation handling + TimeSeries in future
|
|
3
|
+
RWS models are converted to Prisma schemas and are wrapping around generated PrismaClient providing complete typing and better Relation handling + TimeSeries in future versions.
|
|
4
4
|
|
|
5
5
|
## Models index file
|
|
6
6
|
|
|
@@ -21,11 +21,7 @@ export const models = [ User, ApiKey];
|
|
|
21
21
|
import ApiKey from './ApiKey';
|
|
22
22
|
import IApiKey from './interfaces/IApiKey';
|
|
23
23
|
|
|
24
|
-
@RWSCollection('users', {
|
|
25
|
-
relations: {
|
|
26
|
-
transcriptions: true,
|
|
27
|
-
apiKeys: true
|
|
28
|
-
},
|
|
24
|
+
@RWSCollection('users', {
|
|
29
25
|
ignored_keys: ['passwd']
|
|
30
26
|
})
|
|
31
27
|
class User extends RWSModel<User> implements IUser {
|
|
@@ -68,20 +64,26 @@ export default User;
|
|
|
68
64
|
|
|
69
65
|
***Basic many to one relation***
|
|
70
66
|
```typescript
|
|
71
|
-
import { RWSModel, TrackType, Relation } from '@rws-framework/db';
|
|
67
|
+
import { RWSCollection, RWSModel, TrackType, Relation } from '@rws-framework/db';
|
|
72
68
|
|
|
73
69
|
import 'reflect-metadata';
|
|
74
70
|
import User from './User';
|
|
71
|
+
import SomeModel from './SomeModel';
|
|
75
72
|
import IApiKey from './interfaces/IApiKey';
|
|
76
73
|
|
|
74
|
+
@RWSCollection('user_api_keys', {
|
|
75
|
+
relations: {
|
|
76
|
+
dummyIgnoredHydrationRelation: false // ignoring this relation on hydration - will be null
|
|
77
|
+
}
|
|
78
|
+
})
|
|
77
79
|
class ApiKey extends RWSModel<ApiKey> implements IApiKey {
|
|
78
|
-
static _RELATIONS = {
|
|
79
|
-
user: true,
|
|
80
|
-
};
|
|
81
80
|
|
|
82
|
-
@Relation(() => User,
|
|
81
|
+
@Relation(() => User, { requried: false }) // second attribute is required = false
|
|
83
82
|
user: User;
|
|
84
83
|
|
|
84
|
+
@Relation(() => SomeModel) // relation to be ignored by
|
|
85
|
+
dummyIgnoredHydrationRelation: SomeModel;
|
|
86
|
+
|
|
85
87
|
@TrackType(Object)
|
|
86
88
|
keyval: string;
|
|
87
89
|
|
|
@@ -111,29 +113,52 @@ export default ApiKey;
|
|
|
111
113
|
|
|
112
114
|
```typescript
|
|
113
115
|
import 'reflect-metadata';
|
|
116
|
+
import { RWSModel, OpModelType } from '../models/_model';
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
export type CascadingSetup = 'Cascade' | 'Restrict' | 'NoAction' | 'SetNull';
|
|
116
119
|
|
|
117
|
-
interface IRelationOpts {
|
|
120
|
+
export interface IRelationOpts {
|
|
118
121
|
required?: boolean
|
|
119
|
-
key
|
|
120
|
-
relationField
|
|
121
|
-
relatedToField?: string
|
|
122
|
-
|
|
122
|
+
key: string
|
|
123
|
+
relationField: string //name of field that will hold the relation key value
|
|
124
|
+
relatedToField?: string //name of related field (id by default)
|
|
125
|
+
mappingName?: string
|
|
126
|
+
relatedTo: OpModelType<RWSModel<any>>
|
|
127
|
+
many?: boolean // is it one-to-many or many-to-one
|
|
128
|
+
embed?: boolean // @deprecated for mongo - new decorator for embeds incoming
|
|
129
|
+
useUuid?: boolean //for sql dbs - if you're using some text based id
|
|
130
|
+
relationName?: string
|
|
131
|
+
cascade?: {
|
|
132
|
+
onDelete?: CascadingSetup,
|
|
133
|
+
onUpdate?: CascadingSetup
|
|
134
|
+
}
|
|
123
135
|
}
|
|
136
|
+
|
|
137
|
+
const _DEFAULTS: Partial<IRelationOpts> = { required: false, many: false, embed: false, cascade: { onDelete: 'SetNull', onUpdate: 'Cascade' }};
|
|
124
138
|
|
|
125
|
-
function Relation(theModel: () => OpModelType<RWSModel<any>>,
|
|
139
|
+
function Relation(theModel: () => OpModelType<RWSModel<any>>, relationOptions: Partial<IRelationOpts> = _DEFAULTS) {
|
|
126
140
|
return function(target: any, key: string) {
|
|
127
141
|
// Store the promise in metadata immediately
|
|
128
|
-
|
|
142
|
+
|
|
143
|
+
const metadataPromise = Promise.resolve().then(() => {
|
|
129
144
|
const relatedTo = theModel();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
145
|
+
|
|
146
|
+
const metaOpts: IRelationOpts = {
|
|
147
|
+
...relationOptions,
|
|
148
|
+
cascade: relationOptions.cascade || _DEFAULTS.cascade,
|
|
149
|
+
relatedTo,
|
|
150
|
+
relationField: relationOptions.relationField ? relationOptions.relationField : relatedTo._collection + '_id',
|
|
151
|
+
key,
|
|
152
|
+
// Generate a unique relation name if one is not provided
|
|
153
|
+
relationName: relationOptions.relationName ?
|
|
154
|
+
relationOptions.relationName.toLowerCase() :
|
|
155
|
+
`${target.constructor.name.toLowerCase()}_${key}_${relatedTo._collection.toLowerCase()}`
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
if(relationOptions.required){
|
|
159
|
+
metaOpts.cascade.onDelete = 'Restrict';
|
|
160
|
+
}
|
|
161
|
+
|
|
137
162
|
return metaOpts;
|
|
138
163
|
});
|
|
139
164
|
|
|
@@ -147,33 +172,41 @@ function Relation(theModel: () => OpModelType<RWSModel<any>>, required: boolean
|
|
|
147
172
|
|
|
148
173
|
|
|
149
174
|
export default Relation;
|
|
150
|
-
|
|
175
|
+
|
|
176
|
+
|
|
151
177
|
```
|
|
152
178
|
|
|
153
179
|
***Inverse relation decorator*** (one-to-many)
|
|
154
180
|
```typescript
|
|
155
181
|
import 'reflect-metadata';
|
|
156
|
-
import { RWSModel, OpModelType } from '
|
|
182
|
+
import { RWSModel, OpModelType } from '../models/_model';
|
|
157
183
|
|
|
158
|
-
interface InverseRelationOpts{
|
|
184
|
+
export interface InverseRelationOpts {
|
|
159
185
|
key: string,
|
|
160
|
-
inversionModel: OpModelType<RWSModel<any
|
|
161
|
-
foreignKey: string
|
|
162
|
-
|
|
186
|
+
inversionModel: OpModelType<RWSModel<any>>
|
|
187
|
+
foreignKey: string
|
|
188
|
+
singular?: boolean
|
|
189
|
+
relationName?: string
|
|
190
|
+
mappingName?: string
|
|
191
|
+
}
|
|
163
192
|
|
|
164
|
-
|
|
165
|
-
return function(target: any, key: string) {
|
|
166
|
-
// Store the promise in metadata immediately
|
|
193
|
+
function InverseRelation(inversionModel: () => OpModelType<RWSModel<any>>, sourceModel: () => OpModelType<RWSModel<any>>, relationOptions: Partial<InverseRelationOpts> = null) {
|
|
194
|
+
return function (target: any, key: string) {
|
|
167
195
|
const metadataPromise = Promise.resolve().then(() => {
|
|
168
196
|
const model = inversionModel();
|
|
169
197
|
const source = sourceModel();
|
|
170
|
-
|
|
198
|
+
|
|
171
199
|
const metaOpts: InverseRelationOpts = {
|
|
200
|
+
...relationOptions,
|
|
172
201
|
key,
|
|
173
202
|
inversionModel: model,
|
|
174
|
-
foreignKey: foreignKey ? foreignKey : `${source._collection}_id
|
|
175
|
-
|
|
176
|
-
|
|
203
|
+
foreignKey: relationOptions && relationOptions.foreignKey ? relationOptions.foreignKey : `${source._collection}_id`,
|
|
204
|
+
// Generate a unique relation name if one is not provided
|
|
205
|
+
relationName: relationOptions && relationOptions.relationName ?
|
|
206
|
+
relationOptions.relationName.toLowerCase() :
|
|
207
|
+
`${model._collection}_${key}_${source._collection}`.toLowerCase()
|
|
208
|
+
};
|
|
209
|
+
|
|
177
210
|
return metaOpts;
|
|
178
211
|
});
|
|
179
212
|
|
|
@@ -186,7 +219,7 @@ interface InverseRelationOpts{
|
|
|
186
219
|
}
|
|
187
220
|
|
|
188
221
|
export default InverseRelation;
|
|
189
|
-
|
|
222
|
+
|
|
190
223
|
```
|
|
191
224
|
|
|
192
225
|
|
|
@@ -307,99 +340,4 @@ bunx rws-db "mongodb://user:pass@localhost:27017/databaseName?authSource=admin&r
|
|
|
307
340
|
|
|
308
341
|
Code for RWS to prisma conversion from "@rws-framework/server" package:
|
|
309
342
|
|
|
310
|
-
|
|
311
|
-
static async generateModelSections(model: OpModelType<any>): Promise<string> {
|
|
312
|
-
let section = '';
|
|
313
|
-
const modelMetadatas: Record<string, {annotationType: string, metadata: any}> = await RWSModel.getModelAnnotations(model);
|
|
314
|
-
|
|
315
|
-
const modelName: string = (model as any)._collection;
|
|
316
|
-
|
|
317
|
-
section += `model ${modelName} {\n`;
|
|
318
|
-
section += '\tid String @map("_id") @id @default(auto()) @db.ObjectId\n';
|
|
319
|
-
|
|
320
|
-
for (const key in modelMetadatas) {
|
|
321
|
-
const modelMetadata = modelMetadatas[key].metadata;
|
|
322
|
-
let requiredString = modelMetadata.required ? '' : '?';
|
|
323
|
-
const annotationType: string = modelMetadatas[key].annotationType;
|
|
324
|
-
|
|
325
|
-
if(key === 'id'){
|
|
326
|
-
continue;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if(annotationType === 'Relation'){
|
|
331
|
-
const relationMeta = modelMetadata as IRelationOpts
|
|
332
|
-
|
|
333
|
-
const relatedModel = relationMeta.relatedTo as OpModelType<any>;
|
|
334
|
-
const isMany = relationMeta.many;
|
|
335
|
-
const cascadeOpts = [];
|
|
336
|
-
|
|
337
|
-
if (relationMeta.cascade?.onDelete) {
|
|
338
|
-
cascadeOpts.push(`onDelete: ${relationMeta.cascade.onDelete}`);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (relationMeta.cascade?.onUpdate) {
|
|
342
|
-
cascadeOpts.push(`onUpdate: ${relationMeta.cascade.onUpdate}`);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (isMany) {
|
|
346
|
-
// Handle many-to-many or one-to-many relation
|
|
347
|
-
section += `\t${key} ${relatedModel._collection}[] @relation("${modelName}_${relatedModel._collection}")\n`;
|
|
348
|
-
} else {
|
|
349
|
-
// Handle one-to-one or many-to-one relation
|
|
350
|
-
section += `\t${key} ${relatedModel._collection}${requiredString} @relation("${modelName}_${relatedModel._collection}", fields: [${modelMetadata.relationField}], references: [${modelMetadata.relatedToField || 'id'}], ${cascadeOpts.join(', ')})\n`;
|
|
351
|
-
section += `\t${modelMetadata.relationField} String${requiredString} @db.ObjectId\n`;
|
|
352
|
-
}
|
|
353
|
-
} else if (annotationType === 'InverseRelation'){
|
|
354
|
-
const relationMeta = modelMetadata as InverseRelationOpts;
|
|
355
|
-
|
|
356
|
-
// Handle inverse relation (one-to-many or one-to-one)
|
|
357
|
-
section += `\t${key} ${relationMeta.inversionModel._collection}[] @relation("${ relationMeta.relationName ? relationMeta.relationName : `${relationMeta.inversionModel._collection}_${modelName}`}")\n`;
|
|
358
|
-
} else if (annotationType === 'InverseTimeSeries'){
|
|
359
|
-
section += `\t${key} String[] @db.ObjectId\n`;
|
|
360
|
-
} else if (annotationType === 'TrackType'){
|
|
361
|
-
const tags: string[] = modelMetadata.tags.map((item: string) => '@' + item);
|
|
362
|
-
|
|
363
|
-
if(modelMetadata.isArray || modelMetadata.type.name === 'Array'){
|
|
364
|
-
requiredString = '';
|
|
365
|
-
}
|
|
366
|
-
section += `\t${key} ${DbHelper.toConfigCase(modelMetadata)}${requiredString} ${tags.join(' ')}\n`;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
section += '}\n';
|
|
371
|
-
return section;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
static toConfigCase(modelType: IMetaOpts): string {
|
|
375
|
-
const type = modelType.type;
|
|
376
|
-
let input = type.name;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if(input == 'Number'){
|
|
380
|
-
input = 'Int';
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if(input == 'Object'){
|
|
384
|
-
input = 'Json';
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if(input == 'Date'){
|
|
388
|
-
input = 'DateTime';
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if(input == 'Array'){
|
|
392
|
-
input = 'Json[]';
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const firstChar = input.charAt(0).toUpperCase();
|
|
396
|
-
const restOfString = input.slice(1);
|
|
397
|
-
let resultField = firstChar + restOfString;
|
|
398
|
-
|
|
399
|
-
if(modelType.isArray){
|
|
400
|
-
resultField += '[]';
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return resultField;
|
|
404
|
-
}
|
|
405
|
-
```
|
|
343
|
+
[The repo file](https://github.com/rws-framework/db/blob/master/src/helper/DbHelper.ts)
|
|
File without changes
|
|
File without changes
|
|
@@ -1,16 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
require("reflect-metadata");
|
|
4
|
+
const ModelUtils_1 = require("../models/utils/ModelUtils");
|
|
5
|
+
function guessForeignKey(inversionModel, bindingModel, decoratorsData) {
|
|
6
|
+
var _a;
|
|
7
|
+
let key = null;
|
|
8
|
+
let defaultKey = `${bindingModel._collection}_id`;
|
|
9
|
+
const relDecorators = {};
|
|
10
|
+
const trackDecorators = {};
|
|
11
|
+
if (Object.keys(trackDecorators).includes(key)) {
|
|
12
|
+
return key;
|
|
13
|
+
}
|
|
14
|
+
for (const decKey of Object.keys(decoratorsData)) {
|
|
15
|
+
const dec = decoratorsData[decKey];
|
|
16
|
+
if (dec.annotationType === 'Relation') {
|
|
17
|
+
relDecorators[decKey] = dec;
|
|
18
|
+
}
|
|
19
|
+
if (dec.annotationType === 'TrackType') {
|
|
20
|
+
trackDecorators[decKey] = dec;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
for (const relKey of Object.keys(relDecorators)) {
|
|
24
|
+
const prodMeta = (_a = relDecorators[relKey]) === null || _a === void 0 ? void 0 : _a.metadata;
|
|
25
|
+
if (prodMeta && prodMeta.relatedTo._collection === bindingModel._collection) {
|
|
26
|
+
return prodMeta.relationField;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return key;
|
|
30
|
+
}
|
|
4
31
|
function InverseRelation(inversionModel, sourceModel, relationOptions = null) {
|
|
5
32
|
return function (target, key) {
|
|
6
|
-
const metadataPromise = Promise.resolve().then(() => {
|
|
33
|
+
const metadataPromise = Promise.resolve().then(async () => {
|
|
7
34
|
const model = inversionModel();
|
|
8
35
|
const source = sourceModel();
|
|
36
|
+
const decoratorsData = await ModelUtils_1.ModelUtils.getModelAnnotations(model);
|
|
9
37
|
const metaOpts = {
|
|
10
38
|
...relationOptions,
|
|
11
39
|
key,
|
|
12
40
|
inversionModel: model,
|
|
13
|
-
foreignKey: relationOptions && relationOptions.foreignKey ? relationOptions.foreignKey :
|
|
41
|
+
foreignKey: relationOptions && relationOptions.foreignKey ? relationOptions.foreignKey : guessForeignKey(model, source, decoratorsData),
|
|
14
42
|
// Generate a unique relation name if one is not provided
|
|
15
43
|
relationName: relationOptions && relationOptions.relationName ?
|
|
16
44
|
relationOptions.relationName.toLowerCase() :
|
|
File without changes
|
|
File without changes
|
|
@@ -27,6 +27,7 @@ export declare class DbHelper {
|
|
|
27
27
|
* @param leaveFile Whether to leave the schema file after generation
|
|
28
28
|
*/
|
|
29
29
|
static pushDBModels(configService: IDbConfigHandler, dbService: DBService, leaveFile?: boolean): Promise<void>;
|
|
30
|
+
static migrateDBModels(configService: IDbConfigHandler, dbService: DBService, leaveFile?: boolean): Promise<void>;
|
|
30
31
|
/**
|
|
31
32
|
* Generate model sections for the schema
|
|
32
33
|
* @param model The model to generate a section for
|
package/dist/helper/DbHelper.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DbHelper = void 0;
|
|
4
|
+
const console_1 = require("@rws-framework/console");
|
|
4
5
|
const db_1 = require("./db");
|
|
5
6
|
/**
|
|
6
7
|
* Database helper class
|
|
@@ -27,6 +28,11 @@ class DbHelper {
|
|
|
27
28
|
static async pushDBModels(configService, dbService, leaveFile = false) {
|
|
28
29
|
return db_1.SchemaGenerator.pushDBModels(configService, dbService, leaveFile);
|
|
29
30
|
}
|
|
31
|
+
static async migrateDBModels(configService, dbService, leaveFile = false) {
|
|
32
|
+
process.env = { ...process.env, [this.dbUrlVarName]: configService.get('db_url') };
|
|
33
|
+
const [_, schemaPath] = db_1.DbUtils.getSchemaDir();
|
|
34
|
+
await console_1.rwsShell.runCommand(`${db_1.DbUtils.detectInstaller()} prisma migrate dev --create-only --schema=${schemaPath}`, process.cwd());
|
|
35
|
+
}
|
|
30
36
|
/**
|
|
31
37
|
* Generate model sections for the schema
|
|
32
38
|
* @param model The model to generate a section for
|
|
File without changes
|
package/dist/helper/db/index.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -47,7 +47,7 @@ datasource db {
|
|
|
47
47
|
const modelName = model._collection;
|
|
48
48
|
section += `model ${modelName} {\n`;
|
|
49
49
|
if (!model._NO_ID) {
|
|
50
|
-
section += `\t${utils_1.DbUtils.generateId(dbType, modelMetadatas
|
|
50
|
+
section += `\t${utils_1.DbUtils.generateId(dbType, modelMetadatas)}\n`;
|
|
51
51
|
}
|
|
52
52
|
for (const key in modelMetadatas) {
|
|
53
53
|
const modelMetadata = modelMetadatas[key].metadata;
|
|
@@ -81,6 +81,9 @@ datasource db {
|
|
|
81
81
|
const relationFieldName = modelMetadata.relationField ? modelMetadata.relationField : key.toLowerCase() + '_' + modelMetadata.relationField.toLowerCase();
|
|
82
82
|
const relatedToField = modelMetadata.relatedToField || 'id';
|
|
83
83
|
const bindingFieldExists = !!modelMetadatas[relationFieldName];
|
|
84
|
+
if (modelMetadata.required === false) {
|
|
85
|
+
requiredString = '?';
|
|
86
|
+
}
|
|
84
87
|
if (isMany) {
|
|
85
88
|
// Add an inverse field to the related model if it doesn't exist
|
|
86
89
|
section += `\t${key} ${relatedModel._collection}[] @relation("${relationName}", fields: [${relationFieldName}], references: [${relatedToField}], map: "${mapName}", ${cascadeOpts.join(', ')})\n`;
|
|
@@ -151,16 +154,28 @@ datasource db {
|
|
|
151
154
|
else if (annotationType === 'TrackType') {
|
|
152
155
|
const trackMeta = modelMetadata;
|
|
153
156
|
const tags = trackMeta.tags.map((item) => '@' + item);
|
|
157
|
+
if (key === 'id' && model._NO_ID && !model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes('id'))) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
154
160
|
if (trackMeta.unique) {
|
|
155
161
|
const fieldDetail = typeof trackMeta.unique === 'string' ? trackMeta.unique : null;
|
|
156
162
|
tags.push(`@unique(${fieldDetail ? `map: "${fieldDetail}"` : ''})`);
|
|
157
163
|
}
|
|
164
|
+
if (!trackMeta.required) {
|
|
165
|
+
requiredString = '?';
|
|
166
|
+
}
|
|
158
167
|
if (trackMeta.isArray || trackMeta.type.name === 'Array') {
|
|
159
168
|
requiredString = '';
|
|
160
169
|
}
|
|
170
|
+
if (model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes(key))) {
|
|
171
|
+
requiredString = '';
|
|
172
|
+
}
|
|
161
173
|
// Process any database-specific options from the metadata
|
|
162
174
|
const dbSpecificTags = type_converter_1.TypeConverter.processTypeOptions(trackMeta, dbType);
|
|
163
175
|
tags.push(...dbSpecificTags);
|
|
176
|
+
if (modelName === 'category_translation' && key === 'meta_keywords') {
|
|
177
|
+
console.log({ requiredString, trackMeta });
|
|
178
|
+
}
|
|
164
179
|
section += `\t${key} ${type_converter_1.TypeConverter.toConfigCase(trackMeta, dbType, key === 'id')}${requiredString} ${tags.join(' ')}\n`;
|
|
165
180
|
}
|
|
166
181
|
}
|
|
File without changes
|
|
File without changes
|
|
@@ -18,7 +18,7 @@ export declare class DbUtils {
|
|
|
18
18
|
static generateId(dbType: IDbConfigParams['db_type'], modelMeta: Record<string, {
|
|
19
19
|
annotationType: string;
|
|
20
20
|
metadata: IIdMetaOpts;
|
|
21
|
-
}>,
|
|
21
|
+
}>, optional?: boolean): string;
|
|
22
22
|
}
|
|
23
23
|
export declare const workspaceRootPath: string;
|
|
24
24
|
export declare const moduleDirPath: string;
|
package/dist/helper/db/utils.js
CHANGED
|
@@ -34,7 +34,7 @@ class DbUtils {
|
|
|
34
34
|
/**
|
|
35
35
|
* Generate an ID field based on the database type
|
|
36
36
|
*/
|
|
37
|
-
static generateId(dbType, modelMeta,
|
|
37
|
+
static generateId(dbType, modelMeta, optional = false) {
|
|
38
38
|
var _a, _b, _c, _d;
|
|
39
39
|
let useUuid = false;
|
|
40
40
|
let field = 'id';
|
|
@@ -46,9 +46,6 @@ class DbUtils {
|
|
|
46
46
|
if (annotationType == 'IdType') {
|
|
47
47
|
const dbSpecificTags = type_converter_1.TypeConverter.processTypeOptions({ tags: [], dbOptions: modelMetadata.dbOptions }, dbType);
|
|
48
48
|
tags.push(...dbSpecificTags);
|
|
49
|
-
if (debug) {
|
|
50
|
-
console.log({ modelMetadata: modelMetadata.dbOptions });
|
|
51
|
-
}
|
|
52
49
|
field = key;
|
|
53
50
|
if ((_b = (_a = modelMetadata.dbOptions) === null || _a === void 0 ? void 0 : _a.mysql) === null || _b === void 0 ? void 0 : _b.useUuid) {
|
|
54
51
|
useUuid = true;
|
|
@@ -63,23 +60,27 @@ class DbUtils {
|
|
|
63
60
|
}
|
|
64
61
|
}
|
|
65
62
|
let idString;
|
|
63
|
+
let reqStr = '';
|
|
64
|
+
if (optional) {
|
|
65
|
+
reqStr = '?';
|
|
66
|
+
}
|
|
66
67
|
switch (dbType) {
|
|
67
68
|
case 'mongodb':
|
|
68
|
-
idString = `${field} String @id @default(auto()) @map("_id") @db.ObjectId`;
|
|
69
|
+
idString = `${field} String${reqStr} @id @default(auto()) @map("_id") @db.ObjectId`;
|
|
69
70
|
break;
|
|
70
71
|
case 'mysql':
|
|
71
72
|
idString = useUuid
|
|
72
|
-
? `${field} String @id @default(uuid())`
|
|
73
|
-
: `${field} Int @id @default(autoincrement())`;
|
|
73
|
+
? `${field} String${reqStr} @id @default(uuid())`
|
|
74
|
+
: `${field} Int${reqStr} @id @default(autoincrement())`;
|
|
74
75
|
break;
|
|
75
76
|
case 'postgresql':
|
|
76
77
|
case 'postgres':
|
|
77
78
|
idString = useUuid
|
|
78
|
-
? `${field} String @id @default(uuid())`
|
|
79
|
-
: `${field} Int @id @default(autoincrement())`;
|
|
79
|
+
? `${field} String${reqStr} @id @default(uuid())`
|
|
80
|
+
: `${field} Int${reqStr} @id @default(autoincrement())`;
|
|
80
81
|
break;
|
|
81
82
|
case 'sqlite':
|
|
82
|
-
idString = `${field} Int @id @default(autoincrement())`;
|
|
83
|
+
idString = `${field} Int${reqStr} @id @default(autoincrement())`;
|
|
83
84
|
break;
|
|
84
85
|
}
|
|
85
86
|
if (tags.length) {
|
|
@@ -88,9 +89,6 @@ class DbUtils {
|
|
|
88
89
|
if (!idString) {
|
|
89
90
|
throw new Error(`DB type "${dbType}" is not supported!`);
|
|
90
91
|
}
|
|
91
|
-
if (debug) {
|
|
92
|
-
console.log({ idString, useUuid });
|
|
93
|
-
}
|
|
94
92
|
return idString;
|
|
95
93
|
}
|
|
96
94
|
}
|
|
@@ -4,6 +4,8 @@ import { OpModelType } from '../interfaces/OpModelType';
|
|
|
4
4
|
import { FindByType, IPaginationParams } from '../../types/FindParams';
|
|
5
5
|
import { DBService } from '../../services/DBService';
|
|
6
6
|
import { ISuperTagData } from '../../decorators/RWSCollection';
|
|
7
|
+
import { RelManyMetaType, RelOneMetaType } from '../types/RelationTypes';
|
|
8
|
+
import { IRWSModel } from '../../types/IRWSModel';
|
|
7
9
|
declare class RWSModel<T> implements IModel {
|
|
8
10
|
static services: IRWSModelServices;
|
|
9
11
|
[key: string]: any;
|
|
@@ -30,9 +32,9 @@ declare class RWSModel<T> implements IModel {
|
|
|
30
32
|
_asyncFill(data: any, fullDataMode?: boolean, allowRelations?: boolean): Promise<T>;
|
|
31
33
|
private getModelScalarFields;
|
|
32
34
|
private getRelationOneMeta;
|
|
33
|
-
static getRelationOneMeta(model: any, classFields: string[]): Promise<
|
|
35
|
+
static getRelationOneMeta(model: any, classFields: string[]): Promise<RelOneMetaType<IRWSModel>>;
|
|
34
36
|
private getRelationManyMeta;
|
|
35
|
-
static getRelationManyMeta(model: any, classFields: string[]): Promise<
|
|
37
|
+
static getRelationManyMeta(model: any, classFields: string[]): Promise<RelManyMetaType<IRWSModel>>;
|
|
36
38
|
static paginate<T extends RWSModel<T>>(this: OpModelType<T>, paginateParams: IPaginationParams, findParams?: FindByType): Promise<T[]>;
|
|
37
39
|
toMongo(): Promise<any>;
|
|
38
40
|
getCollection(): string | null;
|
|
@@ -54,7 +56,7 @@ declare class RWSModel<T> implements IModel {
|
|
|
54
56
|
sanitizeDBData(data: any): any;
|
|
55
57
|
static watchCollection<T extends RWSModel<T>>(this: OpModelType<T>, preRun: () => void): Promise<any>;
|
|
56
58
|
static findOneBy<T extends RWSModel<T>>(this: OpModelType<T>, findParams?: FindByType): Promise<T | null>;
|
|
57
|
-
static find<T extends RWSModel<T>>(this: OpModelType<T>, id: string, findParams?: Omit<FindByType, 'conditions'>): Promise<T | null>;
|
|
59
|
+
static find<T extends RWSModel<T>>(this: OpModelType<T>, id: string | number, findParams?: Omit<FindByType, 'conditions'>): Promise<T | null>;
|
|
58
60
|
static findBy<T extends RWSModel<T>>(this: OpModelType<T>, findParams?: FindByType): Promise<T[]>;
|
|
59
61
|
static delete<T extends RWSModel<T>>(this: OpModelType<T>, conditions: any): Promise<void>;
|
|
60
62
|
delete<T extends RWSModel<T>>(): Promise<void>;
|
|
@@ -15,6 +15,7 @@ const FieldsHelper_1 = require("../../helper/FieldsHelper");
|
|
|
15
15
|
const RelationUtils_1 = require("../utils/RelationUtils");
|
|
16
16
|
const TimeSeriesUtils_1 = require("../utils/TimeSeriesUtils");
|
|
17
17
|
const ModelUtils_1 = require("../utils/ModelUtils");
|
|
18
|
+
const HydrateUtils_1 = require("../utils/HydrateUtils");
|
|
18
19
|
class RWSModel {
|
|
19
20
|
constructor(data = null) {
|
|
20
21
|
if (!this.getCollection()) {
|
|
@@ -76,7 +77,6 @@ class RWSModel {
|
|
|
76
77
|
}
|
|
77
78
|
async _asyncFill(data, fullDataMode = false, allowRelations = true) {
|
|
78
79
|
const collections_to_models = {};
|
|
79
|
-
const timeSeriesIds = TimeSeriesUtils_1.TimeSeriesUtils.getTimeSeriesModelFields(this);
|
|
80
80
|
const classFields = FieldsHelper_1.FieldsHelper.getAllClassFields(this.constructor);
|
|
81
81
|
// Get both relation metadata types asynchronously
|
|
82
82
|
const [relOneData, relManyData] = await Promise.all([
|
|
@@ -88,72 +88,10 @@ class RWSModel {
|
|
|
88
88
|
});
|
|
89
89
|
const seriesHydrationfields = [];
|
|
90
90
|
if (allowRelations) {
|
|
91
|
-
|
|
92
|
-
for (const key in relManyData) {
|
|
93
|
-
if (!fullDataMode && this.constructor._CUT_KEYS.includes(key)) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
const relMeta = relManyData[key];
|
|
97
|
-
const relationEnabled = !RelationUtils_1.RelationUtils.checkRelDisabled(this, relMeta.key);
|
|
98
|
-
if (relationEnabled) {
|
|
99
|
-
this[relMeta.key] = await relMeta.inversionModel.findBy({
|
|
100
|
-
conditions: {
|
|
101
|
-
[relMeta.foreignKey]: data.id
|
|
102
|
-
},
|
|
103
|
-
allowRelations: false
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// Handle one-to-one relations
|
|
108
|
-
for (const key in relOneData) {
|
|
109
|
-
if (!fullDataMode && this.constructor._CUT_KEYS.includes(key)) {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
const relMeta = relOneData[key];
|
|
113
|
-
const relationEnabled = !RelationUtils_1.RelationUtils.checkRelDisabled(this, relMeta.key);
|
|
114
|
-
if (!data[relMeta.hydrationField] && relMeta.required) {
|
|
115
|
-
throw new Error(`Relation field "${relMeta.hydrationField}" is required in model ${this.constructor.name}.`);
|
|
116
|
-
}
|
|
117
|
-
if (relationEnabled && data[relMeta.hydrationField]) {
|
|
118
|
-
this[relMeta.key] = await relMeta.model.find(data[relMeta.hydrationField], { allowRelations: false });
|
|
119
|
-
}
|
|
120
|
-
else if (relationEnabled && !data[relMeta.hydrationField] && data[relMeta.key]) {
|
|
121
|
-
const newRelModel = await relMeta.model.create(data[relMeta.key]);
|
|
122
|
-
this[relMeta.key] = await newRelModel.save();
|
|
123
|
-
}
|
|
124
|
-
const cutKeys = this.constructor._CUT_KEYS;
|
|
125
|
-
if (!cutKeys.includes(relMeta.hydrationField)) {
|
|
126
|
-
cutKeys.push(relMeta.hydrationField);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
91
|
+
await HydrateUtils_1.HydrateUtils.hydrateRelations(this, relManyData, relOneData, seriesHydrationfields, fullDataMode, data);
|
|
129
92
|
}
|
|
130
93
|
// Process regular fields and time series
|
|
131
|
-
|
|
132
|
-
if (data.hasOwnProperty(key)) {
|
|
133
|
-
if (!fullDataMode && this.constructor._CUT_KEYS.includes(key)) {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
if (Object.keys(relOneData).includes(key)) {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (seriesHydrationfields.includes(key)) {
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
const timeSeriesMetaData = timeSeriesIds[key];
|
|
143
|
-
if (timeSeriesMetaData) {
|
|
144
|
-
this[key] = data[key];
|
|
145
|
-
const seriesModel = collections_to_models[timeSeriesMetaData.collection];
|
|
146
|
-
const dataModels = await seriesModel.findBy({
|
|
147
|
-
id: { in: data[key] }
|
|
148
|
-
});
|
|
149
|
-
seriesHydrationfields.push(timeSeriesMetaData.hydrationField);
|
|
150
|
-
this[timeSeriesMetaData.hydrationField] = dataModels;
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
this[key] = data[key];
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
94
|
+
await HydrateUtils_1.HydrateUtils.hydrateDataFields(this, collections_to_models, relOneData, seriesHydrationfields, fullDataMode, data);
|
|
157
95
|
return this;
|
|
158
96
|
}
|
|
159
97
|
getModelScalarFields(model) {
|
|
@@ -333,7 +271,8 @@ class RWSModel {
|
|
|
333
271
|
const collection = Reflect.get(this, '_collection');
|
|
334
272
|
this.checkForInclusionWithThrow(this.name);
|
|
335
273
|
try {
|
|
336
|
-
const
|
|
274
|
+
const paginateParams = (findParams === null || findParams === void 0 ? void 0 : findParams.pagination) ? findParams === null || findParams === void 0 ? void 0 : findParams.pagination : undefined;
|
|
275
|
+
const dbData = await this.services.dbService.findBy(collection, conditions, fields, ordering, paginateParams);
|
|
337
276
|
if (dbData.length) {
|
|
338
277
|
const instanced = [];
|
|
339
278
|
for (const data of dbData) {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|