@rws-framework/db 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +74 -136
  2. package/dist/decorators/IdType.d.ts +0 -0
  3. package/dist/decorators/IdType.js +0 -0
  4. package/dist/decorators/TypeFunctions.d.ts +0 -0
  5. package/dist/decorators/TypeFunctions.js +0 -0
  6. package/dist/helper/db/index.d.ts +0 -0
  7. package/dist/helper/db/index.js +0 -0
  8. package/dist/helper/db/relation-manager.d.ts +0 -0
  9. package/dist/helper/db/relation-manager.js +0 -0
  10. package/dist/helper/db/schema-generator.d.ts +0 -0
  11. package/dist/helper/db/schema-generator.js +12 -0
  12. package/dist/helper/db/type-converter.d.ts +0 -0
  13. package/dist/helper/db/type-converter.js +0 -0
  14. package/dist/helper/db/utils.d.ts +0 -0
  15. package/dist/helper/db/utils.js +0 -0
  16. package/dist/models/core/RWSModel.d.ts +1 -1
  17. package/dist/models/core/RWSModel.js +2 -1
  18. package/dist/models/interfaces/IDbOpts.d.ts +0 -0
  19. package/dist/models/interfaces/IDbOpts.js +0 -0
  20. package/dist/models/interfaces/IIdOpts.d.ts +0 -0
  21. package/dist/models/interfaces/IIdOpts.js +0 -0
  22. package/dist/models/interfaces/IIdTypeOpts.d.ts +0 -0
  23. package/dist/models/interfaces/IIdTypeOpts.js +0 -0
  24. package/dist/models/interfaces/ITrackerOpts.d.ts +0 -0
  25. package/dist/models/interfaces/ITrackerOpts.js +0 -0
  26. package/dist/models/interfaces/OpModelType.d.ts +1 -1
  27. package/package.json +1 -1
  28. package/src/helper/db/index.ts +0 -0
  29. package/src/helper/db/relation-manager.ts +0 -0
  30. package/src/helper/db/schema-generator.ts +17 -1
  31. package/src/helper/db/type-converter.ts +0 -0
  32. package/src/helper/db/utils.ts +0 -0
  33. package/src/models/core/RWSModel.ts +13 -7
  34. package/src/models/interfaces/IDbOpts.ts +0 -0
  35. package/src/models/interfaces/IIdTypeOpts.ts +0 -0
  36. package/src/models/interfaces/ITrackerOpts.ts +0 -0
  37. package/src/models/interfaces/OpModelType.ts +1 -1
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 minor versions.
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, true) // second attribute is required = false
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
- import { RWSModel, OpModelType } from '@rws-framework/db';
118
+ export type CascadingSetup = 'Cascade' | 'Restrict' | 'NoAction' | 'SetNull';
116
119
 
117
- interface IRelationOpts {
120
+ export interface IRelationOpts {
118
121
  required?: boolean
119
- key?: string
120
- relationField?: string
121
- relatedToField?: string
122
- relatedTo: OpModelType<Model<any>>
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>>, required: boolean = false, relationField: string = null, relatedToField: string = 'id') {
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
- const metadataPromise = Promise.resolve().then(() => {
142
+
143
+ const metadataPromise = Promise.resolve().then(() => {
129
144
  const relatedTo = theModel();
130
- const metaOpts: IRelationOpts = {required, relatedTo, relatedToField};
131
- if(!relationField){
132
- metaOpts.relationField = relatedTo._collection + '_id';
133
- } else{
134
- metaOpts.relationField = relationField;
135
- }
136
- metaOpts.key = key;
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
- export {IRelationOpts};
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 '@rws-framework/db';
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
- function InverseRelation(inversionModel: () => OpModelType<RWSModel<any>>, sourceModel: () => OpModelType<RWSModel<any>>, foreignKey: string = null) {
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
- export {InverseRelationOpts};
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
- ```typescript
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -151,16 +151,28 @@ datasource db {
151
151
  else if (annotationType === 'TrackType') {
152
152
  const trackMeta = modelMetadata;
153
153
  const tags = trackMeta.tags.map((item) => '@' + item);
154
+ if (key === 'id' && model._NO_ID && !model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes('id'))) {
155
+ continue;
156
+ }
154
157
  if (trackMeta.unique) {
155
158
  const fieldDetail = typeof trackMeta.unique === 'string' ? trackMeta.unique : null;
156
159
  tags.push(`@unique(${fieldDetail ? `map: "${fieldDetail}"` : ''})`);
157
160
  }
161
+ if (!trackMeta.required) {
162
+ requiredString = '?';
163
+ }
158
164
  if (trackMeta.isArray || trackMeta.type.name === 'Array') {
159
165
  requiredString = '';
160
166
  }
167
+ if (model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes(key))) {
168
+ requiredString = '';
169
+ }
161
170
  // Process any database-specific options from the metadata
162
171
  const dbSpecificTags = type_converter_1.TypeConverter.processTypeOptions(trackMeta, dbType);
163
172
  tags.push(...dbSpecificTags);
173
+ if (modelName === 'category_translation' && key === 'meta_keywords') {
174
+ console.log({ requiredString, trackMeta });
175
+ }
164
176
  section += `\t${key} ${type_converter_1.TypeConverter.toConfigCase(trackMeta, dbType, key === 'id')}${requiredString} ${tags.join(' ')}\n`;
165
177
  }
166
178
  }
File without changes
File without changes
File without changes
File without changes
@@ -54,7 +54,7 @@ declare class RWSModel<T> implements IModel {
54
54
  sanitizeDBData(data: any): any;
55
55
  static watchCollection<T extends RWSModel<T>>(this: OpModelType<T>, preRun: () => void): Promise<any>;
56
56
  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>;
57
+ static find<T extends RWSModel<T>>(this: OpModelType<T>, id: string | number, findParams?: Omit<FindByType, 'conditions'>): Promise<T | null>;
58
58
  static findBy<T extends RWSModel<T>>(this: OpModelType<T>, findParams?: FindByType): Promise<T[]>;
59
59
  static delete<T extends RWSModel<T>>(this: OpModelType<T>, conditions: any): Promise<void>;
60
60
  delete<T extends RWSModel<T>>(): Promise<void>;
@@ -122,7 +122,8 @@ class RWSModel {
122
122
  this[relMeta.key] = await newRelModel.save();
123
123
  }
124
124
  const cutKeys = this.constructor._CUT_KEYS;
125
- if (!cutKeys.includes(relMeta.hydrationField)) {
125
+ const trackedField = Object.keys((await ModelUtils_1.ModelUtils.getModelAnnotations(this.constructor))).includes(relMeta.hydrationField);
126
+ if (!cutKeys.includes(relMeta.hydrationField) && !trackedField) {
126
127
  cutKeys.push(relMeta.hydrationField);
127
128
  }
128
129
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -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>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rws-framework/db",
3
3
  "private": false,
4
- "version": "3.0.0",
4
+ "version": "3.0.1",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
File without changes
File without changes
@@ -178,21 +178,37 @@ datasource db {
178
178
  }
179
179
  } else if (annotationType === 'TrackType') {
180
180
  const trackMeta = modelMetadata as ITrackerMetaOpts;
181
- const tags: string[] = trackMeta.tags.map((item: string) => '@' + item);
181
+ const tags: string[] = trackMeta.tags.map((item: string) => '@' + item);
182
+
183
+ if(key === 'id' && model._NO_ID && !model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes('id'))){
184
+ continue;
185
+ }
182
186
 
183
187
  if(trackMeta.unique){
184
188
  const fieldDetail: string | null = typeof trackMeta.unique === 'string' ? trackMeta.unique : null;
185
189
  tags.push(`@unique(${fieldDetail ? `map: "${fieldDetail}"` : ''})`);
186
190
  }
187
191
 
192
+ if(!trackMeta.required){
193
+ requiredString = '?';
194
+ }
195
+
188
196
  if (trackMeta.isArray || trackMeta.type.name === 'Array') {
189
197
  requiredString = '';
190
198
  }
191
199
 
200
+ if(model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes(key))){
201
+ requiredString = '';
202
+ }
203
+
192
204
  // Process any database-specific options from the metadata
193
205
  const dbSpecificTags = TypeConverter.processTypeOptions(trackMeta as { tags: string[], dbOptions: IDbOpts['dbOptions'] }, dbType);
194
206
  tags.push(...dbSpecificTags);
195
207
 
208
+ if(modelName === 'category_translation' && key === 'meta_keywords'){
209
+ console.log({requiredString, trackMeta});
210
+ }
211
+
196
212
  section += `\t${key} ${TypeConverter.toConfigCase(trackMeta, dbType, key === 'id')}${requiredString} ${tags.join(' ')}\n`;
197
213
  }
198
214
  }
File without changes
File without changes
@@ -113,7 +113,8 @@ class RWSModel<T> implements IModel {
113
113
  });
114
114
 
115
115
  const seriesHydrationfields: string[] = [];
116
-
116
+
117
+
117
118
  if (allowRelations) {
118
119
  // Handle many-to-many relations
119
120
  for (const key in relManyData) {
@@ -157,15 +158,17 @@ class RWSModel<T> implements IModel {
157
158
 
158
159
  const cutKeys = ((this.constructor as any)._CUT_KEYS as string[]);
159
160
 
160
- if(!cutKeys.includes(relMeta.hydrationField)){
161
+ const trackedField = Object.keys((await ModelUtils.getModelAnnotations(this.constructor as any))).includes(relMeta.hydrationField);
162
+
163
+ if(!cutKeys.includes(relMeta.hydrationField) && !trackedField){
161
164
  cutKeys.push(relMeta.hydrationField)
162
165
  }
163
166
  }
164
167
  }
165
168
 
166
169
  // Process regular fields and time series
167
- for (const key in data) {
168
- if (data.hasOwnProperty(key)) {
170
+ for (const key in data) {
171
+ if (data.hasOwnProperty(key)) {
169
172
  if(!fullDataMode && (this as any).constructor._CUT_KEYS.includes(key)){
170
173
  continue;
171
174
  }
@@ -176,7 +179,8 @@ class RWSModel<T> implements IModel {
176
179
 
177
180
  if (seriesHydrationfields.includes(key)) {
178
181
  continue;
179
- }
182
+ }
183
+
180
184
 
181
185
  const timeSeriesMetaData = timeSeriesIds[key];
182
186
 
@@ -411,7 +415,7 @@ class RWSModel<T> implements IModel {
411
415
 
412
416
  public static async find<T extends RWSModel<T>>(
413
417
  this: OpModelType<T>,
414
- id: string,
418
+ id: string | number,
415
419
  findParams: Omit<FindByType, 'conditions'> = null
416
420
  ): Promise<T | null> {
417
421
  const ordering = findParams?.ordering ?? null;
@@ -445,12 +449,14 @@ class RWSModel<T> implements IModel {
445
449
  const collection = Reflect.get(this, '_collection');
446
450
  this.checkForInclusionWithThrow(this.name);
447
451
  try {
448
- const dbData = await this.services.dbService.findBy(collection, conditions, fields, ordering);
452
+ const dbData = await this.services.dbService.findBy(collection, conditions, fields, ordering);
453
+
449
454
  if (dbData.length) {
450
455
  const instanced: T[] = [];
451
456
 
452
457
  for (const data of dbData) {
453
458
  const inst: T = new (this as { new(): T })();
459
+
454
460
  instanced.push((await inst._asyncFill(data, fullData,allowRelations)) as T);
455
461
  }
456
462
 
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>>(