@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
|
@@ -21,7 +21,7 @@ export interface OpModelType<T> {
|
|
|
21
21
|
checkForInclusionWithThrow: (className: string) => void;
|
|
22
22
|
checkForInclusion: (className: string) => boolean;
|
|
23
23
|
findOneBy<T extends RWSModel<T>>(this: OpModelType<T>, findParams?: FindByType): Promise<T | null>;
|
|
24
|
-
find<T extends RWSModel<T>>(this: OpModelType<T>, id: string, findParams?: Omit<FindByType, 'conditions'>): Promise<T | null>;
|
|
24
|
+
find<T extends RWSModel<T>>(this: OpModelType<T>, id: string | number, findParams?: Omit<FindByType, 'conditions'>): Promise<T | null>;
|
|
25
25
|
findBy<T extends RWSModel<T>>(this: OpModelType<T>, findParams?: FindByType): Promise<T[]>;
|
|
26
26
|
paginate<T extends RWSModel<T>>(this: OpModelType<T>, paginateParams?: IPaginationParams, findParams?: FindByType): Promise<T[]>;
|
|
27
27
|
delete<T extends RWSModel<T>>(this: OpModelType<T>, conditions: any): Promise<void>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RWSModel } from "../core/RWSModel";
|
|
2
|
+
import { RelManyMetaType, RelOneMetaType } from "../types/RelationTypes";
|
|
3
|
+
import { IRWSModel } from "../../types/IRWSModel";
|
|
4
|
+
export declare class HydrateUtils {
|
|
5
|
+
static hydrateDataFields(model: RWSModel<any>, collections_to_models: {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}, relOneData: RelOneMetaType<IRWSModel>, seriesHydrationfields: string[], fullDataMode: boolean, data: {
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
static hydrateRelations(model: RWSModel<any>, relManyData: RelManyMetaType<IRWSModel>, relOneData: RelOneMetaType<IRWSModel>, seriesHydrationfields: string[], fullDataMode: boolean, data: {
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HydrateUtils = void 0;
|
|
4
|
+
const TimeSeriesUtils_1 = require("./TimeSeriesUtils");
|
|
5
|
+
const RelationUtils_1 = require("./RelationUtils");
|
|
6
|
+
const ModelUtils_1 = require("./ModelUtils");
|
|
7
|
+
class HydrateUtils {
|
|
8
|
+
static async hydrateDataFields(model, collections_to_models, relOneData, seriesHydrationfields, fullDataMode, data) {
|
|
9
|
+
const timeSeriesIds = TimeSeriesUtils_1.TimeSeriesUtils.getTimeSeriesModelFields(model);
|
|
10
|
+
for (const key in data) {
|
|
11
|
+
if (data.hasOwnProperty(key)) {
|
|
12
|
+
if (!fullDataMode && (model).constructor._CUT_KEYS.includes(key)) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (Object.keys(relOneData).includes(key)) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (seriesHydrationfields.includes(key)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const timeSeriesMetaData = timeSeriesIds[key];
|
|
22
|
+
if (timeSeriesMetaData) {
|
|
23
|
+
model[key] = data[key];
|
|
24
|
+
const seriesModel = collections_to_models[timeSeriesMetaData.collection];
|
|
25
|
+
const dataModels = await seriesModel.findBy({
|
|
26
|
+
id: { in: data[key] }
|
|
27
|
+
});
|
|
28
|
+
seriesHydrationfields.push(timeSeriesMetaData.hydrationField);
|
|
29
|
+
model[timeSeriesMetaData.hydrationField] = dataModels;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
model[key] = data[key];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
static async hydrateRelations(model, relManyData, relOneData, seriesHydrationfields, fullDataMode, data) {
|
|
38
|
+
// Handle many-to-many relations
|
|
39
|
+
for (const key in relManyData) {
|
|
40
|
+
if (!fullDataMode && model.constructor._CUT_KEYS.includes(key)) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const relMeta = relManyData[key];
|
|
44
|
+
// console.log({relMeta});
|
|
45
|
+
const relationEnabled = !RelationUtils_1.RelationUtils.checkRelDisabled(model, relMeta.key);
|
|
46
|
+
if (relationEnabled) {
|
|
47
|
+
model[relMeta.key] = await relMeta.inversionModel.findBy({
|
|
48
|
+
conditions: {
|
|
49
|
+
[relMeta.foreignKey]: data.id
|
|
50
|
+
},
|
|
51
|
+
allowRelations: false
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Handle one-to-one relations
|
|
56
|
+
for (const key in relOneData) {
|
|
57
|
+
if (!fullDataMode && model.constructor._CUT_KEYS.includes(key)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const relMeta = relOneData[key];
|
|
61
|
+
const relationEnabled = !RelationUtils_1.RelationUtils.checkRelDisabled(model, relMeta.key);
|
|
62
|
+
if (!data[relMeta.hydrationField] && relMeta.required) {
|
|
63
|
+
throw new Error(`Relation field "${relMeta.hydrationField}" is required in model ${this.constructor.name}.`);
|
|
64
|
+
}
|
|
65
|
+
if (relationEnabled && data[relMeta.hydrationField]) {
|
|
66
|
+
model[relMeta.key] = await relMeta.model.findOneBy({ conditions: { [relMeta.foreignKey]: data[relMeta.hydrationField] } }, { allowRelations: false });
|
|
67
|
+
}
|
|
68
|
+
else if (relationEnabled && !data[relMeta.hydrationField] && data[relMeta.key]) {
|
|
69
|
+
const newRelModel = await relMeta.model.create(data[relMeta.key]);
|
|
70
|
+
model[relMeta.key] = await newRelModel.save();
|
|
71
|
+
}
|
|
72
|
+
const cutKeys = model.constructor._CUT_KEYS;
|
|
73
|
+
const trackedField = Object.keys((await ModelUtils_1.ModelUtils.getModelAnnotations(model.constructor))).includes(relMeta.hydrationField);
|
|
74
|
+
if (!cutKeys.includes(relMeta.hydrationField) && !trackedField) {
|
|
75
|
+
cutKeys.push(relMeta.hydrationField);
|
|
76
|
+
}
|
|
77
|
+
// seriesHydrationfields.push(relMeta.hydrationField);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.HydrateUtils = HydrateUtils;
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { RWSModel, OpModelType } from '../models/_model';
|
|
3
|
+
import { ModelUtils } from '../models/utils/ModelUtils';
|
|
3
4
|
|
|
4
5
|
export interface InverseRelationOpts {
|
|
5
6
|
key: string,
|
|
@@ -10,17 +11,57 @@ export interface InverseRelationOpts {
|
|
|
10
11
|
mappingName?: string
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
function guessForeignKey(inversionModel: OpModelType<RWSModel<any>>, bindingModel: OpModelType<RWSModel<any>>, decoratorsData: any)
|
|
15
|
+
{
|
|
16
|
+
let key: string | null = null;
|
|
17
|
+
let defaultKey = `${bindingModel._collection}_id`;
|
|
18
|
+
|
|
19
|
+
const relDecorators: Record<string, {
|
|
20
|
+
annotationType: string;
|
|
21
|
+
metadata: any;
|
|
22
|
+
}> = {};
|
|
23
|
+
|
|
24
|
+
const trackDecorators: Record<string, {
|
|
25
|
+
annotationType: string;
|
|
26
|
+
metadata: any;
|
|
27
|
+
}> = {};
|
|
28
|
+
|
|
29
|
+
if(Object.keys(trackDecorators).includes(key)){
|
|
30
|
+
return key;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for(const decKey of Object.keys(decoratorsData)){
|
|
34
|
+
const dec = decoratorsData[decKey];
|
|
35
|
+
if(dec.annotationType === 'Relation'){
|
|
36
|
+
relDecorators[decKey] = dec;
|
|
37
|
+
}
|
|
38
|
+
if(dec.annotationType === 'TrackType'){
|
|
39
|
+
trackDecorators[decKey] = dec;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for(const relKey of Object.keys(relDecorators)){
|
|
44
|
+
const prodMeta = relDecorators[relKey]?.metadata;
|
|
45
|
+
if(prodMeta && prodMeta.relatedTo._collection === bindingModel._collection){
|
|
46
|
+
return prodMeta.relationField;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return key;
|
|
51
|
+
}
|
|
52
|
+
|
|
13
53
|
function InverseRelation(inversionModel: () => OpModelType<RWSModel<any>>, sourceModel: () => OpModelType<RWSModel<any>>, relationOptions: Partial<InverseRelationOpts> = null) {
|
|
14
54
|
return function (target: any, key: string) {
|
|
15
|
-
const metadataPromise = Promise.resolve().then(() => {
|
|
55
|
+
const metadataPromise = Promise.resolve().then(async () => {
|
|
16
56
|
const model = inversionModel();
|
|
17
57
|
const source = sourceModel();
|
|
58
|
+
const decoratorsData = await ModelUtils.getModelAnnotations(model);
|
|
18
59
|
|
|
19
60
|
const metaOpts: InverseRelationOpts = {
|
|
20
61
|
...relationOptions,
|
|
21
62
|
key,
|
|
22
63
|
inversionModel: model,
|
|
23
|
-
foreignKey: relationOptions && relationOptions.foreignKey ? relationOptions.foreignKey :
|
|
64
|
+
foreignKey: relationOptions && relationOptions.foreignKey ? relationOptions.foreignKey : guessForeignKey(model, source, decoratorsData),
|
|
24
65
|
// Generate a unique relation name if one is not provided
|
|
25
66
|
relationName: relationOptions && relationOptions.relationName ?
|
|
26
67
|
relationOptions.relationName.toLowerCase() :
|
package/src/helper/DbHelper.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IDbConfigHandler, IDbConfigParams, IdGeneratorOptions } from '../types/DbConfigHandler';
|
|
2
2
|
import { OpModelType } from '../models/_model';
|
|
3
3
|
import { DBService } from '../services/DBService';
|
|
4
|
+
import { rwsShell } from '@rws-framework/console';
|
|
4
5
|
|
|
5
6
|
import {
|
|
6
7
|
DbUtils,
|
|
@@ -41,6 +42,14 @@ export class DbHelper {
|
|
|
41
42
|
static async pushDBModels(configService: IDbConfigHandler, dbService: DBService, leaveFile = false): Promise<void> {
|
|
42
43
|
return SchemaGenerator.pushDBModels(configService, dbService, leaveFile);
|
|
43
44
|
}
|
|
45
|
+
|
|
46
|
+
static async migrateDBModels(configService: IDbConfigHandler, dbService: DBService, leaveFile = false): Promise<void> {
|
|
47
|
+
process.env = { ...process.env, [this.dbUrlVarName]: configService.get('db_url') };
|
|
48
|
+
|
|
49
|
+
const [_, schemaPath] = DbUtils.getSchemaDir();
|
|
50
|
+
|
|
51
|
+
await rwsShell.runCommand(`${DbUtils.detectInstaller()} prisma migrate dev --create-only --schema=${schemaPath}`, process.cwd());
|
|
52
|
+
}
|
|
44
53
|
|
|
45
54
|
/**
|
|
46
55
|
* Generate model sections for the schema
|
package/src/helper/db/index.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -62,7 +62,7 @@ datasource db {
|
|
|
62
62
|
if(
|
|
63
63
|
!model._NO_ID
|
|
64
64
|
){
|
|
65
|
-
section += `\t${DbUtils.generateId(dbType, modelMetadatas
|
|
65
|
+
section += `\t${DbUtils.generateId(dbType, modelMetadatas)}\n`;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
for (const key in modelMetadatas) {
|
|
@@ -108,7 +108,11 @@ datasource db {
|
|
|
108
108
|
const relationFieldName = modelMetadata.relationField ? modelMetadata.relationField : key.toLowerCase() + '_' + modelMetadata.relationField.toLowerCase();
|
|
109
109
|
|
|
110
110
|
const relatedToField = modelMetadata.relatedToField || 'id';
|
|
111
|
-
const bindingFieldExists = !!modelMetadatas[relationFieldName];
|
|
111
|
+
const bindingFieldExists = !!modelMetadatas[relationFieldName];
|
|
112
|
+
|
|
113
|
+
if(modelMetadata.required === false){
|
|
114
|
+
requiredString = '?';
|
|
115
|
+
}
|
|
112
116
|
|
|
113
117
|
if (isMany) {
|
|
114
118
|
// Add an inverse field to the related model if it doesn't exist
|
|
@@ -178,21 +182,37 @@ datasource db {
|
|
|
178
182
|
}
|
|
179
183
|
} else if (annotationType === 'TrackType') {
|
|
180
184
|
const trackMeta = modelMetadata as ITrackerMetaOpts;
|
|
181
|
-
const tags: string[] = trackMeta.tags.map((item: string) => '@' + item);
|
|
185
|
+
const tags: string[] = trackMeta.tags.map((item: string) => '@' + item);
|
|
186
|
+
|
|
187
|
+
if(key === 'id' && model._NO_ID && !model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes('id'))){
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
182
190
|
|
|
183
191
|
if(trackMeta.unique){
|
|
184
192
|
const fieldDetail: string | null = typeof trackMeta.unique === 'string' ? trackMeta.unique : null;
|
|
185
193
|
tags.push(`@unique(${fieldDetail ? `map: "${fieldDetail}"` : ''})`);
|
|
186
194
|
}
|
|
187
195
|
|
|
196
|
+
if(!trackMeta.required){
|
|
197
|
+
requiredString = '?';
|
|
198
|
+
}
|
|
199
|
+
|
|
188
200
|
if (trackMeta.isArray || trackMeta.type.name === 'Array') {
|
|
189
201
|
requiredString = '';
|
|
190
202
|
}
|
|
191
203
|
|
|
204
|
+
if(model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes(key))){
|
|
205
|
+
requiredString = '';
|
|
206
|
+
}
|
|
207
|
+
|
|
192
208
|
// Process any database-specific options from the metadata
|
|
193
209
|
const dbSpecificTags = TypeConverter.processTypeOptions(trackMeta as { tags: string[], dbOptions: IDbOpts['dbOptions'] }, dbType);
|
|
194
210
|
tags.push(...dbSpecificTags);
|
|
195
211
|
|
|
212
|
+
if(modelName === 'category_translation' && key === 'meta_keywords'){
|
|
213
|
+
console.log({requiredString, trackMeta});
|
|
214
|
+
}
|
|
215
|
+
|
|
196
216
|
section += `\t${key} ${TypeConverter.toConfigCase(trackMeta, dbType, key === 'id')}${requiredString} ${tags.join(' ')}\n`;
|
|
197
217
|
}
|
|
198
218
|
}
|
|
File without changes
|
package/src/helper/db/utils.ts
CHANGED
|
@@ -40,7 +40,7 @@ export class DbUtils {
|
|
|
40
40
|
static generateId(
|
|
41
41
|
dbType: IDbConfigParams['db_type'],
|
|
42
42
|
modelMeta: Record<string, { annotationType: string, metadata: IIdMetaOpts }>,
|
|
43
|
-
|
|
43
|
+
optional = false
|
|
44
44
|
): string {
|
|
45
45
|
let useUuid = false;
|
|
46
46
|
let field = 'id';
|
|
@@ -53,10 +53,7 @@ export class DbUtils {
|
|
|
53
53
|
if(key !== 'id'){
|
|
54
54
|
if(annotationType == 'IdType'){
|
|
55
55
|
const dbSpecificTags = TypeConverter.processTypeOptions({ tags: [], dbOptions: modelMetadata.dbOptions }, dbType);
|
|
56
|
-
tags.push(...dbSpecificTags);
|
|
57
|
-
if(debug){
|
|
58
|
-
console.log({modelMetadata: modelMetadata.dbOptions});
|
|
59
|
-
}
|
|
56
|
+
tags.push(...dbSpecificTags);
|
|
60
57
|
|
|
61
58
|
field = key;
|
|
62
59
|
|
|
@@ -76,29 +73,35 @@ export class DbUtils {
|
|
|
76
73
|
}
|
|
77
74
|
|
|
78
75
|
let idString: string;
|
|
76
|
+
|
|
77
|
+
let reqStr = '';
|
|
78
|
+
|
|
79
|
+
if(optional){
|
|
80
|
+
reqStr = '?';
|
|
81
|
+
}
|
|
79
82
|
|
|
80
83
|
switch (dbType) {
|
|
81
84
|
case 'mongodb':
|
|
82
|
-
idString = `${field} String @id @default(auto()) @map("_id") @db.ObjectId`;
|
|
85
|
+
idString = `${field} String${reqStr} @id @default(auto()) @map("_id") @db.ObjectId`;
|
|
83
86
|
break;
|
|
84
87
|
|
|
85
88
|
case 'mysql':
|
|
86
89
|
idString = useUuid
|
|
87
|
-
? `${field} String @id @default(uuid())`
|
|
88
|
-
: `${field} Int @id @default(autoincrement())`;
|
|
90
|
+
? `${field} String${reqStr} @id @default(uuid())`
|
|
91
|
+
: `${field} Int${reqStr} @id @default(autoincrement())`;
|
|
89
92
|
break;
|
|
90
93
|
|
|
91
94
|
case 'postgresql':
|
|
92
95
|
case 'postgres':
|
|
93
96
|
idString = useUuid
|
|
94
|
-
? `${field} String @id @default(uuid())`
|
|
95
|
-
: `${field} Int @id @default(autoincrement())`;
|
|
97
|
+
? `${field} String${reqStr} @id @default(uuid())`
|
|
98
|
+
: `${field} Int${reqStr} @id @default(autoincrement())`;
|
|
96
99
|
break;
|
|
97
100
|
|
|
98
101
|
case 'sqlite':
|
|
99
|
-
idString = `${field} Int @id @default(autoincrement())`;
|
|
102
|
+
idString = `${field} Int${reqStr} @id @default(autoincrement())`;
|
|
100
103
|
break;
|
|
101
|
-
}
|
|
104
|
+
}
|
|
102
105
|
|
|
103
106
|
if(tags.length){
|
|
104
107
|
idString += ' '+tags.join(' ');
|
|
@@ -108,9 +111,6 @@ export class DbUtils {
|
|
|
108
111
|
throw new Error(`DB type "${dbType}" is not supported!`);
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
if(debug){
|
|
112
|
-
console.log({idString, useUuid});
|
|
113
|
-
}
|
|
114
114
|
|
|
115
115
|
return idString;
|
|
116
116
|
}
|
|
@@ -11,6 +11,9 @@ import { ModelUtils } from '../utils/ModelUtils';
|
|
|
11
11
|
// import timeSeriesModel from './TimeSeriesModel';
|
|
12
12
|
import { DBService } from '../../services/DBService';
|
|
13
13
|
import { ISuperTagData } from '../../decorators/RWSCollection';
|
|
14
|
+
import { RelManyMetaType, RelOneMetaType } from '../types/RelationTypes';
|
|
15
|
+
import { IRWSModel } from '../../types/IRWSModel';
|
|
16
|
+
import { HydrateUtils } from '../utils/HydrateUtils';
|
|
14
17
|
|
|
15
18
|
class RWSModel<T> implements IModel {
|
|
16
19
|
static services: IRWSModelServices = {};
|
|
@@ -97,9 +100,7 @@ class RWSModel<T> implements IModel {
|
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
public async _asyncFill(data: any, fullDataMode = false, allowRelations = true): Promise<T> {
|
|
100
|
-
const collections_to_models: {[key: string]: any} = {};
|
|
101
|
-
const timeSeriesIds = TimeSeriesUtils.getTimeSeriesModelFields(this);
|
|
102
|
-
|
|
103
|
+
const collections_to_models: {[key: string]: any} = {};
|
|
103
104
|
const classFields = FieldsHelper.getAllClassFields(this.constructor);
|
|
104
105
|
|
|
105
106
|
// Get both relation metadata types asynchronously
|
|
@@ -113,92 +114,19 @@ class RWSModel<T> implements IModel {
|
|
|
113
114
|
});
|
|
114
115
|
|
|
115
116
|
const seriesHydrationfields: string[] = [];
|
|
116
|
-
|
|
117
|
-
if (allowRelations) {
|
|
118
|
-
// Handle many-to-many relations
|
|
119
|
-
for (const key in relManyData) {
|
|
120
|
-
if(!fullDataMode && (this as any).constructor._CUT_KEYS.includes(key)){
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
117
|
|
|
124
|
-
const relMeta = relManyData[key];
|
|
125
|
-
|
|
126
|
-
const relationEnabled = !RelationUtils.checkRelDisabled(this, relMeta.key);
|
|
127
|
-
if (relationEnabled) {
|
|
128
|
-
this[relMeta.key] = await relMeta.inversionModel.findBy({
|
|
129
|
-
conditions: {
|
|
130
|
-
[relMeta.foreignKey]: data.id
|
|
131
|
-
},
|
|
132
|
-
allowRelations: false
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Handle one-to-one relations
|
|
138
|
-
for (const key in relOneData) {
|
|
139
|
-
if(!fullDataMode && (this as any).constructor._CUT_KEYS.includes(key)){
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
118
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if(!data[relMeta.hydrationField] && relMeta.required){
|
|
147
|
-
throw new Error(`Relation field "${relMeta.hydrationField}" is required in model ${this.constructor.name}.`)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (relationEnabled && data[relMeta.hydrationField]) {
|
|
151
|
-
this[relMeta.key] = await relMeta.model.find(data[relMeta.hydrationField], { allowRelations: false });
|
|
152
|
-
}
|
|
153
|
-
else if(relationEnabled && !data[relMeta.hydrationField] && data[relMeta.key]){
|
|
154
|
-
const newRelModel: RWSModel<any> = await relMeta.model.create(data[relMeta.key]);
|
|
155
|
-
this[relMeta.key] = await newRelModel.save();
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const cutKeys = ((this.constructor as any)._CUT_KEYS as string[]);
|
|
159
|
-
|
|
160
|
-
if(!cutKeys.includes(relMeta.hydrationField)){
|
|
161
|
-
cutKeys.push(relMeta.hydrationField)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
119
|
+
if (allowRelations) {
|
|
120
|
+
await HydrateUtils.hydrateRelations(this, relManyData, relOneData, seriesHydrationfields, fullDataMode, data);
|
|
164
121
|
}
|
|
165
122
|
|
|
166
123
|
// Process regular fields and time series
|
|
167
|
-
|
|
168
|
-
if (data.hasOwnProperty(key)) {
|
|
169
|
-
if(!fullDataMode && (this as any).constructor._CUT_KEYS.includes(key)){
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (Object.keys(relOneData).includes(key)) {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (seriesHydrationfields.includes(key)) {
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const timeSeriesMetaData = timeSeriesIds[key];
|
|
182
|
-
|
|
183
|
-
if (timeSeriesMetaData) {
|
|
184
|
-
this[key] = data[key];
|
|
185
|
-
const seriesModel = collections_to_models[timeSeriesMetaData.collection];
|
|
186
|
-
|
|
187
|
-
const dataModels = await seriesModel.findBy({
|
|
188
|
-
id: { in: data[key] }
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
seriesHydrationfields.push(timeSeriesMetaData.hydrationField);
|
|
192
|
-
|
|
193
|
-
this[timeSeriesMetaData.hydrationField] = dataModels;
|
|
194
|
-
} else {
|
|
195
|
-
this[key] = data[key];
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
124
|
+
await HydrateUtils.hydrateDataFields(this, collections_to_models, relOneData, seriesHydrationfields, fullDataMode, data);
|
|
199
125
|
|
|
200
126
|
return this as any as T;
|
|
201
|
-
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
202
130
|
|
|
203
131
|
private getModelScalarFields(model: RWSModel<T>): string[] {
|
|
204
132
|
return ModelUtils.getModelScalarFields(model);
|
|
@@ -411,7 +339,7 @@ class RWSModel<T> implements IModel {
|
|
|
411
339
|
|
|
412
340
|
public static async find<T extends RWSModel<T>>(
|
|
413
341
|
this: OpModelType<T>,
|
|
414
|
-
id: string,
|
|
342
|
+
id: string | number,
|
|
415
343
|
findParams: Omit<FindByType, 'conditions'> = null
|
|
416
344
|
): Promise<T | null> {
|
|
417
345
|
const ordering = findParams?.ordering ?? null;
|
|
@@ -445,12 +373,15 @@ class RWSModel<T> implements IModel {
|
|
|
445
373
|
const collection = Reflect.get(this, '_collection');
|
|
446
374
|
this.checkForInclusionWithThrow(this.name);
|
|
447
375
|
try {
|
|
448
|
-
const
|
|
376
|
+
const paginateParams = findParams?.pagination ? findParams?.pagination : undefined;
|
|
377
|
+
const dbData = await this.services.dbService.findBy(collection, conditions, fields, ordering, paginateParams);
|
|
378
|
+
|
|
449
379
|
if (dbData.length) {
|
|
450
380
|
const instanced: T[] = [];
|
|
451
381
|
|
|
452
382
|
for (const data of dbData) {
|
|
453
383
|
const inst: T = new (this as { new(): T })();
|
|
384
|
+
|
|
454
385
|
instanced.push((await inst._asyncFill(data, fullData,allowRelations)) as T);
|
|
455
386
|
}
|
|
456
387
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -27,7 +27,7 @@ export interface OpModelType<T> {
|
|
|
27
27
|
): Promise<T | null>;
|
|
28
28
|
find<T extends RWSModel<T>>(
|
|
29
29
|
this: OpModelType<T>,
|
|
30
|
-
id: string,
|
|
30
|
+
id: string | number,
|
|
31
31
|
findParams?: Omit<FindByType, 'conditions'>
|
|
32
32
|
): Promise<T | null>;
|
|
33
33
|
findBy<T extends RWSModel<T>>(
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { RWSModel } from "../core/RWSModel";
|
|
2
|
+
import { RelManyMetaType, RelOneMetaType } from "../types/RelationTypes";
|
|
3
|
+
import { IRWSModel } from "../../types/IRWSModel";
|
|
4
|
+
import { TimeSeriesUtils } from "./TimeSeriesUtils";
|
|
5
|
+
import { RelationUtils } from "./RelationUtils";
|
|
6
|
+
import { OpModelType } from "..";
|
|
7
|
+
import { ModelUtils } from "./ModelUtils";
|
|
8
|
+
|
|
9
|
+
export class HydrateUtils {
|
|
10
|
+
static async hydrateDataFields(model: RWSModel<any>, collections_to_models: {[key: string]: any}, relOneData: RelOneMetaType<IRWSModel>, seriesHydrationfields: string[], fullDataMode: boolean, data: {[key: string] : any}){
|
|
11
|
+
const timeSeriesIds = TimeSeriesUtils.getTimeSeriesModelFields(model);
|
|
12
|
+
for (const key in data) {
|
|
13
|
+
if (data.hasOwnProperty(key)) {
|
|
14
|
+
if(!fullDataMode && ((model).constructor as OpModelType<any>)._CUT_KEYS.includes(key)){
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (Object.keys(relOneData).includes(key)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (seriesHydrationfields.includes(key)) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const timeSeriesMetaData = timeSeriesIds[key];
|
|
28
|
+
|
|
29
|
+
if (timeSeriesMetaData) {
|
|
30
|
+
model[key] = data[key];
|
|
31
|
+
const seriesModel = collections_to_models[timeSeriesMetaData.collection];
|
|
32
|
+
|
|
33
|
+
const dataModels = await seriesModel.findBy({
|
|
34
|
+
id: { in: data[key] }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
seriesHydrationfields.push(timeSeriesMetaData.hydrationField);
|
|
38
|
+
|
|
39
|
+
model[timeSeriesMetaData.hydrationField] = dataModels;
|
|
40
|
+
} else {
|
|
41
|
+
model[key] = data[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static async hydrateRelations(model: RWSModel<any>, relManyData: RelManyMetaType<IRWSModel>, relOneData: RelOneMetaType<IRWSModel>, seriesHydrationfields: string[], fullDataMode: boolean, data: {[key: string] : any})
|
|
48
|
+
{
|
|
49
|
+
// Handle many-to-many relations
|
|
50
|
+
for (const key in relManyData) {
|
|
51
|
+
if(!fullDataMode && (model as any).constructor._CUT_KEYS.includes(key)){
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const relMeta = relManyData[key];
|
|
56
|
+
|
|
57
|
+
// console.log({relMeta});
|
|
58
|
+
|
|
59
|
+
const relationEnabled = !RelationUtils.checkRelDisabled(model, relMeta.key);
|
|
60
|
+
if (relationEnabled) {
|
|
61
|
+
model[relMeta.key] = await relMeta.inversionModel.findBy({
|
|
62
|
+
conditions: {
|
|
63
|
+
[relMeta.foreignKey]: data.id
|
|
64
|
+
},
|
|
65
|
+
allowRelations: false
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Handle one-to-one relations
|
|
71
|
+
for (const key in relOneData) {
|
|
72
|
+
if(!fullDataMode && ((model as any).constructor as OpModelType<any>)._CUT_KEYS.includes(key)){
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const relMeta = relOneData[key];
|
|
77
|
+
const relationEnabled = !RelationUtils.checkRelDisabled(model, relMeta.key);
|
|
78
|
+
|
|
79
|
+
if(!data[relMeta.hydrationField] && relMeta.required){
|
|
80
|
+
throw new Error(`Relation field "${relMeta.hydrationField}" is required in model ${this.constructor.name}.`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (relationEnabled && data[relMeta.hydrationField]) {
|
|
84
|
+
model[relMeta.key] = await relMeta.model.findOneBy({conditions: {[relMeta.foreignKey] : data[relMeta.hydrationField]}}, { allowRelations: false });
|
|
85
|
+
}
|
|
86
|
+
else if(relationEnabled && !data[relMeta.hydrationField] && data[relMeta.key]){
|
|
87
|
+
const newRelModel: RWSModel<any> = await relMeta.model.create(data[relMeta.key]);
|
|
88
|
+
model[relMeta.key] = await newRelModel.save();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const cutKeys = ((model.constructor as OpModelType<any>)._CUT_KEYS as string[]);
|
|
92
|
+
|
|
93
|
+
const trackedField = Object.keys((await ModelUtils.getModelAnnotations(model.constructor as OpModelType<any>))).includes(relMeta.hydrationField);
|
|
94
|
+
|
|
95
|
+
if(!cutKeys.includes(relMeta.hydrationField) && !trackedField){
|
|
96
|
+
cutKeys.push(relMeta.hydrationField)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// seriesHydrationfields.push(relMeta.hydrationField);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|