@solidstarters/solid-core 1.2.24 → 1.2.26

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 (97) hide show
  1. package/dist/controllers/user.controller.d.ts +1 -0
  2. package/dist/controllers/user.controller.d.ts.map +1 -1
  3. package/dist/controllers/user.controller.js +14 -0
  4. package/dist/controllers/user.controller.js.map +1 -1
  5. package/dist/dtos/create-field-metadata.dto.d.ts +3 -4
  6. package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
  7. package/dist/dtos/create-field-metadata.dto.js +9 -15
  8. package/dist/dtos/create-field-metadata.dto.js.map +1 -1
  9. package/dist/dtos/create-media.dto.d.ts +3 -0
  10. package/dist/dtos/create-media.dto.d.ts.map +1 -1
  11. package/dist/dtos/create-media.dto.js +16 -1
  12. package/dist/dtos/create-media.dto.js.map +1 -1
  13. package/dist/entities/field-metadata.entity.d.ts +3 -4
  14. package/dist/entities/field-metadata.entity.d.ts.map +1 -1
  15. package/dist/entities/field-metadata.entity.js +8 -12
  16. package/dist/entities/field-metadata.entity.js.map +1 -1
  17. package/dist/entities/media.entity.d.ts +3 -0
  18. package/dist/entities/media.entity.d.ts.map +1 -1
  19. package/dist/entities/media.entity.js +19 -1
  20. package/dist/entities/media.entity.js.map +1 -1
  21. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts +2 -2
  22. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts.map +1 -1
  23. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js +4 -4
  24. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js.map +1 -1
  25. package/dist/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.d.ts +1 -1
  26. package/dist/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.d.ts.map +1 -1
  27. package/dist/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.js +3 -3
  28. package/dist/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.js.map +1 -1
  29. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts +2 -2
  30. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts.map +1 -1
  31. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js +4 -4
  32. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js.map +1 -1
  33. package/dist/helpers/schematic.service.d.ts +1 -0
  34. package/dist/helpers/schematic.service.d.ts.map +1 -1
  35. package/dist/helpers/schematic.service.js +3 -0
  36. package/dist/helpers/schematic.service.js.map +1 -1
  37. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  38. package/dist/seeders/module-metadata-seeder.service.js +14 -2
  39. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  40. package/dist/seeders/seed-data/solid-core-metadata.json +189 -84
  41. package/dist/services/authentication.service.d.ts.map +1 -1
  42. package/dist/services/authentication.service.js +1 -0
  43. package/dist/services/authentication.service.js.map +1 -1
  44. package/dist/services/crud.service.js +9 -9
  45. package/dist/services/crud.service.js.map +1 -1
  46. package/dist/services/field-metadata.service.d.ts.map +1 -1
  47. package/dist/services/field-metadata.service.js +14 -21
  48. package/dist/services/field-metadata.service.js.map +1 -1
  49. package/dist/services/media.service.d.ts.map +1 -1
  50. package/dist/services/media.service.js +17 -3
  51. package/dist/services/media.service.js.map +1 -1
  52. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
  53. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +4 -1
  54. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  55. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  56. package/dist/services/mediaStorageProviders/file-storage-provider.js +6 -3
  57. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  58. package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
  59. package/dist/services/menu-item-metadata.service.js +10 -1
  60. package/dist/services/menu-item-metadata.service.js.map +1 -1
  61. package/dist/services/model-metadata.service.d.ts.map +1 -1
  62. package/dist/services/model-metadata.service.js +1 -0
  63. package/dist/services/model-metadata.service.js.map +1 -1
  64. package/dist/services/role-metadata.service.d.ts +1 -1
  65. package/dist/services/role-metadata.service.d.ts.map +1 -1
  66. package/dist/services/role-metadata.service.js +1 -1
  67. package/dist/services/role-metadata.service.js.map +1 -1
  68. package/dist/services/user.service.d.ts +1 -0
  69. package/dist/services/user.service.d.ts.map +1 -1
  70. package/dist/services/user.service.js +14 -0
  71. package/dist/services/user.service.js.map +1 -1
  72. package/dist/services/view-metadata.service.js +1 -1
  73. package/dist/services/view-metadata.service.js.map +1 -1
  74. package/dist/tsconfig.tsbuildinfo +1 -1
  75. package/package.json +1 -1
  76. package/src/controllers/user.controller.ts +8 -0
  77. package/src/dtos/create-field-metadata.dto.ts +7 -12
  78. package/src/dtos/create-media.dto.ts +15 -3
  79. package/src/entities/field-metadata.entity.ts +6 -9
  80. package/src/entities/media.entity.ts +9 -0
  81. package/src/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.ts +6 -6
  82. package/src/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.ts +4 -4
  83. package/src/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.ts +6 -6
  84. package/src/helpers/schematic.service.ts +5 -1
  85. package/src/seeders/module-metadata-seeder.service.ts +15 -3
  86. package/src/seeders/seed-data/solid-core-metadata.json +189 -84
  87. package/src/services/authentication.service.ts +2 -0
  88. package/src/services/crud.service.ts +9 -9
  89. package/src/services/field-metadata.service.ts +14 -21
  90. package/src/services/media.service.ts +35 -5
  91. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +4 -1
  92. package/src/services/mediaStorageProviders/file-storage-provider.ts +6 -3
  93. package/src/services/menu-item-metadata.service.ts +8 -2
  94. package/src/services/model-metadata.service.ts +1 -0
  95. package/src/services/role-metadata.service.ts +2 -2
  96. package/src/services/user.service.ts +15 -0
  97. package/src/services/view-metadata.service.ts +2 -2
@@ -36,7 +36,7 @@ export class FieldMetadataService {
36
36
  // Update the inverse field in the db
37
37
  const savedInverseField = await this.updateInverseFieldInDb(field, fieldRepository, modelRepository);
38
38
  // Update the inverse field in the file
39
- this.updateRelationInverseFieldInFile(savedInverseField, field.relationModelSingularName, field.model.module.name);
39
+ this.updateRelationInverseFieldInFile(savedInverseField, field.relationCoModelSingularName, field.model.module.name);
40
40
  }
41
41
 
42
42
  private async updateInverseFieldInDb(field: FieldMetadata, fieldRepository: Repository<FieldMetadata>, modelRepository: Repository<ModelMetadata>): Promise<FieldMetadata> {
@@ -48,7 +48,7 @@ export class FieldMetadataService {
48
48
  // const {id, createdAt, updatedAt, deletedAt, ...fieldKeys} = field;
49
49
  switch (field.relationType) {
50
50
  case RelationType.manyToOne: {
51
- const fieldName = field.relationModelFieldName || `${modelName}s`;
51
+ const fieldName = field.relationCoModelFieldName || `${modelName}s`;
52
52
  const inverseField: FieldMetadata = {
53
53
  ...field,
54
54
  name: fieldName,
@@ -57,11 +57,11 @@ export class FieldMetadataService {
57
57
  type: SolidFieldType.relation,
58
58
  isSystem: field.isSystem,
59
59
  relationType: RelationType.oneToMany,
60
- relationModelSingularName: modelName,
60
+ relationCoModelSingularName: modelName,
61
61
  relationCreateInverse: true,
62
62
  relationCascade: field.relationCascade,
63
63
  relationModelModuleName: moduleName,
64
- relationModelFieldName: field.name,
64
+ relationCoModelFieldName: field.name,
65
65
  required: false,
66
66
  unique: false,
67
67
  index: false,
@@ -70,8 +70,6 @@ export class FieldMetadataService {
70
70
  model: relationModel,
71
71
  columnName:null,
72
72
  relationJoinTableName: null,
73
- relationJoinColumnName: null,
74
- joinColumnName: null,
75
73
  id : null,
76
74
  }
77
75
 
@@ -80,7 +78,7 @@ export class FieldMetadataService {
80
78
  return savedField;
81
79
  }
82
80
  case RelationType.oneToMany: {
83
- const fieldName = field.relationModelFieldName || `${modelName}`;
81
+ const fieldName = field.relationCoModelFieldName || `${modelName}`;
84
82
  const inverseField: FieldMetadata = {
85
83
  ...field,
86
84
  name: fieldName,
@@ -89,11 +87,11 @@ export class FieldMetadataService {
89
87
  type: SolidFieldType.relation,
90
88
  isSystem: field.isSystem,
91
89
  relationType: RelationType.manyToOne,
92
- relationModelSingularName: modelName,
90
+ relationCoModelSingularName: modelName,
93
91
  relationCreateInverse: true,
94
92
  relationCascade: null,
95
93
  relationModelModuleName: moduleName,
96
- relationModelFieldName: field.name,
94
+ relationCoModelFieldName: field.name,
97
95
  required: false,
98
96
  unique: false,
99
97
  index: false,
@@ -102,8 +100,6 @@ export class FieldMetadataService {
102
100
  model: relationModel,
103
101
  columnName:null,
104
102
  relationJoinTableName: null,
105
- relationJoinColumnName: null,
106
- joinColumnName: null,
107
103
  id : null,
108
104
  }
109
105
 
@@ -112,7 +108,7 @@ export class FieldMetadataService {
112
108
  return savedField;
113
109
  }
114
110
  case RelationType.manyTomany: {
115
- const fieldName = field.relationModelFieldName;
111
+ const fieldName = field.relationCoModelFieldName;
116
112
  // Logic to create a manyToMany inverse field definition
117
113
  const inverseFieldManyToMany: FieldMetadata = {
118
114
  ...field,
@@ -122,11 +118,11 @@ export class FieldMetadataService {
122
118
  type: SolidFieldType.relation,
123
119
  isSystem: field.isSystem,
124
120
  relationType: RelationType.manyTomany,
125
- relationModelSingularName: modelName,
121
+ relationCoModelSingularName: modelName,
126
122
  relationCreateInverse: true,
127
123
  relationCascade: null,
128
124
  relationModelModuleName: moduleName,
129
- relationModelFieldName: field.name,
125
+ relationCoModelFieldName: field.name,
130
126
  required: false,
131
127
  unique: false,
132
128
  index: false,
@@ -135,8 +131,6 @@ export class FieldMetadataService {
135
131
  model: relationModel,
136
132
  columnName:null,
137
133
  relationJoinTableName: null,
138
- relationJoinColumnName: null,
139
- joinColumnName: null,
140
134
  isRelationManyToManyOwner: false,
141
135
  id : null,
142
136
  }
@@ -151,7 +145,7 @@ export class FieldMetadataService {
151
145
  private async getRelationModel(modelRepository: Repository<ModelMetadata>, field: FieldMetadata, moduleName: string) {
152
146
  return await modelRepository.findOne({
153
147
  where: {
154
- singularName: field.relationModelSingularName,
148
+ singularName: field.relationCoModelSingularName,
155
149
  module: {
156
150
  name: field.relationModelModuleName ?? moduleName
157
151
  }
@@ -740,9 +734,10 @@ export class FieldMetadataService {
740
734
  "ormType",
741
735
  "isSystem",
742
736
  "relationType",
743
- "relationModelFieldName",
737
+ "relationCoModelFieldName",
744
738
  "relationCreateInverse",
745
- "relationModelSingularName",
739
+ "relationCoModelSingularName",
740
+ "relationCoModelColumnName",
746
741
  "relationModelModuleName",
747
742
  "required",
748
743
  "unique",
@@ -752,8 +747,6 @@ export class FieldMetadataService {
752
747
  "encryptionType",
753
748
  "decryptWhen",
754
749
  "columnName",
755
- "relationJoinColumnName",
756
- "joinColumnName",
757
750
  "relationJoinTableName",
758
751
  "isRelationManyToManyOwner",
759
752
  ];
@@ -10,6 +10,9 @@ import { CrudHelperService } from "./crud-helper.service";
10
10
  import { FileService } from "src/services/file.service";
11
11
  import { ConfigService } from "@nestjs/config";
12
12
  import { MediaStorageProviderType } from "../dtos/create-media-storage-provider-metadata.dto";
13
+ import { FileStorageProvider } from "./mediaStorageProviders/file-storage-provider";
14
+ import { FileS3StorageProvider } from "./mediaStorageProviders/file-s3-storage-provider";
15
+ import { getMediaStorageProvider } from "./mediaStorageProviders";
13
16
 
14
17
  @Injectable()
15
18
  export class MediaService {
@@ -32,8 +35,8 @@ export class MediaService {
32
35
  async findMany(basicFilterDto: BasicFilterDto) {
33
36
  const alias = 'media';
34
37
  // Extract the required keys from the input query
35
- let { limit, offset} = basicFilterDto;
36
-
38
+ let { limit, offset } = basicFilterDto;
39
+
37
40
  // Create above query on pincode table using query builder
38
41
  var qb: SelectQueryBuilder<Media> = this.mediaRepo.createQueryBuilder(alias)
39
42
  qb = await this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias);
@@ -174,8 +177,35 @@ export class MediaService {
174
177
  }
175
178
 
176
179
  async remove(id: number) {
177
- const lov = await this.findOne(id);
178
- return this.mediaRepo.remove(lov);
180
+ // const lov = await this.findOne(id);
181
+ const media = await this.mediaRepo.findOne({
182
+ where: {
183
+ id: id,
184
+ },
185
+ relations: ['mediaStorageProviderMetadata', 'fieldMetadata', 'fieldMetadata.model','fieldMetadata.mediaStorageProvider'],
186
+ });
187
+ const modelEntity = await this.modelMetadataRepo.findOne({
188
+ where: {
189
+ id: media.entityId,
190
+ }
191
+ }
192
+ );
193
+ // if (media.mediaStorageProviderMetadata.type === 'filesystem') {
194
+ // const fileStorageProvider = new FileStorageProvider(this.configService, this.fileService, this);
195
+
196
+ // await fileStorageProvider.delete(media, media.fieldMetadata);
197
+
198
+ // } else if (media.mediaStorageProviderMetadata.type === 'aws-s3') {
199
+ // const fileStorageProvider = new FileS3StorageProvider(this.configService, this.fileService, this);
200
+ // await fileStorageProvider.delete(media, media.fieldMetadata);
201
+
202
+ // } else {
203
+ // }
204
+ const storageProviderType = media.mediaStorageProviderMetadata.type as MediaStorageProviderType;
205
+ const storageProvider = getMediaStorageProvider(this.configService, this.fileService, this, storageProviderType);
206
+ await storageProvider.delete(modelEntity, media.fieldMetadata);
207
+
208
+ return this.mediaRepo.remove(media);
179
209
  }
180
210
 
181
211
  async delete(id: number) {
@@ -215,7 +245,7 @@ export class MediaService {
215
245
  // }
216
246
 
217
247
  async findByEntityIdAndFieldIdAndModelMetadataId(entityId: number, fieldMetadataId: number, modelMetadataId: number, relations = {}) {
218
- return this.mediaRepo.find({
248
+ return await this.mediaRepo.find({
219
249
  where: {
220
250
  modelMetadata: {
221
251
  id: modelMetadataId
@@ -51,6 +51,9 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
51
51
  entityId: entity.id,
52
52
  modelMetadataId: mediaFieldMetadata.model.id,
53
53
  relativeUri: awsFileUrl,
54
+ mimeType: file.mimetype,
55
+ fileSize: file.size,
56
+ originalFileName: file.originalname,
54
57
  mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
55
58
  fieldMetadataId: mediaFieldMetadata.id
56
59
  }) as unknown as Media;
@@ -64,7 +67,7 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
64
67
  if (!(entity instanceof CommonEntity)) {
65
68
  throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
66
69
  }
67
- const existingMedia = await this.mediaService.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
70
+ const existingMedia = await this.mediaService.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id,['mediaStorageProviderMetadata']);
68
71
  this.mediaService.deleteByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
69
72
  existingMedia.forEach(media => {
70
73
  this.fileService.deleteFromS3(media.relativeUri, mediaFieldMetadata.mediaStorageProvider.bucketName); //TODO
@@ -25,7 +25,7 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
25
25
  const media = await this.mediaService.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id, ['mediaStorageProviderMetadata']);
26
26
  // Add the full URL to the media
27
27
  media.forEach(m => {
28
- m['_full_url'] = this.getFullFilePath(m.relativeUri);
28
+ m['_full_url'] = `${process.env.BASE_URL}/${this.getFullFilePath(m.relativeUri)}`;
29
29
  });
30
30
  return media;
31
31
  }
@@ -46,6 +46,9 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
46
46
  entityId: entity.id,
47
47
  modelMetadataId: mediaFieldMetadata.model.id,
48
48
  relativeUri: this.getFileName(file),
49
+ mimeType: file.mimetype,
50
+ fileSize: file.size,
51
+ originalFileName: file.originalname,
49
52
  mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
50
53
  fieldMetadataId: mediaFieldMetadata.id
51
54
  }) as unknown as Media;
@@ -59,7 +62,7 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
59
62
  if (!(entity instanceof CommonEntity)) {
60
63
  throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
61
64
  }
62
- const existingMedia = await this.mediaService.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
65
+ const existingMedia = await this.mediaService.findByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id,['mediaStorageProviderMetadata']);
63
66
  this.mediaService.deleteByEntityIdAndFieldIdAndModelMetadataId(entity.id, mediaFieldMetadata.id, mediaFieldMetadata.model.id);
64
67
  existingMedia.forEach(media => {
65
68
  this.fileService.deleteFile(this.getFullFilePath(media.relativeUri));
@@ -67,7 +70,7 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
67
70
  }
68
71
 
69
72
  private getFullFilePath(fileName: string): string {
70
- return `${process.env.BASE_URL}/${this.configService.get('app-builder.fileStorageDir')}/${fileName}`;
73
+ return `${this.configService.get('app-builder.fileStorageDir')}/${fileName}`;
71
74
  }
72
75
 
73
76
  private getFileName(file: Express.Multer.File): string {
@@ -148,9 +148,15 @@ export class MenuItemMetadataService extends CRUDService<MenuItemMetadata> {
148
148
  // Recursive function to build the tree
149
149
  private buildMenuTree(rootItems: MenuItemMetadata[], allMenuItems: MenuItemMetadata[], activeUser: ActiveUserData): any[] {
150
150
  const menuItemsData = rootItems.map(rootItem => {
151
-
151
+ const allowedMenuItems =allMenuItems.filter(i => {
152
+ if(!i.parentMenuItem){
153
+ return true
154
+ }else{
155
+ const hasPermission = hasReadPermissionOnModel(activeUser, i.action.model.singularName);
156
+ return hasReadPermissionOnModel(activeUser, i.action.model.singularName)
157
+ }});
152
158
  // Get immediate children of the current loop variable menuItem.
153
- const children = allMenuItems.filter(item => item.parentMenuItem && item.parentMenuItem.id === rootItem.id);
159
+ const children = allowedMenuItems.filter(item => item.parentMenuItem && item.parentMenuItem.id === rootItem.id);
154
160
 
155
161
  // TODO: We should specify path only if there are no more children present.
156
162
  // For now adding path everywhere.
@@ -902,6 +902,7 @@ export class ModelMetadataService {
902
902
  dataSource: model.dataSource,
903
903
  table: model.tableName,
904
904
  fields: fieldsForRefresh,
905
+ modelEnableSoftDelete: model.enableSoftDelete
905
906
  },
906
907
  dryRun
907
908
  );
@@ -99,8 +99,8 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
99
99
  return await this._addPermissionsToRole(roleName, permissionNames);
100
100
  }
101
101
 
102
- async addPermissionToRole(roleName: string, permissionName: string) {
103
- return await this._addPermissionsToRole(roleName, [permissionName]);
102
+ async addPermissionToRole(roleName: string, permissionName: string[]) {
103
+ return await this._addPermissionsToRole(roleName, permissionName);
104
104
  }
105
105
 
106
106
  private async _addPermissionsToRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {
@@ -75,6 +75,21 @@ export class UserService extends CRUDService<User> {
75
75
  // return entity;
76
76
  }
77
77
 
78
+ async updateUser(id: any, updateDto, files) {
79
+ const user = await this.repo.findOne({
80
+ where: { id: id },
81
+ relations: {
82
+ roles: true
83
+ }
84
+ });
85
+ if (!user) {
86
+ throw new Error(`User not found.`);
87
+ }
88
+ const roles = updateDto.roles ? updateDto.roles :[];
89
+ await this.addRolesToUser(user.username, roles);
90
+ await this.update(id, updateDto, files, true);
91
+ }
92
+
78
93
  async addRoleToUser(username: string, roleName: string): Promise<User> {
79
94
  // Find the role, find the user and populate the many 2 many table.
80
95
  const user = await this.repo.findOne({
@@ -86,11 +86,11 @@ export class ViewMetadataService extends CRUDService<ViewMetadata> {
86
86
  const field = fields[i];
87
87
 
88
88
  // For fields of type relation & relationType many-to-one
89
- // We fetch metadata regarding the relationModelSingularName
89
+ // We fetch metadata regarding the relationCoModelSingularName
90
90
  if (field.type === 'relation') {
91
91
  const relationModel = await this.modelMetadataRepo.find({
92
92
  where: {
93
- singularName: field.relationModelSingularName
93
+ singularName: field.relationCoModelSingularName
94
94
  },
95
95
  relations: {
96
96
  userKeyField: true