@solidstarters/solid-core 1.2.93 → 1.2.96
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/dist/controllers/action-metadata.controller.d.ts +3 -1
- package/dist/controllers/action-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/chatter-message-details.controller.d.ts +3 -1
- package/dist/controllers/chatter-message-details.controller.d.ts.map +1 -1
- package/dist/controllers/chatter-message.controller.d.ts +3 -1
- package/dist/controllers/chatter-message.controller.d.ts.map +1 -1
- package/dist/controllers/email-template.controller.d.ts +3 -1
- package/dist/controllers/email-template.controller.d.ts.map +1 -1
- package/dist/controllers/export-template.controller.d.ts +3 -1
- package/dist/controllers/export-template.controller.d.ts.map +1 -1
- package/dist/controllers/export-transaction.controller.d.ts +3 -1
- package/dist/controllers/export-transaction.controller.d.ts.map +1 -1
- package/dist/controllers/import-transaction-error-log.controller.d.ts +3 -1
- package/dist/controllers/import-transaction-error-log.controller.d.ts.map +1 -1
- package/dist/controllers/import-transaction.controller.d.ts +3 -1
- package/dist/controllers/import-transaction.controller.d.ts.map +1 -1
- package/dist/controllers/list-of-values.controller.d.ts +3 -1
- package/dist/controllers/list-of-values.controller.d.ts.map +1 -1
- package/dist/controllers/locale.controller.d.ts +43 -0
- package/dist/controllers/locale.controller.d.ts.map +1 -0
- package/dist/controllers/locale.controller.js +179 -0
- package/dist/controllers/locale.controller.js.map +1 -0
- package/dist/controllers/media.controller.d.ts +3 -1
- package/dist/controllers/media.controller.d.ts.map +1 -1
- package/dist/controllers/menu-item-metadata.controller.d.ts +3 -1
- package/dist/controllers/menu-item-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/mq-message-queue.controller.d.ts +3 -1
- package/dist/controllers/mq-message-queue.controller.d.ts.map +1 -1
- package/dist/controllers/mq-message.controller.d.ts +3 -1
- package/dist/controllers/mq-message.controller.d.ts.map +1 -1
- package/dist/controllers/permission-metadata.controller.d.ts +3 -1
- package/dist/controllers/permission-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/role-metadata.controller.d.ts +3 -1
- package/dist/controllers/role-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/saved-filters.controller.d.ts +3 -1
- package/dist/controllers/saved-filters.controller.d.ts.map +1 -1
- package/dist/controllers/security-rule.controller.d.ts +3 -1
- package/dist/controllers/security-rule.controller.d.ts.map +1 -1
- package/dist/controllers/setting.controller.d.ts +3 -1
- package/dist/controllers/setting.controller.d.ts.map +1 -1
- package/dist/controllers/sms-template.controller.d.ts +3 -1
- package/dist/controllers/sms-template.controller.d.ts.map +1 -1
- package/dist/controllers/user-view-metadata.controller.d.ts +3 -1
- package/dist/controllers/user-view-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/user.controller.d.ts +3 -1
- package/dist/controllers/user.controller.d.ts.map +1 -1
- package/dist/controllers/view-metadata.controller.d.ts +4 -1
- package/dist/controllers/view-metadata.controller.d.ts.map +1 -1
- package/dist/dtos/basic-filters.dto.d.ts +2 -0
- package/dist/dtos/basic-filters.dto.d.ts.map +1 -1
- package/dist/dtos/basic-filters.dto.js +11 -1
- package/dist/dtos/basic-filters.dto.js.map +1 -1
- package/dist/dtos/create-locale.dto.d.ts +6 -0
- package/dist/dtos/create-locale.dto.d.ts.map +1 -0
- package/dist/dtos/create-locale.dto.js +43 -0
- package/dist/dtos/create-locale.dto.js.map +1 -0
- package/dist/dtos/create-model-metadata.dto.d.ts +1 -0
- package/dist/dtos/create-model-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-model-metadata.dto.js +7 -2
- package/dist/dtos/create-model-metadata.dto.js.map +1 -1
- package/dist/dtos/create-saved-filters.dto.d.ts +1 -1
- package/dist/dtos/create-saved-filters.dto.d.ts.map +1 -1
- package/dist/dtos/create-saved-filters.dto.js +3 -3
- package/dist/dtos/create-saved-filters.dto.js.map +1 -1
- package/dist/dtos/update-locale.dto.d.ts +7 -0
- package/dist/dtos/update-locale.dto.d.ts.map +1 -0
- package/dist/dtos/update-locale.dto.js +48 -0
- package/dist/dtos/update-locale.dto.js.map +1 -0
- package/dist/dtos/update-saved-filters.dto.d.ts +1 -1
- package/dist/dtos/update-saved-filters.dto.d.ts.map +1 -1
- package/dist/dtos/update-saved-filters.dto.js +3 -3
- package/dist/dtos/update-saved-filters.dto.js.map +1 -1
- package/dist/entities/common.entity.d.ts +3 -0
- package/dist/entities/common.entity.d.ts.map +1 -1
- package/dist/entities/common.entity.js +13 -1
- package/dist/entities/common.entity.js.map +1 -1
- package/dist/entities/locale.entity.d.ts +7 -0
- package/dist/entities/locale.entity.d.ts.map +1 -0
- package/dist/entities/locale.entity.js +43 -0
- package/dist/entities/locale.entity.js.map +1 -0
- package/dist/entities/model-metadata.entity.d.ts +1 -0
- package/dist/entities/model-metadata.entity.d.ts.map +1 -1
- package/dist/entities/model-metadata.entity.js +5 -1
- package/dist/entities/model-metadata.entity.js.map +1 -1
- package/dist/entities/saved-filters.entity.d.ts +1 -1
- package/dist/entities/saved-filters.entity.d.ts.map +1 -1
- package/dist/entities/saved-filters.entity.js +2 -2
- package/dist/entities/saved-filters.entity.js.map +1 -1
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js.map +1 -1
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js.map +1 -1
- package/dist/helpers/module-metadata-helper.service.d.ts.map +1 -1
- package/dist/helpers/module-metadata-helper.service.js.map +1 -1
- package/dist/helpers/solid-registry.d.ts +2 -0
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js +5 -0
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +204 -8
- package/dist/services/crud-helper.service.d.ts +2 -1
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +35 -2
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +3 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +22 -4
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/locale.service.d.ts +26 -0
- package/dist/services/locale.service.d.ts.map +1 -0
- package/dist/services/locale.service.js +64 -0
- package/dist/services/locale.service.js.map +1 -0
- package/dist/services/media.service.d.ts +3 -1
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
- package/dist/services/selection-providers/locale-list-selection-provider.service.d.ts +9 -0
- package/dist/services/selection-providers/locale-list-selection-provider.service.d.ts.map +1 -0
- package/dist/services/selection-providers/locale-list-selection-provider.service.js +87 -0
- package/dist/services/selection-providers/locale-list-selection-provider.service.js.map +1 -0
- package/dist/services/view-metadata.service.d.ts +3 -0
- package/dist/services/view-metadata.service.d.ts.map +1 -1
- package/dist/services/view-metadata.service.js +73 -7
- package/dist/services/view-metadata.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +8 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/model.subscriber.d.ts.map +1 -1
- package/dist/subscribers/model.subscriber.js +24 -0
- package/dist/subscribers/model.subscriber.js.map +1 -1
- package/dist/transformers/datetime-transformer.d.ts +4 -0
- package/dist/transformers/datetime-transformer.d.ts.map +1 -0
- package/dist/transformers/datetime-transformer.js +11 -0
- package/dist/transformers/datetime-transformer.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/controllers/locale.controller.ts +94 -0
- package/src/dtos/basic-filters.dto.ts +13 -1
- package/src/dtos/create-locale.dto.ts +17 -0
- package/src/dtos/create-model-metadata.dto.ts +5 -1
- package/src/dtos/create-saved-filters.dto.ts +3 -3
- package/src/dtos/update-locale.dto.ts +23 -0
- package/src/dtos/update-saved-filters.dto.ts +3 -3
- package/src/entities/common.entity.ts +11 -1
- package/src/entities/locale.entity.ts +14 -0
- package/src/entities/model-metadata.entity.ts +3 -0
- package/src/entities/saved-filters.entity.ts +1 -1
- package/src/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.ts +2 -1
- package/src/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.ts +1 -0
- package/src/helpers/module-metadata-helper.service.ts +0 -1
- package/src/helpers/solid-registry.ts +11 -2
- package/src/index.ts +6 -3
- package/src/seeders/seed-data/solid-core-metadata.json +204 -8
- package/src/services/crud-helper.service.ts +57 -14
- package/src/services/crud.service.ts +56 -33
- package/src/services/locale.service.ts +37 -0
- package/src/services/mediaStorageProviders/file-storage-provider.ts +1 -1
- package/src/services/selection-providers/locale-list-selection-provider.service.ts +58 -0
- package/src/services/view-metadata.service.ts +179 -14
- package/src/solid-core.module.ts +8 -0
- package/src/subscribers/model.subscriber.ts +24 -0
- package/src/transformers/datetime-transformer.ts +12 -0
|
@@ -53,7 +53,7 @@ export class CrudHelperService {
|
|
|
53
53
|
}
|
|
54
54
|
else { // Recursively call the applyFilters method to handle nested conditions
|
|
55
55
|
const joinField = `${alias}.${key}`;
|
|
56
|
-
|
|
56
|
+
if (!this.isRelationJoined(selectQb, joinField)) selectQb.leftJoin(joinField, key);
|
|
57
57
|
this.applyFilters(qb, primaryFilterObj, key, selectQb);
|
|
58
58
|
}
|
|
59
59
|
});
|
|
@@ -155,10 +155,9 @@ export class CrudHelperService {
|
|
|
155
155
|
return queryBuilder.expressionMap.joinAttributes.length > 0;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
buildFilterQuery(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string): SelectQueryBuilder<any> { //TODO : Check how to pass a type to SelectQueryBuilder instead of any
|
|
158
|
+
buildFilterQuery(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string, internationalisation?: boolean, draftPublishWorkflow?: boolean): SelectQueryBuilder<any> { // TODO : Check how to pass a type to SelectQueryBuilder instead of any
|
|
160
159
|
let { limit, offset, showSoftDeleted, filters } = basicFilterDto;
|
|
161
|
-
const { fields, sort, groupBy, populate = [], populateMedia=[] } = basicFilterDto;
|
|
160
|
+
const { fields, sort, groupBy, populate = [], populateMedia = [], locale, status } = basicFilterDto;
|
|
162
161
|
|
|
163
162
|
// Normalize the fields, sort, groupBy and populate options i.e (since they can be either a string or an array of strings, when coming from the request)
|
|
164
163
|
const normalizedFields = this.normalize(fields);
|
|
@@ -187,6 +186,25 @@ export class CrudHelperService {
|
|
|
187
186
|
}));
|
|
188
187
|
}
|
|
189
188
|
|
|
189
|
+
let finalLocale = locale
|
|
190
|
+
if (internationalisation) {
|
|
191
|
+
// If locale is not provided in the filter dto, then assume it is the default locale to be used.
|
|
192
|
+
if (!finalLocale) {
|
|
193
|
+
// TODO: get the default locale from the database.
|
|
194
|
+
// This should be replaced with the actual default locale from the database, make sure to cache this request.
|
|
195
|
+
// @Sundaram consult with Oswald and use a registry based approach to fetch the default locale, making sure that it gets cached and we don't have to query again and again.
|
|
196
|
+
finalLocale = 'en';
|
|
197
|
+
}
|
|
198
|
+
qb.andWhere(`${entityAlias}.localeName = :locale`, { locale: finalLocale });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (draftPublishWorkflow && status) {
|
|
202
|
+
if (basicFilterDto.status === 'published') {
|
|
203
|
+
qb.andWhere(`${entityAlias}.publishedAt IS NOT NULL`);
|
|
204
|
+
} else if (basicFilterDto.status === 'draft') {
|
|
205
|
+
qb.andWhere(`${entityAlias}.publishedAt IS NULL`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
190
208
|
// Depending upon the select option, apply the select clause
|
|
191
209
|
if (normalizedFields && normalizedFields.length) {
|
|
192
210
|
qb.select(normalizedFields.map(field => {
|
|
@@ -223,22 +241,22 @@ export class CrudHelperService {
|
|
|
223
241
|
qb.addGroupBy(`${entityAlias}.${field}`);
|
|
224
242
|
});
|
|
225
243
|
}
|
|
226
|
-
|
|
244
|
+
|
|
227
245
|
// Apply the pagination options & handle the case when the query has joins
|
|
228
246
|
if (limit) this.hasJoins(qb) ? qb.take(limit) : qb.limit(limit);
|
|
229
|
-
if (offset) this.hasJoins(qb) ? qb.skip(offset): qb.offset(offset);
|
|
247
|
+
if (offset) this.hasJoins(qb) ? qb.skip(offset) : qb.offset(offset);
|
|
230
248
|
return qb;
|
|
231
249
|
}
|
|
232
250
|
|
|
233
251
|
additionalRelationsRequiredForMediaPopulation(normalizedPopulateMedia: string[]) {
|
|
234
252
|
// Populate relations containing the media field
|
|
235
253
|
return normalizedPopulateMedia
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
254
|
+
.filter(pm => pm.includes("."))
|
|
255
|
+
.map((pm) => {
|
|
256
|
+
const mediaPathParts = pm.split('.');
|
|
257
|
+
if (mediaPathParts.length <= 1) return pm;
|
|
258
|
+
return mediaPathParts.slice(0, -1).join('.');
|
|
259
|
+
});
|
|
242
260
|
}
|
|
243
261
|
|
|
244
262
|
private buildPopulateQuery(normalizedPopulate: string[], entityAlias: string, qb: SelectQueryBuilder<any>) {
|
|
@@ -257,12 +275,12 @@ export class CrudHelperService {
|
|
|
257
275
|
// Check if the relation is already joined, if not then join it
|
|
258
276
|
if (!this.isRelationJoined(qb, joinProperty)) {
|
|
259
277
|
const joinAlias = relationParts.slice(0, i + 1).join('_');
|
|
260
|
-
qb.leftJoinAndSelect(joinProperty, joinAlias);
|
|
278
|
+
qb.leftJoinAndSelect(joinProperty, joinAlias);
|
|
261
279
|
}
|
|
262
280
|
else {
|
|
263
281
|
// Since in populate, we are create a unique alias based on the relation path
|
|
264
282
|
//If the join is already present, it is probably because of the relation being passed in the where filter i.e applyFilters method
|
|
265
|
-
qb.addSelect(`${part}`);
|
|
283
|
+
qb.addSelect(`${part}`);
|
|
266
284
|
}
|
|
267
285
|
parentAlias = part; // Update the parent alias for the next iteration
|
|
268
286
|
});
|
|
@@ -332,6 +350,31 @@ export class CrudHelperService {
|
|
|
332
350
|
};
|
|
333
351
|
}
|
|
334
352
|
|
|
353
|
+
async countGroupedRecords(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string) { //TODO : Check how to pass a type to SelectQueryBuilder instead of any
|
|
354
|
+
const { limit, offset, ...rest } = basicFilterDto;
|
|
355
|
+
|
|
356
|
+
const filteredDto = { ...rest, limit: undefined, offset: undefined };
|
|
357
|
+
|
|
358
|
+
const filteredQB = this.buildFilterQuery(qb, filteredDto as BasicFilterDto, entityAlias);
|
|
359
|
+
|
|
360
|
+
// Select only the group field and count distinct rows
|
|
361
|
+
const groupByField = filteredDto.groupBy;
|
|
362
|
+
|
|
363
|
+
if (!groupByField || (Array.isArray(groupByField) && groupByField.length !== 1)) {
|
|
364
|
+
throw new Error('Exactly one groupBy field is required to count grouped records.');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const field = Array.isArray(groupByField) ? groupByField[0] : groupByField;
|
|
368
|
+
const rawResults = await filteredQB
|
|
369
|
+
.select([]) // Remove prior select fields
|
|
370
|
+
.addSelect(`${entityAlias}.${field}`, 'groupField')
|
|
371
|
+
.groupBy(`${entityAlias}.${field}`)
|
|
372
|
+
.limit(undefined) // Important: prevent LIMIT 1 from propagating
|
|
373
|
+
.offset(undefined)
|
|
374
|
+
.getRawMany();
|
|
375
|
+
|
|
376
|
+
return rawResults.length;
|
|
377
|
+
}
|
|
335
378
|
|
|
336
379
|
hasReadPermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
337
380
|
const permissionNames = [`${classify(modelName)}Controller.findOne`, `${classify(modelName)}Controller.findMany`];
|
|
@@ -52,7 +52,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
52
52
|
//We can just have the Model Entity here
|
|
53
53
|
) { }
|
|
54
54
|
|
|
55
|
-
async create(createDto: any, files: Express.Multer.File[] = [],solidRequestContext
|
|
55
|
+
async create(createDto: any, files: Express.Multer.File[] = [], solidRequestContext: any = {}): Promise<T> {
|
|
56
56
|
// This class will be extended by the generated service class i.e PersonService
|
|
57
57
|
// The data required to identify the model and module name will be passed from the generate CrudService subclass
|
|
58
58
|
//TODO: Algorithm to create the entity
|
|
@@ -215,7 +215,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
215
215
|
throw new BadRequestException('Forbidden');
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
const model = await this.modelMetadataService.findOneBySingularName(this.modelName, {
|
|
220
220
|
fields: {
|
|
221
221
|
model: true,
|
|
@@ -232,10 +232,26 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
232
232
|
if (!entity) {
|
|
233
233
|
throw new Error(`Entity [${this.moduleName}.${this.modelName}] with id ${id} not found`);
|
|
234
234
|
}
|
|
235
|
+
|
|
236
|
+
// If the model has internationalisation enabled, delete children with defaultEntityLocaleId === this entity's id
|
|
237
|
+
if (model.internationalisation) {
|
|
238
|
+
// Find all child entities where defaultEntityLocaleId === this entity's id
|
|
239
|
+
const childEntities = await this.repo.find({
|
|
240
|
+
where: { defaultEntityLocaleId: id } as any
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
if (childEntities.length > 0) {
|
|
244
|
+
if (model.enableSoftDelete === true) {
|
|
245
|
+
await this.repo.softRemove(childEntities);
|
|
246
|
+
} else {
|
|
247
|
+
await this.repo.remove(childEntities);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
235
252
|
if (model.enableSoftDelete === true) {
|
|
236
253
|
await this.repo.softRemove(entity);
|
|
237
254
|
return this.repo.save(entity);
|
|
238
|
-
|
|
239
255
|
} else {
|
|
240
256
|
return this.repo.remove(entity);
|
|
241
257
|
}
|
|
@@ -365,7 +381,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
365
381
|
// Validation against the selectionStatic values. No transformation is required
|
|
366
382
|
// If the value is not in the selectionStatic values, then throw
|
|
367
383
|
// Also validate against the selectionType
|
|
368
|
-
const options = { ...commonOptions, selectionStaticValues: fieldMetadata.selectionStaticValues, selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, isMultiSelect: fieldMetadata.isMultiSelect};
|
|
384
|
+
const options = { ...commonOptions, selectionStaticValues: fieldMetadata.selectionStaticValues, selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, isMultiSelect: fieldMetadata.isMultiSelect };
|
|
369
385
|
return new SelectionStaticFieldCrudManager(options);
|
|
370
386
|
}
|
|
371
387
|
case SolidFieldType.selectionDynamic: {// [HOLD]
|
|
@@ -374,7 +390,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
374
390
|
// dataSource: string; // The name of the selection provider
|
|
375
391
|
// filterSchema : json // This is a custom json object that every data source will handle accordingly. We could validate the query against the selection provider
|
|
376
392
|
// values : string[]; // The values returned by the selection provider
|
|
377
|
-
const options = { ...commonOptions, selectionDynamicProvider: fieldMetadata.selectionDynamicProvider, selectionDynamicProviderCtxt: fieldMetadata.selectionDynamicProviderCtxt, selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, discoveryService: this.discoveryService, isMultiSelect: fieldMetadata.isMultiSelect};
|
|
393
|
+
const options = { ...commonOptions, selectionDynamicProvider: fieldMetadata.selectionDynamicProvider, selectionDynamicProviderCtxt: fieldMetadata.selectionDynamicProviderCtxt, selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, discoveryService: this.discoveryService, isMultiSelect: fieldMetadata.isMultiSelect };
|
|
378
394
|
return new SelectionDynamicFieldCrudManager(options);
|
|
379
395
|
}
|
|
380
396
|
case SolidFieldType.uuid: {
|
|
@@ -398,7 +414,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
398
414
|
const alias = 'entity';
|
|
399
415
|
// Extract the required keys from the input query
|
|
400
416
|
let { limit, offset, populateMedia, populateGroup, groupFilter } = basicFilterDto;
|
|
401
|
-
const {singularName} = await this.loadModel();
|
|
417
|
+
const { singularName, internationalisation, draftPublishWorkflow } = await this.loadModel();
|
|
402
418
|
// Check wheather user has update permission for model
|
|
403
419
|
if (solidRequestContext.activeUser) {
|
|
404
420
|
const hasPermission = this.crudHelperService.hasReadPermissionOnModel(solidRequestContext.activeUser, singularName);
|
|
@@ -410,11 +426,18 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
410
426
|
// Create above query on pincode table using query builder
|
|
411
427
|
var qb: SelectQueryBuilder<T> = this.repo.createQueryBuilder(alias)
|
|
412
428
|
qb = this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias);
|
|
413
|
-
|
|
429
|
+
|
|
430
|
+
|
|
414
431
|
if (basicFilterDto.groupBy) {
|
|
415
432
|
// Get the records and the count
|
|
416
|
-
const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter, populateGroup, alias, populateMedia);
|
|
433
|
+
const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter, populateGroup, alias, populateMedia, internationalisation, draftPublishWorkflow);
|
|
434
|
+
const totalGroups = await this.crudHelperService.countGroupedRecords(qb, basicFilterDto, alias);
|
|
435
|
+
qb = this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias, internationalisation, draftPublishWorkflow);
|
|
436
|
+
|
|
417
437
|
return {
|
|
438
|
+
meta: {
|
|
439
|
+
"totalRecords": totalGroups
|
|
440
|
+
},
|
|
418
441
|
groupMeta,
|
|
419
442
|
groupRecords,
|
|
420
443
|
}
|
|
@@ -440,7 +463,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
440
463
|
return this.wrapFindResponse(offset, limit, count, entities);
|
|
441
464
|
}
|
|
442
465
|
|
|
443
|
-
private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto, populateGroup: boolean, alias: string, populateMedia: string[]) {
|
|
466
|
+
private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto, populateGroup: boolean, alias: string, populateMedia: string[], internationalisation: boolean, draftPublishWorkflow: boolean) {
|
|
444
467
|
const groupByResult = await qb.getRawMany();
|
|
445
468
|
|
|
446
469
|
const groupMeta = [];
|
|
@@ -449,7 +472,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
449
472
|
for (const group of groupByResult) {
|
|
450
473
|
if (populateGroup) {
|
|
451
474
|
let groupByQb: SelectQueryBuilder<T> = this.repo.createQueryBuilder(alias);
|
|
452
|
-
groupByQb = this.crudHelperService.buildFilterQuery(groupByQb, groupFilter, alias);
|
|
475
|
+
groupByQb = this.crudHelperService.buildFilterQuery(groupByQb, groupFilter, alias, internationalisation, draftPublishWorkflow);
|
|
453
476
|
groupByQb = this.crudHelperService.buildGroupByRecordsQuery(groupByQb, group, alias);
|
|
454
477
|
const [entities, count] = await groupByQb.getManyAndCount();
|
|
455
478
|
|
|
@@ -486,12 +509,12 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
486
509
|
return r;
|
|
487
510
|
}
|
|
488
511
|
|
|
489
|
-
|
|
512
|
+
private async handlePopulateMedia(populateMedia: string[], entities: T[]) {
|
|
490
513
|
const model = await this.entityManager.getRepository(ModelMetadata).findOne({
|
|
491
514
|
where: {
|
|
492
515
|
singularName: this.modelName,
|
|
493
516
|
},
|
|
494
|
-
relations: ['fields', 'fields.mediaStorageProvider', 'fields.model','module'],
|
|
517
|
+
relations: ['fields', 'fields.mediaStorageProvider', 'fields.model', 'module'],
|
|
495
518
|
});
|
|
496
519
|
|
|
497
520
|
// Will iterate through every entity & all populateMedia & call getMediaDetails for each field
|
|
@@ -501,7 +524,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
501
524
|
}
|
|
502
525
|
}
|
|
503
526
|
return entities;
|
|
504
|
-
|
|
527
|
+
}
|
|
505
528
|
|
|
506
529
|
// Adds the media with full URL to the entity / nested entity
|
|
507
530
|
private async populateMediaObject(mediaFieldPath: string, model: ModelMetadata, entity: T) {
|
|
@@ -515,12 +538,12 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
515
538
|
// We can assume that the media field entity model is already populated as part of the entity data
|
|
516
539
|
const mediaFieldEntities = this.getMediaFieldEntities(entity, pathParts);
|
|
517
540
|
if (!mediaFieldEntities || mediaFieldEntities.length === 0) {
|
|
518
|
-
|
|
541
|
+
return;//no need to populate data if relation not exists
|
|
519
542
|
}
|
|
520
543
|
// Populate the media field entities with the full URL
|
|
521
544
|
for (const mediaFieldEntity of mediaFieldEntities) {
|
|
522
545
|
const mediaWithFullUrl = await this.getMediaWithFullUrl(mediaFieldEntity, mediaFieldMetadata);
|
|
523
|
-
this.appendMediaKey(mediaWithFullUrl, mediaFieldEntity,
|
|
546
|
+
this.appendMediaKey(mediaWithFullUrl, mediaFieldEntity, mediaFieldMetadata.name);
|
|
524
547
|
// mediaFieldEntity['_media'][mediaFieldPath] = mediaWithFullUrl
|
|
525
548
|
}
|
|
526
549
|
}
|
|
@@ -546,7 +569,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
546
569
|
entity['_media'] = {
|
|
547
570
|
[mediaFieldPath]: mediaWithFullUrl
|
|
548
571
|
};
|
|
549
|
-
}
|
|
572
|
+
}
|
|
550
573
|
}
|
|
551
574
|
|
|
552
575
|
private getMediaFieldEntities(entity: T, mediaPathParts: string[]): T[] {
|
|
@@ -562,12 +585,12 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
562
585
|
return isArray(entityPart) ? entityPart : [entityPart];
|
|
563
586
|
}
|
|
564
587
|
|
|
565
|
-
async getMediaWithFullUrl(mediaEntity: any, mediaFieldMetadata: FieldMetadata): Promise<MediaWithFullUrl[]>{
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
588
|
+
async getMediaWithFullUrl(mediaEntity: any, mediaFieldMetadata: FieldMetadata): Promise<MediaWithFullUrl[]> {
|
|
589
|
+
const storageProviderMetadata = mediaFieldMetadata.mediaStorageProvider;
|
|
590
|
+
const storageProviderType = storageProviderMetadata.type as MediaStorageProviderType;
|
|
591
|
+
const storageProvider = await getMediaStorageProvider(this.moduleRef, storageProviderType);
|
|
592
|
+
const mediaDetails = await storageProvider.retrieve(mediaEntity, mediaFieldMetadata);
|
|
593
|
+
return mediaDetails as MediaWithFullUrl[];
|
|
571
594
|
}
|
|
572
595
|
|
|
573
596
|
async findOne(id: number, query: any, solidRequestContext: any = {}) {
|
|
@@ -610,7 +633,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
610
633
|
return entity;
|
|
611
634
|
}
|
|
612
635
|
|
|
613
|
-
async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = [],solidRequestContext: any = {}): Promise<T[]> {
|
|
636
|
+
async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = [], solidRequestContext: any = {}): Promise<T[]> {
|
|
614
637
|
|
|
615
638
|
|
|
616
639
|
// if (createDtos.length !== filesArray.length) {
|
|
@@ -669,7 +692,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
669
692
|
return savedEntities;
|
|
670
693
|
}
|
|
671
694
|
|
|
672
|
-
async deleteMany(ids: number[],solidRequestContext: any = {}): Promise<any> {
|
|
695
|
+
async deleteMany(ids: number[], solidRequestContext: any = {}): Promise<any> {
|
|
673
696
|
|
|
674
697
|
if (!ids || ids.length === 0) {
|
|
675
698
|
throw new Error('At least one ID is required for deletion');
|
|
@@ -713,7 +736,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
713
736
|
// return removedEntities
|
|
714
737
|
}
|
|
715
738
|
|
|
716
|
-
async recover(id: number,solidRequestContext: any = {}) {
|
|
739
|
+
async recover(id: number, solidRequestContext: any = {}) {
|
|
717
740
|
try {
|
|
718
741
|
const loadedmodel = await this.loadModel();
|
|
719
742
|
// Check wheather user has update permission for model
|
|
@@ -753,7 +776,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
753
776
|
}
|
|
754
777
|
}
|
|
755
778
|
|
|
756
|
-
async recoverMany(ids: number[],solidRequestContext: any = {}) {
|
|
779
|
+
async recoverMany(ids: number[], solidRequestContext: any = {}) {
|
|
757
780
|
try {
|
|
758
781
|
const loadedmodel = await this.loadModel();
|
|
759
782
|
// Check wheather user has update permission for model
|
|
@@ -808,32 +831,32 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
808
831
|
if (!pathParts || pathParts.length === 0) {
|
|
809
832
|
throw new BadRequestException('Path parts cannot be empty');
|
|
810
833
|
}
|
|
811
|
-
|
|
834
|
+
|
|
812
835
|
const [currentPart, ...remainingParts] = pathParts;
|
|
813
836
|
const field = fields.find(field => field.name === currentPart);
|
|
814
|
-
|
|
837
|
+
|
|
815
838
|
if (!field) {
|
|
816
839
|
throw new BadRequestException(`Field ${currentPart} not found in model ${this.modelName}`);
|
|
817
840
|
}
|
|
818
|
-
|
|
841
|
+
|
|
819
842
|
// Base case: last part, return the field
|
|
820
843
|
if (remainingParts.length === 0) {
|
|
821
844
|
return field;
|
|
822
845
|
}
|
|
823
|
-
|
|
846
|
+
|
|
824
847
|
if (!field.relationCoModelSingularName) {
|
|
825
848
|
throw new BadRequestException(`Field ${field.name} does not define a relationCoModelSingularName`);
|
|
826
849
|
}
|
|
827
|
-
|
|
850
|
+
|
|
828
851
|
const relationCoModel = await this.entityManager.getRepository(ModelMetadata).findOne({
|
|
829
852
|
where: { singularName: field.relationCoModelSingularName },
|
|
830
853
|
relations: ['fields', 'fields.mediaStorageProvider', 'fields.model'],
|
|
831
854
|
});
|
|
832
|
-
|
|
855
|
+
|
|
833
856
|
if (!relationCoModel) {
|
|
834
857
|
throw new BadRequestException(`Model ${field.relationCoModelSingularName} not found`);
|
|
835
858
|
}
|
|
836
|
-
|
|
859
|
+
|
|
837
860
|
return this.getFieldMetadataRecursively(remainingParts, relationCoModel.fields);
|
|
838
861
|
}
|
|
839
862
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
|
|
3
|
+
import { DiscoveryService, ModuleRef } from "@nestjs/core";
|
|
4
|
+
import { EntityManager, Repository } from 'typeorm';
|
|
5
|
+
|
|
6
|
+
import { CRUDService } from 'src/services/crud.service';
|
|
7
|
+
import { ModelMetadataService } from 'src/services/model-metadata.service';
|
|
8
|
+
import { ModuleMetadataService } from 'src/services/module-metadata.service';
|
|
9
|
+
import { ConfigService } from '@nestjs/config';
|
|
10
|
+
import { FileService } from 'src/services/file.service';
|
|
11
|
+
import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
12
|
+
import { ModelMetadata } from 'src/entities/model-metadata.entity';
|
|
13
|
+
import { RequestContextService } from './request-context.service';
|
|
14
|
+
import { Locale } from 'src/entities/locale.entity';
|
|
15
|
+
@Injectable()
|
|
16
|
+
export class LocaleService extends CRUDService<Locale>{
|
|
17
|
+
constructor(
|
|
18
|
+
readonly modelMetadataService: ModelMetadataService,
|
|
19
|
+
readonly moduleMetadataService: ModuleMetadataService,
|
|
20
|
+
readonly configService: ConfigService,
|
|
21
|
+
readonly fileService: FileService,
|
|
22
|
+
readonly discoveryService: DiscoveryService,
|
|
23
|
+
readonly crudHelperService: CrudHelperService,
|
|
24
|
+
@InjectEntityManager()
|
|
25
|
+
readonly entityManager: EntityManager,
|
|
26
|
+
@InjectRepository(Locale, 'default')
|
|
27
|
+
readonly repo: Repository<Locale>,
|
|
28
|
+
@InjectRepository(Locale, 'default')
|
|
29
|
+
readonly moduleRef: ModuleRef,
|
|
30
|
+
@InjectRepository(ModelMetadata)
|
|
31
|
+
private readonly modelMetadataRepo: Repository<ModelMetadata>,
|
|
32
|
+
readonly requestContextService: RequestContextService
|
|
33
|
+
) {
|
|
34
|
+
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService,entityManager, repo, 'locale', 'solid-core', moduleRef);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
}
|
|
@@ -45,7 +45,7 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
|
|
|
45
45
|
await this.fileService.deleteFile(file.path);
|
|
46
46
|
|
|
47
47
|
// Create an entry in the media table
|
|
48
|
-
|
|
48
|
+
const mediaEntity = await this.mediaRepository.createMedia({
|
|
49
49
|
entityId: entity.id,
|
|
50
50
|
modelMetadataId: mediaFieldMetadata.model.id,
|
|
51
51
|
relativeUri: this.getFileName(file),
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { ModelMetadataService } from "src/services/model-metadata.service";
|
|
3
|
+
import { SelectionProvider } from "src/decorators/selection-provider.decorator";
|
|
4
|
+
import { ISelectionProvider, ISelectionProviderContext, ISelectionProviderValues } from "../../interfaces";
|
|
5
|
+
// import localeCodes from 'locale-codes';
|
|
6
|
+
import * as locale from 'locale-codes'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@SelectionProvider()
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class LocaleListSelectionProvider implements ISelectionProvider<ISelectionProviderContext> {
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
help(): string {
|
|
17
|
+
return "# Gets all locales available to the user";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
name(): string {
|
|
21
|
+
return 'LocaleListSelectionProvider';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async value(optionValue: string, ctxt: ISelectionProviderContext): Promise<ISelectionProviderValues | any> {
|
|
25
|
+
const locales = locale.all
|
|
26
|
+
.filter(code => code.tag === optionValue)
|
|
27
|
+
.map(code => ({ label: `${code.name} (${code.tag})`, value: code.tag }));
|
|
28
|
+
|
|
29
|
+
return locales.length > 0 ? locales[0] : null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async values(query: string, ctxt: ISelectionProviderContext): Promise<readonly ISelectionProviderValues[]> {
|
|
33
|
+
|
|
34
|
+
const locales = locale.all
|
|
35
|
+
.filter(code => {
|
|
36
|
+
// Look at the documentation here - https://www.npmjs.com/package/locale-codes
|
|
37
|
+
// In this if you check the section - https://www.npmjs.com/package/locale-codes#locale-list
|
|
38
|
+
|
|
39
|
+
// We disabled this because this was not returning the basic locale codes like 'en', 'fr', etc.
|
|
40
|
+
// We are only interested in the locales that have a tag with a hyphen (-) in it.
|
|
41
|
+
// if (code.tag.includes('-')) {
|
|
42
|
+
if (query) {
|
|
43
|
+
const lowerCaseQuery = query.toLowerCase();
|
|
44
|
+
return code.name.toLowerCase().includes(lowerCaseQuery) || code.tag.toLowerCase().includes(lowerCaseQuery);
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
// }
|
|
48
|
+
return false;
|
|
49
|
+
})
|
|
50
|
+
.map(code => ({
|
|
51
|
+
label: `${code.name} (${code.tag})`,
|
|
52
|
+
value: code.tag,
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
return locales;
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
}
|