@rapidd/build 1.0.3 → 1.0.4
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/package.json +1 -1
- package/src/commands/build.js +102 -15
- package/src/generators/modelGenerator.js +1 -12
package/package.json
CHANGED
package/src/commands/build.js
CHANGED
|
@@ -12,7 +12,8 @@ const { generateAllRoutes } = require('../generators/routeGenerator');
|
|
|
12
12
|
*/
|
|
13
13
|
function generateBaseModelFile(modelJsPath) {
|
|
14
14
|
const content = `const { QueryBuilder, prisma } = require("./QueryBuilder");
|
|
15
|
-
const {
|
|
15
|
+
const {rls} = require('../../rapidd/rapidd');
|
|
16
|
+
const {ErrorResponse} = require('./Api');
|
|
16
17
|
|
|
17
18
|
class Model {
|
|
18
19
|
/**
|
|
@@ -21,17 +22,23 @@ class Model {
|
|
|
21
22
|
*/
|
|
22
23
|
constructor(name, options){
|
|
23
24
|
this.modelName = name;
|
|
25
|
+
this.queryBuilder = new QueryBuilder(name);
|
|
26
|
+
this.rls = rls.model[name] || {};
|
|
24
27
|
this.options = options || {}
|
|
25
28
|
this.user = this.options.user || {'id': 1, 'role': 'application'};
|
|
26
29
|
this.user_id = this.user ? this.user.id : null;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
_select = (fields) => this.
|
|
30
|
-
_filter = (q) => this.
|
|
31
|
-
_include = (include) => this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
_select = (fields) => this.queryBuilder.select(fields);
|
|
33
|
+
_filter = (q) => this.queryBuilder.filter(q);
|
|
34
|
+
_include = (include) => this.queryBuilder.include(include, this.user);
|
|
35
|
+
// RLS METHODS
|
|
36
|
+
_canCreate = () => this.rls.canCreate?.(this.user) || false;
|
|
37
|
+
_hasAccess = (data) => this.rls.hasAccess?.(data, this.user) || false;
|
|
38
|
+
_getAccessFilter = () => this.rls.getAccessFilter?.(this.user);
|
|
39
|
+
_getUpdateFilter = () => this.rls.getUpdateFilter?.(this.user);
|
|
40
|
+
_getDeleteFilter = () => this.rls.getDeleteFilter?.(this.user);
|
|
41
|
+
_omit = () => this.queryBuilder.omit(this.user);
|
|
35
42
|
|
|
36
43
|
/**
|
|
37
44
|
*
|
|
@@ -50,8 +57,7 @@ class Model {
|
|
|
50
57
|
sortBy = sortBy.trim();
|
|
51
58
|
sortOrder = sortOrder.trim();
|
|
52
59
|
if (!sortBy.includes('.') && this.fields[sortBy] == undefined) {
|
|
53
|
-
|
|
54
|
-
throw new ErrorResponse(message, 400);
|
|
60
|
+
throw new ErrorResponse(400, "invalid_sort_field", {sortBy, modelName: this.constructor.name});
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
// Query the database using Prisma with filters, pagination, and limits
|
|
@@ -114,8 +120,13 @@ class Model {
|
|
|
114
120
|
* @returns {Promise<Object>}
|
|
115
121
|
*/
|
|
116
122
|
_create = async (data, options = {}) => {
|
|
123
|
+
// CHECK CREATE PERMISSION
|
|
124
|
+
if (!this.canCreate()) {
|
|
125
|
+
throw new ErrorResponse(403, "no_permission_to_create");
|
|
126
|
+
}
|
|
127
|
+
|
|
117
128
|
// VALIDATE PASSED FIELDS AND RELATIONSHIPS
|
|
118
|
-
this.
|
|
129
|
+
this.queryBuilder.create(data, this.user_id);
|
|
119
130
|
|
|
120
131
|
// CREATE
|
|
121
132
|
return await this.prisma.create({
|
|
@@ -132,11 +143,32 @@ class Model {
|
|
|
132
143
|
*/
|
|
133
144
|
_update = async (id, data, options = {}) => {
|
|
134
145
|
id = Number(id);
|
|
135
|
-
|
|
146
|
+
|
|
147
|
+
// CHECK UPDATE PERMISSION
|
|
148
|
+
const updateFilter = this.getUpdateFilter();
|
|
149
|
+
if (updateFilter === false) {
|
|
150
|
+
throw new ErrorResponse(403, "no_permission_to_update");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// GET DATA FIRST (also checks read access)
|
|
136
154
|
const current_data = await this._get(id, "ALL");
|
|
137
155
|
|
|
156
|
+
// If updateFilter is not empty, verify the record matches the filter
|
|
157
|
+
if (Object.keys(updateFilter).length > 0) {
|
|
158
|
+
const canUpdate = await this.prisma.findUnique({
|
|
159
|
+
'where': {
|
|
160
|
+
'id': id,
|
|
161
|
+
...updateFilter
|
|
162
|
+
},
|
|
163
|
+
'select': { 'id': true }
|
|
164
|
+
});
|
|
165
|
+
if (!canUpdate) {
|
|
166
|
+
throw new ErrorResponse(403, "no_permission_to_update");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
138
170
|
// VALIDATE PASSED FIELDS AND RELATIONSHIPS
|
|
139
|
-
this.
|
|
171
|
+
this.queryBuilder.update(id, data, this.user_id);
|
|
140
172
|
return await this.prisma.update({
|
|
141
173
|
'where': {
|
|
142
174
|
'id': id
|
|
@@ -163,9 +195,31 @@ class Model {
|
|
|
163
195
|
* @returns {Promise<Object>}
|
|
164
196
|
*/
|
|
165
197
|
_delete = async (id, options = {}) => {
|
|
166
|
-
|
|
198
|
+
id = Number(id);
|
|
199
|
+
|
|
200
|
+
// CHECK DELETE PERMISSION
|
|
201
|
+
const deleteFilter = this.getDeleteFilter();
|
|
202
|
+
if (deleteFilter === false) {
|
|
203
|
+
throw new ErrorResponse(403, "no_permission_to_delete");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// GET DATA FIRST (also checks read access)
|
|
167
207
|
const current_data = await this._get(id);
|
|
168
208
|
|
|
209
|
+
// If deleteFilter is not empty, verify the record matches the filter
|
|
210
|
+
if (Object.keys(deleteFilter).length > 0) {
|
|
211
|
+
const canDelete = await this.prisma.findUnique({
|
|
212
|
+
'where': {
|
|
213
|
+
'id': id,
|
|
214
|
+
...deleteFilter
|
|
215
|
+
},
|
|
216
|
+
'select': { 'id': true }
|
|
217
|
+
});
|
|
218
|
+
if (!canDelete) {
|
|
219
|
+
throw new ErrorResponse(403, "no_permission_to_delete");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
169
223
|
return await this.prisma.delete({
|
|
170
224
|
'where': {
|
|
171
225
|
id: parseInt(id)
|
|
@@ -233,10 +287,10 @@ class Model {
|
|
|
233
287
|
return this._include(include);
|
|
234
288
|
}
|
|
235
289
|
sort(sortBy, sortOrder) {
|
|
236
|
-
return this.
|
|
290
|
+
return this.queryBuilder.sort(sortBy, sortOrder);
|
|
237
291
|
}
|
|
238
292
|
take(limit){
|
|
239
|
-
return this.
|
|
293
|
+
return this.queryBuilder.take(Number(limit));
|
|
240
294
|
}
|
|
241
295
|
skip(offset){
|
|
242
296
|
const parsed = parseInt(offset);
|
|
@@ -267,6 +321,39 @@ class Model {
|
|
|
267
321
|
return this.user.role == "application" ? true : this._hasAccess(data, this.user);
|
|
268
322
|
}
|
|
269
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Check if user can create records
|
|
326
|
+
* @returns {boolean}
|
|
327
|
+
*/
|
|
328
|
+
canCreate() {
|
|
329
|
+
if(this.user.role == "application") return true;
|
|
330
|
+
return this._canCreate();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Get update filter for RLS
|
|
335
|
+
* @returns {Object|false}
|
|
336
|
+
*/
|
|
337
|
+
getUpdateFilter(){
|
|
338
|
+
const filter = this._getUpdateFilter();
|
|
339
|
+
if(this.user.role == "application" || filter == true){
|
|
340
|
+
return {};
|
|
341
|
+
}
|
|
342
|
+
return filter;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Get delete filter for RLS
|
|
347
|
+
* @returns {Object|false}
|
|
348
|
+
*/
|
|
349
|
+
getDeleteFilter(){
|
|
350
|
+
const filter = this._getDeleteFilter();
|
|
351
|
+
if(this.user.role == "application" || filter == true){
|
|
352
|
+
return {};
|
|
353
|
+
}
|
|
354
|
+
return filter;
|
|
355
|
+
}
|
|
356
|
+
|
|
270
357
|
set modelName (name){
|
|
271
358
|
this.name = name;
|
|
272
359
|
this.prisma = prisma[name];
|
|
@@ -12,21 +12,10 @@ function generateModelFile(modelName, modelInfo) {
|
|
|
12
12
|
const className = modelName.charAt(0).toUpperCase() + modelName.slice(1);
|
|
13
13
|
|
|
14
14
|
return `const {Model, QueryBuilder, prisma} = require('../Model');
|
|
15
|
-
const {rls} = require('../../rapidd/rapidd');
|
|
16
15
|
|
|
17
16
|
class ${className} extends Model {
|
|
18
17
|
constructor(options){
|
|
19
|
-
super('${
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
static queryBuilder = new QueryBuilder('${modelName}', rls.model.${modelName} || {});
|
|
23
|
-
|
|
24
|
-
static getAccessFilter(user) {
|
|
25
|
-
return rls.model.${modelName}?.getAccessFilter?.(user) || {};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
static hasAccess(data, user) {
|
|
29
|
-
return rls.model.${modelName}?.hasAccess?.(data, user) || true;
|
|
18
|
+
super('${className}', options);
|
|
30
19
|
}
|
|
31
20
|
|
|
32
21
|
/**
|