@rws-framework/db 3.0.1 → 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.
@@ -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 : `${source._collection}_id`,
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() :
@@ -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
@@ -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
@@ -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, false)}\n`;
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`;
@@ -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
- }>, debug?: boolean): string;
21
+ }>, optional?: boolean): string;
22
22
  }
23
23
  export declare const workspaceRootPath: string;
24
24
  export declare const moduleDirPath: string;
@@ -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, debug = false) {
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<import("..").RelOneMetaType<import("../..").IRWSModel>>;
35
+ static getRelationOneMeta(model: any, classFields: string[]): Promise<RelOneMetaType<IRWSModel>>;
34
36
  private getRelationManyMeta;
35
- static getRelationManyMeta(model: any, classFields: string[]): Promise<import("..").RelManyMetaType<import("../..").IRWSModel>>;
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;
@@ -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,73 +88,10 @@ class RWSModel {
88
88
  });
89
89
  const seriesHydrationfields = [];
90
90
  if (allowRelations) {
91
- // Handle many-to-many relations
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
- const trackedField = Object.keys((await ModelUtils_1.ModelUtils.getModelAnnotations(this.constructor))).includes(relMeta.hydrationField);
126
- if (!cutKeys.includes(relMeta.hydrationField) && !trackedField) {
127
- cutKeys.push(relMeta.hydrationField);
128
- }
129
- }
91
+ await HydrateUtils_1.HydrateUtils.hydrateRelations(this, relManyData, relOneData, seriesHydrationfields, fullDataMode, data);
130
92
  }
131
93
  // Process regular fields and time series
132
- for (const key in data) {
133
- if (data.hasOwnProperty(key)) {
134
- if (!fullDataMode && this.constructor._CUT_KEYS.includes(key)) {
135
- continue;
136
- }
137
- if (Object.keys(relOneData).includes(key)) {
138
- continue;
139
- }
140
- if (seriesHydrationfields.includes(key)) {
141
- continue;
142
- }
143
- const timeSeriesMetaData = timeSeriesIds[key];
144
- if (timeSeriesMetaData) {
145
- this[key] = data[key];
146
- const seriesModel = collections_to_models[timeSeriesMetaData.collection];
147
- const dataModels = await seriesModel.findBy({
148
- id: { in: data[key] }
149
- });
150
- seriesHydrationfields.push(timeSeriesMetaData.hydrationField);
151
- this[timeSeriesMetaData.hydrationField] = dataModels;
152
- }
153
- else {
154
- this[key] = data[key];
155
- }
156
- }
157
- }
94
+ await HydrateUtils_1.HydrateUtils.hydrateDataFields(this, collections_to_models, relOneData, seriesHydrationfields, fullDataMode, data);
158
95
  return this;
159
96
  }
160
97
  getModelScalarFields(model) {
@@ -334,7 +271,8 @@ class RWSModel {
334
271
  const collection = Reflect.get(this, '_collection');
335
272
  this.checkForInclusionWithThrow(this.name);
336
273
  try {
337
- const dbData = await this.services.dbService.findBy(collection, conditions, fields, ordering);
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);
338
276
  if (dbData.length) {
339
277
  const instanced = [];
340
278
  for (const data of dbData) {
@@ -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,7 +1,7 @@
1
1
  {
2
2
  "name": "@rws-framework/db",
3
3
  "private": false,
4
- "version": "3.0.1",
4
+ "version": "3.1.0",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -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 : `${source._collection}_id`,
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() :
@@ -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
@@ -62,7 +62,7 @@ datasource db {
62
62
  if(
63
63
  !model._NO_ID
64
64
  ){
65
- section += `\t${DbUtils.generateId(dbType, modelMetadatas, false)}\n`;
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
@@ -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
- debug = false
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
@@ -116,93 +117,16 @@ class RWSModel<T> implements IModel {
116
117
 
117
118
 
118
119
  if (allowRelations) {
119
- // Handle many-to-many relations
120
- for (const key in relManyData) {
121
- if(!fullDataMode && (this as any).constructor._CUT_KEYS.includes(key)){
122
- continue;
123
- }
124
-
125
- const relMeta = relManyData[key];
126
-
127
- const relationEnabled = !RelationUtils.checkRelDisabled(this, relMeta.key);
128
- if (relationEnabled) {
129
- this[relMeta.key] = await relMeta.inversionModel.findBy({
130
- conditions: {
131
- [relMeta.foreignKey]: data.id
132
- },
133
- allowRelations: false
134
- });
135
- }
136
- }
137
-
138
- // Handle one-to-one relations
139
- for (const key in relOneData) {
140
- if(!fullDataMode && (this as any).constructor._CUT_KEYS.includes(key)){
141
- continue;
142
- }
143
-
144
- const relMeta = relOneData[key];
145
- const relationEnabled = !RelationUtils.checkRelDisabled(this, relMeta.key);
146
-
147
- if(!data[relMeta.hydrationField] && relMeta.required){
148
- throw new Error(`Relation field "${relMeta.hydrationField}" is required in model ${this.constructor.name}.`)
149
- }
150
-
151
- if (relationEnabled && data[relMeta.hydrationField]) {
152
- this[relMeta.key] = await relMeta.model.find(data[relMeta.hydrationField], { allowRelations: false });
153
- }
154
- else if(relationEnabled && !data[relMeta.hydrationField] && data[relMeta.key]){
155
- const newRelModel: RWSModel<any> = await relMeta.model.create(data[relMeta.key]);
156
- this[relMeta.key] = await newRelModel.save();
157
- }
158
-
159
- const cutKeys = ((this.constructor as any)._CUT_KEYS as string[]);
160
-
161
- const trackedField = Object.keys((await ModelUtils.getModelAnnotations(this.constructor as any))).includes(relMeta.hydrationField);
162
-
163
- if(!cutKeys.includes(relMeta.hydrationField) && !trackedField){
164
- cutKeys.push(relMeta.hydrationField)
165
- }
166
- }
120
+ await HydrateUtils.hydrateRelations(this, relManyData, relOneData, seriesHydrationfields, fullDataMode, data);
167
121
  }
168
122
 
169
123
  // Process regular fields and time series
170
- for (const key in data) {
171
- if (data.hasOwnProperty(key)) {
172
- if(!fullDataMode && (this as any).constructor._CUT_KEYS.includes(key)){
173
- continue;
174
- }
175
-
176
- if (Object.keys(relOneData).includes(key)) {
177
- continue;
178
- }
179
-
180
- if (seriesHydrationfields.includes(key)) {
181
- continue;
182
- }
183
-
184
-
185
- const timeSeriesMetaData = timeSeriesIds[key];
186
-
187
- if (timeSeriesMetaData) {
188
- this[key] = data[key];
189
- const seriesModel = collections_to_models[timeSeriesMetaData.collection];
190
-
191
- const dataModels = await seriesModel.findBy({
192
- id: { in: data[key] }
193
- });
194
-
195
- seriesHydrationfields.push(timeSeriesMetaData.hydrationField);
196
-
197
- this[timeSeriesMetaData.hydrationField] = dataModels;
198
- } else {
199
- this[key] = data[key];
200
- }
201
- }
202
- }
124
+ await HydrateUtils.hydrateDataFields(this, collections_to_models, relOneData, seriesHydrationfields, fullDataMode, data);
203
125
 
204
126
  return this as any as T;
205
- }
127
+ }
128
+
129
+
206
130
 
207
131
  private getModelScalarFields(model: RWSModel<T>): string[] {
208
132
  return ModelUtils.getModelScalarFields(model);
@@ -449,7 +373,8 @@ class RWSModel<T> implements IModel {
449
373
  const collection = Reflect.get(this, '_collection');
450
374
  this.checkForInclusionWithThrow(this.name);
451
375
  try {
452
- const dbData = await this.services.dbService.findBy(collection, conditions, fields, ordering);
376
+ const paginateParams = findParams?.pagination ? findParams?.pagination : undefined;
377
+ const dbData = await this.services.dbService.findBy(collection, conditions, fields, ordering, paginateParams);
453
378
 
454
379
  if (dbData.length) {
455
380
  const instanced: 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
+ }