@solidstarters/solid-core 1.2.32 → 1.2.33
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 +8 -7
- package/dist/controllers/action-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/action-metadata.controller.js +30 -21
- package/dist/controllers/action-metadata.controller.js.map +1 -1
- package/dist/controllers/menu-item-metadata.controller.d.ts +8 -7
- package/dist/controllers/menu-item-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/menu-item-metadata.controller.js +30 -21
- package/dist/controllers/menu-item-metadata.controller.js.map +1 -1
- package/dist/controllers/mq-message-queue.controller.d.ts +8 -7
- package/dist/controllers/mq-message-queue.controller.d.ts.map +1 -1
- package/dist/controllers/mq-message-queue.controller.js +30 -21
- package/dist/controllers/mq-message-queue.controller.js.map +1 -1
- package/dist/controllers/mq-message.controller.d.ts +8 -7
- package/dist/controllers/mq-message.controller.d.ts.map +1 -1
- package/dist/controllers/mq-message.controller.js +30 -21
- package/dist/controllers/mq-message.controller.js.map +1 -1
- package/dist/controllers/permission-metadata.controller.d.ts +8 -7
- package/dist/controllers/permission-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/permission-metadata.controller.js +30 -21
- package/dist/controllers/permission-metadata.controller.js.map +1 -1
- package/dist/controllers/role-metadata.controller.d.ts +8 -7
- package/dist/controllers/role-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/role-metadata.controller.js +30 -21
- package/dist/controllers/role-metadata.controller.js.map +1 -1
- package/dist/controllers/setting.controller.d.ts +9 -8
- package/dist/controllers/setting.controller.d.ts.map +1 -1
- package/dist/controllers/setting.controller.js +34 -24
- package/dist/controllers/setting.controller.js.map +1 -1
- package/dist/controllers/user.controller.d.ts +10 -9
- package/dist/controllers/user.controller.d.ts.map +1 -1
- package/dist/controllers/user.controller.js +38 -27
- package/dist/controllers/user.controller.js.map +1 -1
- package/dist/controllers/view-metadata.controller.d.ts +8 -7
- package/dist/controllers/view-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/view-metadata.controller.js +30 -21
- package/dist/controllers/view-metadata.controller.js.map +1 -1
- package/dist/decorators/solid-request-context.decorator.d.ts +2 -0
- package/dist/decorators/solid-request-context.decorator.d.ts.map +1 -0
- package/dist/decorators/solid-request-context.decorator.js +14 -0
- package/dist/decorators/solid-request-context.decorator.js.map +1 -0
- package/dist/dtos/solid-request-context.dto.d.ts +5 -0
- package/dist/dtos/solid-request-context.dto.d.ts.map +1 -0
- package/dist/dtos/solid-request-context.dto.js +25 -0
- package/dist/dtos/solid-request-context.dto.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/services/crud-helper.service.d.ts +6 -0
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +28 -1
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +7 -7
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +55 -8
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
- package/dist/services/menu-item-metadata.service.js +2 -4
- package/dist/services/menu-item-metadata.service.js.map +1 -1
- package/dist/services/permission-metadata.service.d.ts +0 -6
- package/dist/services/permission-metadata.service.d.ts.map +1 -1
- package/dist/services/permission-metadata.service.js +1 -32
- package/dist/services/permission-metadata.service.js.map +1 -1
- package/dist/services/user.service.d.ts +1 -1
- package/dist/services/user.service.d.ts.map +1 -1
- package/dist/services/user.service.js +1 -1
- package/dist/services/user.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/controllers/action-metadata.controller.ts +16 -14
- package/src/controllers/menu-item-metadata.controller.ts +16 -14
- package/src/controllers/mq-message-queue.controller.ts +16 -14
- package/src/controllers/mq-message.controller.ts +16 -14
- package/src/controllers/permission-metadata.controller.ts +16 -14
- package/src/controllers/role-metadata.controller.ts +16 -14
- package/src/controllers/setting.controller.ts +18 -16
- package/src/controllers/user.controller.ts +20 -18
- package/src/controllers/view-metadata.controller.ts +16 -14
- package/src/decorators/solid-request-context.decorator.ts +17 -0
- package/src/dtos/solid-request-context.dto.ts +10 -0
- package/src/index.ts +2 -1
- package/src/services/crud-helper.service.ts +45 -11
- package/src/services/crud.service.ts +117 -59
- package/src/services/menu-item-metadata.service.ts +7 -8
- package/src/services/permission-metadata.service.ts +0 -29
- package/src/services/user.service.ts +1 -1
|
@@ -4,6 +4,8 @@ import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger';
|
|
|
4
4
|
import { ViewMetadataService } from '../services/view-metadata.service';
|
|
5
5
|
import { CreateViewMetadataDto } from '../dtos/create-view-metadata.dto';
|
|
6
6
|
import { UpdateViewMetadataDto } from '../dtos/update-view-metadata.dto';
|
|
7
|
+
import { SolidRequestContextDecorator } from 'src/decorators/solid-request-context.decorator';
|
|
8
|
+
import { SolidRequestContextDto } from 'src/dtos/solid-request-context.dto';
|
|
7
9
|
|
|
8
10
|
@ApiTags('App')
|
|
9
11
|
@Controller('view-metadata') //FIXME: Change this to the model plural name
|
|
@@ -13,23 +15,23 @@ export class ViewMetadataController {
|
|
|
13
15
|
@ApiBearerAuth("jwt")
|
|
14
16
|
@Post()
|
|
15
17
|
@UseInterceptors(AnyFilesInterceptor())
|
|
16
|
-
create(@Body() createDto: CreateViewMetadataDto, @UploadedFiles() files: Array<Express.Multer.File
|
|
17
|
-
return this.service.create(createDto, files);
|
|
18
|
+
create(@Body() createDto: CreateViewMetadataDto, @UploadedFiles() files: Array<Express.Multer.File>,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
|
|
19
|
+
return this.service.create(createDto, files,solidRequestContext);
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
@ApiBearerAuth("jwt")
|
|
21
23
|
@Post('/bulk')
|
|
22
24
|
@UseInterceptors(AnyFilesInterceptor())
|
|
23
|
-
insertMany(@Body() createDtos: CreateViewMetadataDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = []) {
|
|
24
|
-
return this.service.insertMany(createDtos, filesArray);
|
|
25
|
+
insertMany(@Body() createDtos: CreateViewMetadataDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = [],@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
|
|
26
|
+
return this.service.insertMany(createDtos, filesArray,solidRequestContext);
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
@ApiBearerAuth("jwt")
|
|
29
31
|
@Put(':id')
|
|
30
32
|
@UseInterceptors(AnyFilesInterceptor())
|
|
31
|
-
update(@Param('id') id: number, @Body() updateDto: UpdateViewMetadataDto, @UploadedFiles() files: Array<Express.Multer.File
|
|
32
|
-
return this.service.update(id, updateDto, files);
|
|
33
|
+
update(@Param('id') id: number, @Body() updateDto: UpdateViewMetadataDto, @UploadedFiles() files: Array<Express.Multer.File>,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
|
|
34
|
+
return this.service.update(id, updateDto, files,false,solidRequestContext);
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
@ApiBearerAuth("jwt")
|
|
@@ -44,25 +46,25 @@ export class ViewMetadataController {
|
|
|
44
46
|
@ApiQuery({ name: 'populateMedia', required: false, type: Array })
|
|
45
47
|
@ApiQuery({ name: 'filters', required: false, type: Array })
|
|
46
48
|
@Get()
|
|
47
|
-
async findMany(@Query() query: any) {
|
|
48
|
-
return this.service.find(query);
|
|
49
|
+
async findMany(@Query() query: any,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
|
|
50
|
+
return this.service.find(query,solidRequestContext);
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
@ApiBearerAuth("jwt")
|
|
52
54
|
@Get(':id')
|
|
53
|
-
async findOne(@Param('id') id: string, @Query() query: any) {
|
|
54
|
-
return this.service.findOne(+id, query);
|
|
55
|
+
async findOne(@Param('id') id: string, @Query() query: any,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
|
|
56
|
+
return this.service.findOne(+id, query,solidRequestContext);
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
@Delete('/bulk')
|
|
58
|
-
async deleteMany(@Body() ids: number[]) {
|
|
59
|
-
return this.service.deleteMany(ids);
|
|
60
|
+
async deleteMany(@Body() ids: number[],@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
|
|
61
|
+
return this.service.deleteMany(ids,solidRequestContext);
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
@ApiBearerAuth("jwt")
|
|
63
65
|
@Delete(':id')
|
|
64
|
-
async delete(@Param('id') id: number) {
|
|
65
|
-
return this.service.delete(id);
|
|
66
|
+
async delete(@Param('id') id: number,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
|
|
67
|
+
return this.service.delete(id,solidRequestContext);
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
@ApiBearerAuth("jwt")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { ActiveUserData } from '../interfaces/active-user-data.interface';
|
|
3
|
+
import { REQUEST_USER_KEY } from '../constants';
|
|
4
|
+
import { SolidRequestContextDto } from 'src/dtos/solid-request-context.dto';
|
|
5
|
+
|
|
6
|
+
export const SolidRequestContextDecorator = createParamDecorator(
|
|
7
|
+
(data: unknown, ctx: ExecutionContext): SolidRequestContextDto => {
|
|
8
|
+
const request = ctx.switchToHttp().getRequest();
|
|
9
|
+
const activeUser: ActiveUserData | undefined = request[REQUEST_USER_KEY]; // Get active user
|
|
10
|
+
|
|
11
|
+
// Create a new instance of SolidRequestContextDto and stamp user data
|
|
12
|
+
const solidRequestContext = new SolidRequestContextDto();
|
|
13
|
+
solidRequestContext.activeUser = activeUser;
|
|
14
|
+
|
|
15
|
+
return solidRequestContext; // Return modified request context DTO
|
|
16
|
+
},
|
|
17
|
+
);
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './config/iam.config'
|
|
|
10
10
|
export * from './config/cache.options'
|
|
11
11
|
|
|
12
12
|
export * from './decorators/active-user.decorator'
|
|
13
|
+
export * from './decorators/solid-request-context.decorator'
|
|
13
14
|
export * from './decorators/auth.decorator'
|
|
14
15
|
export * from './decorators/computed-field-provider.decorator'
|
|
15
16
|
export * from './decorators/is-not-in-enum.decorator'
|
|
@@ -19,8 +20,8 @@ export * from './decorators/roles.decorator'
|
|
|
19
20
|
export * from './decorators/selection-provider.decorator'
|
|
20
21
|
export * from './decorators/solid-database-module.decorator'
|
|
21
22
|
export * from './decorators/solid-service.decorator'
|
|
22
|
-
|
|
23
23
|
export * from './dtos/basic-filters.dto'
|
|
24
|
+
export * from './dtos/solid-request-context.dto'
|
|
24
25
|
export * from './dtos/change-password.dto'
|
|
25
26
|
export * from './dtos/confirm-forgot-password.dto'
|
|
26
27
|
export * from './dtos/create-action-metadata.dto'
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Brackets, SelectQueryBuilder, WhereExpressionBuilder } from "typeorm";
|
|
2
2
|
import { BasicFilterDto } from "../dtos/basic-filters.dto";
|
|
3
|
+
import { classify } from "@angular-devkit/core/src/utils/strings";
|
|
4
|
+
import { ActiveUserData } from "src/interfaces/active-user-data.interface";
|
|
3
5
|
|
|
4
6
|
export class CrudHelperService {
|
|
5
7
|
constructor(
|
|
6
8
|
|
|
7
9
|
) { }
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
|
|
10
12
|
private orderOptions(sort: any[] = []) {
|
|
11
13
|
const orderOptions = {};
|
|
12
14
|
sort.forEach((s: string) => {
|
|
@@ -51,7 +53,7 @@ export class CrudHelperService {
|
|
|
51
53
|
}
|
|
52
54
|
else { // Recursively call the applyFilters method to handle nested conditions
|
|
53
55
|
selectQb.leftJoin(`${alias}.${key}`, key);
|
|
54
|
-
this.applyFilters(qb, primaryFilterObj, key, selectQb);
|
|
56
|
+
this.applyFilters(qb, primaryFilterObj, key, selectQb);
|
|
55
57
|
}
|
|
56
58
|
});
|
|
57
59
|
}
|
|
@@ -139,7 +141,7 @@ export class CrudHelperService {
|
|
|
139
141
|
}, {});
|
|
140
142
|
}
|
|
141
143
|
|
|
142
|
-
private normalize(value: string|string[]): string[] {
|
|
144
|
+
private normalize(value: string | string[]): string[] {
|
|
143
145
|
if (!value) return [];// if the value is nullish, then return an empty array
|
|
144
146
|
return Array.isArray(value) ? value : [value]; // if the value is an array, return it as is, otherwise return it as an array
|
|
145
147
|
}
|
|
@@ -150,7 +152,7 @@ export class CrudHelperService {
|
|
|
150
152
|
|
|
151
153
|
buildFilterQuery(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string): SelectQueryBuilder<any> { //TODO : Check how to pass a type to SelectQueryBuilder instead of any
|
|
152
154
|
let { limit, offset, showSoftDeleted, filters } = basicFilterDto;
|
|
153
|
-
const { fields, sort, groupBy, populate = [] } = basicFilterDto;
|
|
155
|
+
const { fields, sort, groupBy, populate = [] } = basicFilterDto;
|
|
154
156
|
|
|
155
157
|
// 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)
|
|
156
158
|
const normalizedFields = this.normalize(fields);
|
|
@@ -158,7 +160,7 @@ export class CrudHelperService {
|
|
|
158
160
|
const normalizedSort = this.normalize(sort);
|
|
159
161
|
const normalizedGroupBy = this.normalize(groupBy);
|
|
160
162
|
if (normalizedGroupBy.length > 1) {
|
|
161
|
-
|
|
163
|
+
throw new Error('buildFilterQuery: Only 1 Group by field is supported currently');
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
if (filters) {
|
|
@@ -171,7 +173,7 @@ export class CrudHelperService {
|
|
|
171
173
|
if (normalizedFields && normalizedFields.length) {
|
|
172
174
|
qb.select(normalizedFields.map(field => {
|
|
173
175
|
// If the field contains a (, do not prefix the entity alias
|
|
174
|
-
return this.isAggregateField(field) ?
|
|
176
|
+
return this.isAggregateField(field) ? field : `${entityAlias}.${field}`;
|
|
175
177
|
}));
|
|
176
178
|
}
|
|
177
179
|
|
|
@@ -200,11 +202,11 @@ export class CrudHelperService {
|
|
|
200
202
|
if (showSoftDeleted === 'inclusive') {
|
|
201
203
|
qb.withDeleted();
|
|
202
204
|
}
|
|
203
|
-
|
|
205
|
+
|
|
204
206
|
if (showSoftDeleted === 'exclusive') {
|
|
205
207
|
qb.withDeleted();
|
|
206
208
|
qb.where(`${entityAlias}.deletedAt IS NOT NULL`);
|
|
207
|
-
}
|
|
209
|
+
}
|
|
208
210
|
|
|
209
211
|
// Apply the group by options
|
|
210
212
|
if (normalizedGroupBy && normalizedGroupBy.length) {
|
|
@@ -245,9 +247,9 @@ export class CrudHelperService {
|
|
|
245
247
|
|
|
246
248
|
getGroupName(group: any, alias: string): string {
|
|
247
249
|
return Object.keys(group)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
.filter(key => !this.isAggregateFieldKey(key, alias))
|
|
251
|
+
.map(key => group[key])
|
|
252
|
+
.join('_');
|
|
251
253
|
}
|
|
252
254
|
|
|
253
255
|
createGroupRecords(group: any, alias: string, groupData: any) {
|
|
@@ -272,4 +274,36 @@ export class CrudHelperService {
|
|
|
272
274
|
};
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
|
|
278
|
+
hasReadPermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
279
|
+
const permissionNames = [`${classify(modelName)}Controller.findOne`, `${classify(modelName)}Controller.findMany`];
|
|
280
|
+
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
281
|
+
return matchingPermssions.length > 0
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
hasWritePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
285
|
+
const permissionNames = [`${classify(modelName)}Controller.create`, `${classify(modelName)}Controller.insertMany`, `${classify(modelName)}Controller.update`];
|
|
286
|
+
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
287
|
+
return matchingPermssions.length > 0
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
hasUpdatePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
291
|
+
const permissionNames = [`${classify(modelName)}Controller.update`];
|
|
292
|
+
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
293
|
+
return matchingPermssions.length > 0
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
hasDeletePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
297
|
+
const permissionNames = [`${classify(modelName)}Controller.delete`, `${classify(modelName)}Controller.deleteMany`];
|
|
298
|
+
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
299
|
+
return matchingPermssions.length > 0
|
|
300
|
+
}
|
|
301
|
+
hasCreatePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
302
|
+
const permissionNames = [`${classify(modelName)}Controller.create`];
|
|
303
|
+
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
304
|
+
return matchingPermssions.length > 0
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
|
275
309
|
}
|
|
@@ -36,6 +36,7 @@ import { MediaService } from "./media.service";
|
|
|
36
36
|
import { getMediaStorageProvider } from "./mediaStorageProviders";
|
|
37
37
|
import { ModelMetadataService } from "./model-metadata.service";
|
|
38
38
|
import { ModuleMetadataService } from "./module-metadata.service";
|
|
39
|
+
import { SolidRequestContextDto } from "src/dtos/solid-request-context.dto";
|
|
39
40
|
|
|
40
41
|
const DEFAULT_LIMIT = 10;
|
|
41
42
|
const DEFAULT_OFFSET = 0;
|
|
@@ -57,7 +58,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
57
58
|
//We can just have the Model Entity here
|
|
58
59
|
) { }
|
|
59
60
|
|
|
60
|
-
async create(createDto: any, files: Express.Multer.File[] = []): Promise<T> {
|
|
61
|
+
async create(createDto: any, files: Express.Multer.File[] = [],solidRequestContext :any = {}): Promise<T> {
|
|
61
62
|
// This class will be extended by the generated service class i.e PersonService
|
|
62
63
|
// The data required to identify the model and module name will be passed from the generate CrudService subclass
|
|
63
64
|
//TODO: Algorithm to create the entity
|
|
@@ -66,6 +67,13 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
66
67
|
let hasMediaFields = false;
|
|
67
68
|
|
|
68
69
|
const model = await this.loadModel();
|
|
70
|
+
// Check wheather user has create permission for model
|
|
71
|
+
if (solidRequestContext.activeUser) {
|
|
72
|
+
const hasPermission = this.crudHelperService.hasCreatePermissionOnModel(solidRequestContext.activeUser, model.singularName);
|
|
73
|
+
if (!hasPermission) {
|
|
74
|
+
throw new BadRequestException('Forbidden');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
69
77
|
// const inverseRelationFields = await this.loadInverseRelationFields();
|
|
70
78
|
const fieldsToProcess = [...model.fields];
|
|
71
79
|
|
|
@@ -162,10 +170,18 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
162
170
|
}
|
|
163
171
|
|
|
164
172
|
//TODO: Will the updates be partial i.e PATCH or full i.e PUT
|
|
165
|
-
async update(id: number, updateDto: any, files: Express.Multer.File[] = [], isPartialUpdate: boolean = false): Promise<T> {
|
|
173
|
+
async update(id: number, updateDto: any, files: Express.Multer.File[] = [], isPartialUpdate: boolean = false, solidRequestContext: any = {}): Promise<T> {
|
|
166
174
|
if (!id) {
|
|
167
175
|
throw new Error('Id is required for update');
|
|
168
176
|
}
|
|
177
|
+
const model = await this.loadModel();
|
|
178
|
+
// Check wheather user has update permission for model
|
|
179
|
+
if (solidRequestContext.activeUser) {
|
|
180
|
+
const hasPermission = this.crudHelperService.hasUpdatePermissionOnModel(solidRequestContext.activeUser, model.singularName);
|
|
181
|
+
if (!hasPermission) {
|
|
182
|
+
throw new BadRequestException('Forbidden');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
169
185
|
const entity = await this.repo.findOne({
|
|
170
186
|
where: {
|
|
171
187
|
//@ts-ignore
|
|
@@ -184,7 +200,6 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
184
200
|
// FIXME This can be optimized to take in module name i.e (handle scenario wherein same model exists in multiple modules)
|
|
185
201
|
let hasMediaFields = false;
|
|
186
202
|
|
|
187
|
-
const model = await this.loadModel();
|
|
188
203
|
const fieldsToProcess = [...model.fields];
|
|
189
204
|
|
|
190
205
|
// 2. Loop through the fields with a switch statement
|
|
@@ -208,11 +223,19 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
208
223
|
}
|
|
209
224
|
|
|
210
225
|
//TODO: Will the updates be partial i.e PATCH or full i.e PUT
|
|
211
|
-
async delete(id: number) {
|
|
226
|
+
async delete(id: number, solidRequestContext: any = {}) {
|
|
212
227
|
if (!id) {
|
|
213
228
|
throw new Error('Id is required for update');
|
|
214
229
|
}
|
|
215
|
-
|
|
230
|
+
const loadedmodel = await this.loadModel();
|
|
231
|
+
// Check wheather user has update permission for model
|
|
232
|
+
if (solidRequestContext.activeUser) {
|
|
233
|
+
const hasPermission = this.crudHelperService.hasDeletePermissionOnModel(solidRequestContext.activeUser, loadedmodel.singularName);
|
|
234
|
+
if (!hasPermission) {
|
|
235
|
+
throw new BadRequestException('Forbidden');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
216
239
|
const model = await this.modelMetadataService.findOneBySingularName(this.modelName, {
|
|
217
240
|
fields: {
|
|
218
241
|
model: true,
|
|
@@ -305,25 +328,25 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
305
328
|
case SolidFieldType.relation: {
|
|
306
329
|
// Identify if the field is for the inverse side or not
|
|
307
330
|
if (fieldMetadata.relationType === RelationType.manyToOne) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
331
|
+
const manyToOneOptions: ManyToOneRelationFieldOptions = {
|
|
332
|
+
...commonOptions,
|
|
333
|
+
relationCoModelSingularName: fieldMetadata.relationCoModelSingularName,
|
|
334
|
+
modelUserKeyFieldName: fieldMetadata.model.userKeyField?.name,
|
|
335
|
+
modelSingularName: fieldMetadata.model.singularName,
|
|
336
|
+
entityManager,
|
|
337
|
+
}
|
|
338
|
+
return new ManyToOneRelationFieldCrudManager(manyToOneOptions);
|
|
316
339
|
}
|
|
317
340
|
else if (fieldMetadata.relationType === RelationType.oneToMany) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
341
|
+
const oneToManyOptions: OneToManyRelationFieldOptions = {
|
|
342
|
+
...commonOptions,
|
|
343
|
+
relationCoModelSingularName: fieldMetadata.relationCoModelSingularName,
|
|
344
|
+
modelSingularName: fieldMetadata.model.singularName,
|
|
345
|
+
entityManager,
|
|
346
|
+
inverseFieldName: fieldMetadata.relationCoModelFieldName,
|
|
347
|
+
inverseRelationCoModelFieldName: fieldMetadata.name,
|
|
348
|
+
}
|
|
349
|
+
return new OneToManyRelationFieldCrudManager(oneToManyOptions);
|
|
327
350
|
}
|
|
328
351
|
else if (fieldMetadata.relationType === RelationType.manyTomany) {
|
|
329
352
|
if (fieldMetadata.isRelationManyToManyOwner) {
|
|
@@ -335,7 +358,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
335
358
|
entityManager,
|
|
336
359
|
fieldName: fieldMetadata.name,
|
|
337
360
|
}
|
|
338
|
-
return new ManyToManyRelationFieldCrudManager(manyToManyOptions);
|
|
361
|
+
return new ManyToManyRelationFieldCrudManager(manyToManyOptions);
|
|
339
362
|
}
|
|
340
363
|
else {
|
|
341
364
|
const inverseManyToManyOptions: ManyToManyRelationFieldOptions = {
|
|
@@ -392,10 +415,18 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
392
415
|
}
|
|
393
416
|
|
|
394
417
|
|
|
395
|
-
async find(basicFilterDto: BasicFilterDto) {
|
|
418
|
+
async find(basicFilterDto: BasicFilterDto, solidRequestContext: any = {}) {
|
|
396
419
|
const alias = 'entity';
|
|
397
420
|
// Extract the required keys from the input query
|
|
398
|
-
let { limit, offset, populateMedia, populateGroup,groupFilter } = basicFilterDto;
|
|
421
|
+
let { limit, offset, populateMedia, populateGroup, groupFilter } = basicFilterDto;
|
|
422
|
+
const model = await this.loadModel();
|
|
423
|
+
// Check wheather user has update permission for model
|
|
424
|
+
if (solidRequestContext.activeUser) {
|
|
425
|
+
const hasPermission = this.crudHelperService.hasReadPermissionOnModel(solidRequestContext.activeUser, model.singularName);
|
|
426
|
+
if (!hasPermission) {
|
|
427
|
+
throw new BadRequestException('Forbidden');
|
|
428
|
+
}
|
|
429
|
+
}
|
|
399
430
|
|
|
400
431
|
// Create above query on pincode table using query builder
|
|
401
432
|
var qb: SelectQueryBuilder<T> = this.repo.createQueryBuilder(alias)
|
|
@@ -403,7 +434,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
403
434
|
|
|
404
435
|
if (basicFilterDto.groupBy) {
|
|
405
436
|
// Get the records and the count
|
|
406
|
-
const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter,populateGroup, alias, populateMedia);
|
|
437
|
+
const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter, populateGroup, alias, populateMedia);
|
|
407
438
|
return {
|
|
408
439
|
groupMeta,
|
|
409
440
|
groupRecords,
|
|
@@ -430,7 +461,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
430
461
|
return this.wrapFindResponse(offset, limit, count, entities);
|
|
431
462
|
}
|
|
432
463
|
|
|
433
|
-
private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto,populateGroup: boolean, alias: string, populateMedia: string[]) {
|
|
464
|
+
private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto, populateGroup: boolean, alias: string, populateMedia: string[]) {
|
|
434
465
|
const groupByResult = await qb.getRawMany();
|
|
435
466
|
|
|
436
467
|
const groupMeta = [];
|
|
@@ -515,8 +546,18 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
515
546
|
}
|
|
516
547
|
}
|
|
517
548
|
|
|
518
|
-
async findOne(id: number, query: any) {
|
|
549
|
+
async findOne(id: number, query: any, solidRequestContext: any = {}) {
|
|
519
550
|
const { populate = [], fields = [], populateMedia = [] } = query;
|
|
551
|
+
|
|
552
|
+
const model = await this.loadModel();
|
|
553
|
+
// Check wheather user has update permission for model
|
|
554
|
+
if (solidRequestContext.activeUser) {
|
|
555
|
+
const hasPermission = this.crudHelperService.hasReadPermissionOnModel(solidRequestContext.activeUser, model.singularName);
|
|
556
|
+
if (!hasPermission) {
|
|
557
|
+
throw new BadRequestException('Forbidden');
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
520
561
|
const entity = await this.repo.findOne({
|
|
521
562
|
where: {
|
|
522
563
|
//@ts-ignore
|
|
@@ -557,13 +598,22 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
557
598
|
return entity;
|
|
558
599
|
}
|
|
559
600
|
|
|
560
|
-
async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = []): Promise<T[]> {
|
|
601
|
+
async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = [],solidRequestContext: any = {}): Promise<T[]> {
|
|
561
602
|
|
|
562
603
|
|
|
563
604
|
// if (createDtos.length !== filesArray.length) {
|
|
564
605
|
// throw new BadRequestException('Mismatch between data objects and file arrays.');
|
|
565
606
|
// }
|
|
566
607
|
|
|
608
|
+
const loadedmodel = await this.loadModel();
|
|
609
|
+
// Check wheather user has create permission for model
|
|
610
|
+
if (solidRequestContext.activeUser) {
|
|
611
|
+
const hasPermission = this.crudHelperService.hasCreatePermissionOnModel(solidRequestContext.activeUser, loadedmodel.singularName);
|
|
612
|
+
if (!hasPermission) {
|
|
613
|
+
throw new BadRequestException('Forbidden');
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
567
617
|
// Fetch model metadata once
|
|
568
618
|
const model = await this.modelMetadataService.findOneBySingularName(this.modelName, {
|
|
569
619
|
fields: {
|
|
@@ -607,12 +657,20 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
607
657
|
return savedEntities;
|
|
608
658
|
}
|
|
609
659
|
|
|
610
|
-
async deleteMany(ids: number[]): Promise<any> {
|
|
660
|
+
async deleteMany(ids: number[],solidRequestContext: any = {}): Promise<any> {
|
|
611
661
|
|
|
612
662
|
if (!ids || ids.length === 0) {
|
|
613
663
|
throw new Error('At least one ID is required for deletion');
|
|
614
664
|
}
|
|
615
665
|
|
|
666
|
+
const loadedmodel = await this.loadModel();
|
|
667
|
+
// Check wheather user has update permission for model
|
|
668
|
+
if (solidRequestContext.activeUser) {
|
|
669
|
+
const hasPermission = this.crudHelperService.hasDeletePermissionOnModel(solidRequestContext.activeUser, loadedmodel.singularName);
|
|
670
|
+
if (!hasPermission) {
|
|
671
|
+
throw new BadRequestException('Forbidden');
|
|
672
|
+
}
|
|
673
|
+
}
|
|
616
674
|
const model = await this.modelMetadataService.findOneBySingularName(this.modelName, {
|
|
617
675
|
fields: {
|
|
618
676
|
model: true,
|
|
@@ -645,41 +703,41 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
645
703
|
|
|
646
704
|
async recover(id: number) {
|
|
647
705
|
try {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
706
|
+
const softDeletedRows = await this.repo.findOne({
|
|
707
|
+
where: {
|
|
708
|
+
//@ts-ignore
|
|
709
|
+
id, deletedAt: Not(IsNull())
|
|
710
|
+
},
|
|
711
|
+
withDeleted: true,
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
if (!softDeletedRows) {
|
|
715
|
+
throw new Error('No soft-deleted record found with the given ID.');
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
await this.repo.update(id, {
|
|
661
719
|
//@ts-ignore
|
|
662
|
-
deletedAt: null, deletedTracker: "not-deleted"
|
|
720
|
+
deletedAt: null, deletedTracker: "not-deleted"
|
|
663
721
|
});
|
|
664
|
-
|
|
665
|
-
|
|
722
|
+
|
|
723
|
+
return { message: 'Record successfully recovered', data: softDeletedRows };
|
|
666
724
|
} catch (error) {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
725
|
+
if (error instanceof QueryFailedError) {
|
|
726
|
+
if ((error as any).code === '23505') {
|
|
727
|
+
throw new Error('Another record is conflicting with the record you are attempting to Un-Archive, either delete or change the other record so as to avoid this conflict.');
|
|
728
|
+
}
|
|
670
729
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
throw new Error(error);
|
|
730
|
+
|
|
731
|
+
throw new Error(error);
|
|
674
732
|
}
|
|
675
733
|
}
|
|
676
|
-
|
|
734
|
+
|
|
677
735
|
async recoverMany(ids: number[]) {
|
|
678
736
|
try {
|
|
679
737
|
if (!ids || ids.length === 0) {
|
|
680
738
|
throw new Error("No IDs provided for recovery.");
|
|
681
739
|
}
|
|
682
|
-
|
|
740
|
+
|
|
683
741
|
// Find soft-deleted records matching the given IDs
|
|
684
742
|
const softDeletedRows = await this.repo.find({
|
|
685
743
|
where: {
|
|
@@ -689,18 +747,18 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
689
747
|
},
|
|
690
748
|
withDeleted: true,
|
|
691
749
|
});
|
|
692
|
-
|
|
750
|
+
|
|
693
751
|
if (softDeletedRows.length === 0) {
|
|
694
752
|
throw new Error("No matching soft-deleted records found.");
|
|
695
753
|
}
|
|
696
|
-
|
|
754
|
+
|
|
697
755
|
// Recover the specific records by setting deletedAt to null
|
|
698
756
|
await this.repo.update(
|
|
699
757
|
//@ts-ignore
|
|
700
758
|
{ id: In(ids) },
|
|
701
759
|
{ deletedAt: null, deletedTracker: "not-deleted" }
|
|
702
760
|
);
|
|
703
|
-
|
|
761
|
+
|
|
704
762
|
return { message: "Selected records successfully recovered", recoveredIds: ids };
|
|
705
763
|
} catch (error) {
|
|
706
764
|
if (error instanceof QueryFailedError) {
|
|
@@ -710,10 +768,10 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
|
|
|
710
768
|
);
|
|
711
769
|
}
|
|
712
770
|
}
|
|
713
|
-
|
|
771
|
+
|
|
714
772
|
throw new Error(error);
|
|
715
773
|
}
|
|
716
774
|
}
|
|
717
|
-
|
|
775
|
+
|
|
718
776
|
|
|
719
777
|
}
|
|
@@ -17,7 +17,6 @@ import { UpdateMenuItemMetadataDto } from '../dtos/update-menu-item-metadata.dto
|
|
|
17
17
|
import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
|
|
18
18
|
import { ModuleMetadata } from '../entities/module-metadata.entity';
|
|
19
19
|
import { dasherize } from '@angular-devkit/core/src/utils/strings';
|
|
20
|
-
import { hasReadPermissionOnModel } from './permission-metadata.service';
|
|
21
20
|
|
|
22
21
|
@Injectable()
|
|
23
22
|
export class MenuItemMetadataService extends CRUDService<MenuItemMetadata> {
|
|
@@ -148,13 +147,13 @@ export class MenuItemMetadataService extends CRUDService<MenuItemMetadata> {
|
|
|
148
147
|
// Recursive function to build the tree
|
|
149
148
|
private buildMenuTree(rootItems: MenuItemMetadata[], allMenuItems: MenuItemMetadata[], activeUser: ActiveUserData): any[] {
|
|
150
149
|
const menuItemsData = rootItems.map(rootItem => {
|
|
151
|
-
const allowedMenuItems =allMenuItems.filter(i => {
|
|
152
|
-
if(!i.parentMenuItem){
|
|
150
|
+
const allowedMenuItems = allMenuItems.filter(i => {
|
|
151
|
+
if (!i.parentMenuItem) {
|
|
153
152
|
return true
|
|
154
|
-
}else{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
} else {
|
|
154
|
+
return this.crudHelperService.hasReadPermissionOnModel(activeUser, i.action.model.singularName)
|
|
155
|
+
}
|
|
156
|
+
});
|
|
158
157
|
// Get immediate children of the current loop variable menuItem.
|
|
159
158
|
const children = allowedMenuItems.filter(item => item.parentMenuItem && item.parentMenuItem.id === rootItem.id);
|
|
160
159
|
|
|
@@ -166,7 +165,7 @@ export class MenuItemMetadataService extends CRUDService<MenuItemMetadata> {
|
|
|
166
165
|
path = rootItem.action.customComponent;
|
|
167
166
|
}
|
|
168
167
|
if (rootItem.action && rootItem.action.type === 'solid') {
|
|
169
|
-
if (hasReadPermissionOnModel(activeUser, rootItem.action.model.singularName)) {
|
|
168
|
+
if (this.crudHelperService.hasReadPermissionOnModel(activeUser, rootItem.action.model.singularName)) {
|
|
170
169
|
|
|
171
170
|
|
|
172
171
|
// TODO: Here we are assuming that we will always take the user to collection view of a model.
|
|
@@ -61,33 +61,4 @@ export class PermissionMetadataService extends CRUDService<PermissionMetadata> {
|
|
|
61
61
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export const hasReadPermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
65
|
-
const permissionNames = [`${classify(modelName)}Controller.findOne`, `${classify(modelName)}Controller.findMany`];
|
|
66
|
-
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
67
|
-
return matchingPermssions.length > 0
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export const hasWritePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
71
|
-
const permissionNames = [`${classify(modelName)}Controller.create`, `${classify(modelName)}Controller.insertMany`, `${classify(modelName)}Controller.update`];
|
|
72
|
-
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
73
|
-
return matchingPermssions.length > 0
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export const hasUpdatePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
77
|
-
const permissionNames = [`${classify(modelName)}Controller.update`];
|
|
78
|
-
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
79
|
-
return matchingPermssions.length > 0
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export const hasDeletePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
83
|
-
const permissionNames = [`${classify(modelName)}Controller.delete`, `${classify(modelName)}Controller.deleteMany`];
|
|
84
|
-
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
85
|
-
return matchingPermssions.length > 0
|
|
86
|
-
}
|
|
87
|
-
export const hasCreatePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
|
|
88
|
-
const permissionNames = [`${classify(modelName)}Controller.create`];
|
|
89
|
-
const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
|
|
90
|
-
return matchingPermssions.length > 0
|
|
91
|
-
}
|
|
92
|
-
|
|
93
64
|
|
|
@@ -75,7 +75,7 @@ export class UserService extends CRUDService<User> {
|
|
|
75
75
|
// return entity;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
async updateUser(id: any, updateDto, files) {
|
|
78
|
+
async updateUser(id: any, updateDto, files,solidRequestContext :any = {}) {
|
|
79
79
|
const user = await this.repo.findOne({
|
|
80
80
|
where: { id: id },
|
|
81
81
|
relations: {
|