@rapidd/build 1.0.0 β†’ 1.0.2

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/README.md CHANGED
@@ -11,6 +11,7 @@ Dynamic code generator that transforms Prisma schemas into complete Express.js C
11
11
  - πŸ‘₯ **Role-Based Access Control** - Properly handles role checks in filters
12
12
  - πŸ“Š **Model Generation** - Creates CRUD model classes with capitalized filenames
13
13
  - πŸ—ΊοΈ **Relationships JSON** - Generates complete relationship mappings with foreign keys
14
+ - ⚑ **Selective Generation** - Update only specific models or components
14
15
 
15
16
  ## Installation
16
17
 
@@ -21,16 +22,77 @@ npm install @rapidd/build
21
22
  ## Quick Start
22
23
 
23
24
  ```bash
24
- # Generate in current directory (default)
25
+ # Generate everything in current directory (default)
25
26
  npx rapidd build
26
27
 
27
28
  # Generate in specific directory
28
29
  npx rapidd build --output ./generated
29
30
 
31
+ # Generate only specific model
32
+ npx rapidd build --model user
33
+
34
+ # Generate only specific component
35
+ npx rapidd build --only model
36
+ npx rapidd build --only route
37
+ npx rapidd build --only rls
38
+ npx rapidd build --only relationship
39
+
40
+ # Combine model and component filters
41
+ npx rapidd build --model account --only route
42
+
30
43
  # Specify custom user table
31
44
  npx rapidd build --user-table accounts
32
45
  ```
33
46
 
47
+ ## CLI Options
48
+
49
+ - `-o, --output <path>` - Output directory (default: `./`)
50
+ - `-s, --schema <path>` - Prisma schema file (default: `./prisma/schema.prisma`)
51
+ - `-m, --model <name>` - Generate/update only specific model (e.g., "account", "user")
52
+ - `--only <component>` - Generate only specific component: "model", "route", "rls", or "relationship"
53
+ - `--user-table <name>` - User table name for RLS (default: auto-detected)
54
+
55
+ ## Selective Generation
56
+
57
+ ### Update Single Model
58
+
59
+ ```bash
60
+ # Update only the account model across all components
61
+ npx rapidd build --model account
62
+ ```
63
+
64
+ This will:
65
+ - Generate/update `src/Model/Account.js`
66
+ - Generate/update `routes/api/v1/account.js`
67
+ - Update the `account` entry in `rapidd/relationships.json`
68
+ - Update the `account` entry in `rapidd/rls.js`
69
+
70
+ ### Update Single Component
71
+
72
+ ```bash
73
+ # Regenerate all routes
74
+ npx rapidd build --only route
75
+
76
+ # Regenerate all RLS configs
77
+ npx rapidd build --only rls
78
+
79
+ # Regenerate all models
80
+ npx rapidd build --only model
81
+
82
+ # Regenerate relationships
83
+ npx rapidd build --only relationship
84
+ ```
85
+
86
+ ### Combine Filters
87
+
88
+ ```bash
89
+ # Update only the route for a specific model
90
+ npx rapidd build --model user --only route
91
+
92
+ # Update RLS for account model
93
+ npx rapidd build --model account --only rls
94
+ ```
95
+
34
96
  ## Generated Structure
35
97
 
36
98
  ```
@@ -69,18 +131,39 @@ getAccessFilter: (user) => {
69
131
  }
70
132
  ```
71
133
 
72
- ## CLI Options
73
-
74
- - `-o, --output <path>` - Output directory (default: `./`)
75
- - `-s, --schema <path>` - Prisma schema file (default: `./prisma/schema.prisma`)
76
- - `-u, --user-table <name>` - User table name for RLS (default: auto-detected)
77
-
78
134
  ## Usage with PostgreSQL RLS
79
135
 
80
136
  ```bash
81
137
  DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" npx rapidd build
82
138
  ```
83
139
 
140
+ ## Use Cases
141
+
142
+ ### During Development
143
+ ```bash
144
+ # After adding a new model to schema
145
+ npx rapidd build --model newModel
146
+
147
+ # After changing relationships
148
+ npx rapidd build --only relationship
149
+
150
+ # After updating RLS policies
151
+ npx rapidd build --only rls
152
+ ```
153
+
154
+ ### Continuous Integration
155
+ ```bash
156
+ # Full rebuild for CI/CD
157
+ npx rapidd build --output ./generated
158
+ ```
159
+
160
+ ### Incremental Updates
161
+ ```bash
162
+ # Update specific model after schema changes
163
+ npx rapidd build --model user --only model
164
+ npx rapidd build --model user --only rls
165
+ ```
166
+
84
167
  ## License
85
168
 
86
169
  MIT
package/bin/cli.js CHANGED
@@ -16,7 +16,10 @@ program
16
16
  .description('Build model files from Prisma schema')
17
17
  .option('-s, --schema <path>', 'Path to Prisma schema file', process.env.PRISMA_SCHEMA_PATH || './prisma/schema.prisma')
18
18
  .option('-o, --output <path>', 'Output base directory', './')
19
+ .option('-m, --model <name>', 'Generate/update only specific model (e.g., "account", "user")')
20
+ .option('--only <component>', 'Generate only specific component: "model", "route", "rls", or "relationship"')
19
21
  .option('--user-table <name>', 'Name of the user table for RLS (default: auto-detect from user/users)')
22
+ .option('--debug', 'Enable debug mode (generates rls-mappings.json)')
20
23
  .action(async (options) => {
21
24
  try {
22
25
  await buildModels(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rapidd/build",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Dynamic code generator that transforms Prisma schemas into Express.js CRUD APIs with PostgreSQL RLS-to-JavaScript translation",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -12,12 +12,12 @@
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",
15
- "url": "https://github.com/rapidd/build"
15
+ "url": "https://github.com/MertDalbudak/rapidd-build"
16
16
  },
17
17
  "bugs": {
18
- "url": "https://github.com/rapidd/build/issues"
18
+ "url": "https://github.com/MertDalbudak/rapidd-build/issues"
19
19
  },
20
- "homepage": "https://github.com/rapidd/build#readme",
20
+ "homepage": "https://github.com/MertDalbudak/rapidd-build#readme",
21
21
  "keywords": [
22
22
  "rapidd",
23
23
  "prisma",
@@ -12,7 +12,7 @@ const { generateAllRoutes } = require('../generators/routeGenerator');
12
12
  */
13
13
  function generateBaseModelFile(modelJsPath) {
14
14
  const content = `const { QueryBuilder, prisma } = require("./QueryBuilder");
15
- const {ErrorResponse} = require('./Api');
15
+ const {ErrorResponse, getTranslation} = require('./Api');
16
16
 
17
17
  class Model {
18
18
  /**
@@ -50,7 +50,8 @@ class Model {
50
50
  sortBy = sortBy.trim();
51
51
  sortOrder = sortOrder.trim();
52
52
  if (!sortBy.includes('.') && this.fields[sortBy] == undefined) {
53
- throw new ErrorResponse(\`Parameter sortBy '\${sortBy}' is not a valid field of \${this.constructor.name}\`, 400);
53
+ const message = getTranslation("invalid_sort_field", {sortBy, modelName: this.constructor.name});
54
+ throw new ErrorResponse(message, 400);
54
55
  }
55
56
 
56
57
  // Query the database using Prisma with filters, pagination, and limits
@@ -71,37 +72,40 @@ class Model {
71
72
  */
72
73
  _get = async (id, include, options = {}) =>{
73
74
  const {omit, ..._options} = options;
74
- id = Number(id)
75
+ id = Number(id);
75
76
  // To determine if the record is inaccessible, either due to non-existence or insufficient permissions, two simultaneous queries are performed.
76
77
  const _response = this.prisma.findUnique({
77
78
  'where': {
78
- 'id': id,
79
- ...this.getAccessFilter()
79
+ 'id': id
80
80
  },
81
81
  'include': this.include(include),
82
82
  'omit': {...this._omit(), ...omit},
83
83
  ..._options
84
84
  });
85
85
 
86
- const _checkExistence = this.prisma.findUnique({
86
+ const _checkPermission = this.prisma.findUnique({
87
87
  'where': {
88
- 'id': id
88
+ 'id': id,
89
+ ...this.getAccessFilter()
89
90
  },
90
91
  'select': {
91
92
  'id': true
92
93
  }
93
94
  });
94
95
 
95
- const [response, checkExistence] = await Promise.all([_response, _checkExistence]);
96
-
97
- if(response == null){
98
- if(checkExistence == null){
99
- throw new ErrorResponse("Record not found", 404);
96
+ const [response, checkPermission] = await Promise.all([_response, _checkPermission]);
97
+ if(response){
98
+ if(checkPermission){
99
+ if(response.id != checkExistence?.id){ // IN CASE access_filter CONTAINS id FIELD
100
+ throw new ErrorResponse(getTranslation("no_permission"), 403);
101
+ }
102
+ }
103
+ else{
104
+ throw new ErrorResponse(getTranslation("no_permission"), 403);
100
105
  }
101
- throw new ErrorResponse("No permission", 403);
102
106
  }
103
- if(response.id != checkExistence?.id){ // IN CASE access_filter CONTAINS id FIELD
104
- throw new ErrorResponse("No permission", 403);
107
+ else{
108
+ throw new ErrorResponse(getTranslation("record_not_found"), 404);
105
109
  }
106
110
  return response;
107
111
  }
@@ -276,6 +280,12 @@ class Model {
276
280
  module.exports = {Model, QueryBuilder, prisma};
277
281
  `;
278
282
 
283
+ // Ensure src directory exists
284
+ const srcDir = path.dirname(modelJsPath);
285
+ if (!fs.existsSync(srcDir)) {
286
+ fs.mkdirSync(srcDir, { recursive: true });
287
+ }
288
+
279
289
  fs.writeFileSync(modelJsPath, content);
280
290
  console.log('βœ“ Generated src/Model.js');
281
291
  }
@@ -302,12 +312,175 @@ module.exports = {prisma, Prisma, rls};
302
312
  console.log('βœ“ Generated rapidd/rapidd.js');
303
313
  }
304
314
 
315
+ /**
316
+ * Update relationships.json for a specific model
317
+ */
318
+ async function updateRelationshipsForModel(filteredModels, relationshipsPath, prismaClientPath, schemaPath, usedDMMF) {
319
+ let existingRelationships = {};
320
+
321
+ // Load existing relationships if file exists
322
+ if (fs.existsSync(relationshipsPath)) {
323
+ try {
324
+ existingRelationships = JSON.parse(fs.readFileSync(relationshipsPath, 'utf8'));
325
+ } catch (error) {
326
+ console.warn('Could not parse existing relationships.json, will create new');
327
+ }
328
+ }
329
+
330
+ // Generate relationships for the filtered model(s)
331
+ let newRelationships = {};
332
+ if (usedDMMF) {
333
+ // Use DMMF to get relationships for specific model
334
+ const { generateRelationshipsFromDMMF } = require('../generators/relationshipsGenerator');
335
+ const tempPath = relationshipsPath + '.tmp';
336
+ await generateRelationshipsFromDMMF(prismaClientPath, tempPath);
337
+ const allRelationships = JSON.parse(fs.readFileSync(tempPath, 'utf8'));
338
+ fs.unlinkSync(tempPath);
339
+
340
+ // Extract only the filtered model's relationships
341
+ for (const modelName of Object.keys(filteredModels)) {
342
+ if (allRelationships[modelName]) {
343
+ newRelationships[modelName] = allRelationships[modelName];
344
+ }
345
+ }
346
+ } else {
347
+ // Use schema parser
348
+ const { generateRelationshipsFromSchema } = require('../generators/relationshipsGenerator');
349
+ const tempPath = relationshipsPath + '.tmp';
350
+ generateRelationshipsFromSchema(schemaPath, tempPath);
351
+ const allRelationships = JSON.parse(fs.readFileSync(tempPath, 'utf8'));
352
+ fs.unlinkSync(tempPath);
353
+
354
+ // Extract only the filtered model's relationships
355
+ for (const modelName of Object.keys(filteredModels)) {
356
+ if (allRelationships[modelName]) {
357
+ newRelationships[modelName] = allRelationships[modelName];
358
+ }
359
+ }
360
+ }
361
+
362
+ // Merge with existing relationships
363
+ const updatedRelationships = { ...existingRelationships, ...newRelationships };
364
+
365
+ // Write back to file
366
+ fs.writeFileSync(relationshipsPath, JSON.stringify(updatedRelationships, null, 2));
367
+ }
368
+
369
+ /**
370
+ * Update rls.js for a specific model
371
+ */
372
+ async function updateRLSForModel(filteredModels, allModels, rlsPath, datasource, userTable, relationships, debug = false) {
373
+ const { generateRLS } = require('../generators/rlsGeneratorV2');
374
+
375
+ // Generate RLS for the filtered model (but pass all models for user table detection)
376
+ const tempPath = rlsPath + '.tmp';
377
+ await generateRLS(
378
+ filteredModels,
379
+ tempPath,
380
+ datasource.url,
381
+ datasource.isPostgreSQL,
382
+ userTable,
383
+ relationships,
384
+ debug,
385
+ allModels
386
+ );
387
+
388
+ // Read the generated RLS for the specific model
389
+ const tempContent = fs.readFileSync(tempPath, 'utf8');
390
+ fs.unlinkSync(tempPath);
391
+
392
+ // Extract the model's RLS configuration
393
+ const modelName = Object.keys(filteredModels)[0];
394
+
395
+ // Find the start of the model definition
396
+ const modelStart = tempContent.indexOf(`${modelName}:`);
397
+ if (modelStart === -1) {
398
+ throw new Error(`Could not find model ${modelName} in generated RLS`);
399
+ }
400
+
401
+ // Find the matching closing brace by counting braces
402
+ let braceCount = 0;
403
+ let inString = false;
404
+ let stringChar = null;
405
+ let i = tempContent.indexOf('{', modelStart);
406
+ const contentStart = i;
407
+
408
+ for (; i < tempContent.length; i++) {
409
+ const char = tempContent[i];
410
+ const prevChar = i > 0 ? tempContent[i - 1] : '';
411
+
412
+ // Handle string literals
413
+ if ((char === '"' || char === "'" || char === '`') && prevChar !== '\\') {
414
+ if (!inString) {
415
+ inString = true;
416
+ stringChar = char;
417
+ } else if (char === stringChar) {
418
+ inString = false;
419
+ stringChar = null;
420
+ }
421
+ }
422
+
423
+ if (!inString) {
424
+ if (char === '{') braceCount++;
425
+ if (char === '}') braceCount--;
426
+
427
+ if (braceCount === 0) {
428
+ // Found the closing brace
429
+ const modelRls = tempContent.substring(modelStart, i + 1);
430
+ break;
431
+ }
432
+ }
433
+ }
434
+
435
+ if (braceCount !== 0) {
436
+ throw new Error(`Could not extract RLS for model ${modelName} - unmatched braces`);
437
+ }
438
+
439
+ const modelRls = tempContent.substring(modelStart, i + 1);
440
+
441
+ // Read existing rls.js
442
+ if (fs.existsSync(rlsPath)) {
443
+ let existingContent = fs.readFileSync(rlsPath, 'utf8');
444
+
445
+ // Check if model already exists in RLS
446
+ const existingModelPattern = new RegExp(`${modelName}:\\s*\\{[\\s\\S]*?\\n \\}(?=,|\\n)`);
447
+
448
+ if (existingModelPattern.test(existingContent)) {
449
+ // Replace existing model RLS
450
+ existingContent = existingContent.replace(existingModelPattern, modelRls);
451
+ } else {
452
+ // Add new model RLS before the closing of rls.model
453
+ // Find the last closing brace of a model object and add comma after it
454
+ existingContent = existingContent.replace(
455
+ /(\n \})\n(\};)/,
456
+ `$1,\n ${modelRls}\n$2`
457
+ );
458
+ }
459
+
460
+ fs.writeFileSync(rlsPath, existingContent);
461
+ console.log(`βœ“ Updated RLS for model: ${modelName}`);
462
+ } else {
463
+ // If rls.js doesn't exist, create it with just this model
464
+ await generateRLS(
465
+ filteredModels,
466
+ rlsPath,
467
+ datasource.url,
468
+ datasource.isPostgreSQL,
469
+ userTable,
470
+ relationships,
471
+ debug,
472
+ allModels
473
+ );
474
+ }
475
+ }
476
+
305
477
  /**
306
478
  * Build models from Prisma schema
307
479
  * @param {Object} options - Build options
308
480
  * @param {string} options.schema - Path to Prisma schema file
309
481
  * @param {string} options.output - Output directory for generated models
310
- * @param {string} options.relationships - Path to relationships.json file
482
+ * @param {string} options.model - Optional: specific model to generate
483
+ * @param {string} options.only - Optional: specific component to generate
311
484
  */
312
485
  async function buildModels(options) {
313
486
  const schemaPath = path.resolve(process.cwd(), options.schema);
@@ -378,67 +551,121 @@ async function buildModels(options) {
378
551
 
379
552
  const { models, enums } = parsedData;
380
553
 
381
- console.log(`Found ${Object.keys(models).length} models`);
554
+ // Filter models if --model option is provided
555
+ let filteredModels = models;
556
+ if (options.model) {
557
+ const modelName = options.model.toLowerCase();
558
+ const matchedModel = Object.keys(models).find(m => m.toLowerCase() === modelName);
382
559
 
383
- // Generate model files
384
- generateAllModels(models, modelDir, modelJsPath);
560
+ if (!matchedModel) {
561
+ throw new Error(`Model "${options.model}" not found in schema. Available models: ${Object.keys(models).join(', ')}`);
562
+ }
385
563
 
386
- // Generate src/Model.js (base Model class)
387
- console.log('\nGenerating src/Model.js...');
388
- generateBaseModelFile(modelJsPath);
564
+ filteredModels = { [matchedModel]: models[matchedModel] };
565
+ console.log(`Filtering to model: ${matchedModel}`);
566
+ }
389
567
 
390
- // Generate rapidd/rapidd.js
391
- console.log('Generating rapidd/rapidd.js...');
392
- generateRapiddFile(rapiddJsPath);
568
+ console.log(`Found ${Object.keys(models).length} models${options.model ? ` (generating ${Object.keys(filteredModels).length})` : ''}`);
393
569
 
394
- // Generate relationships.json
395
- console.log(`\nGenerating relationships.json...`);
570
+ // Determine which components to generate
571
+ const shouldGenerate = {
572
+ model: !options.only || options.only === 'model',
573
+ route: !options.only || options.only === 'route',
574
+ rls: !options.only || options.only === 'rls',
575
+ relationship: !options.only || options.only === 'relationship'
576
+ };
396
577
 
397
- try {
398
- if (usedDMMF) {
399
- await generateRelationshipsFromDMMF(prismaClientPath, relationshipsPath);
400
- } else {
401
- generateRelationshipsFromSchema(schemaPath, relationshipsPath);
402
- }
403
- console.log(`βœ“ Relationships file generated at: ${relationshipsPath}`);
404
- } catch (error) {
405
- console.error('Failed to generate relationships.json:', error.message);
406
- console.log('Note: You may need to create relationships.json manually.');
578
+ // Validate --only option
579
+ if (options.only && !['model', 'route', 'rls', 'relationship'].includes(options.only)) {
580
+ throw new Error(`Invalid --only value "${options.only}". Must be one of: model, route, rls, relationship`);
407
581
  }
408
582
 
409
- // Generate RLS configuration
410
- console.log(`\nGenerating RLS configuration...`);
583
+ // Generate model files
584
+ if (shouldGenerate.model) {
585
+ generateAllModels(filteredModels, modelDir, modelJsPath);
586
+ }
411
587
 
412
- // Load relationships for Prisma filter building
413
- let relationships = {};
414
- try {
415
- if (fs.existsSync(relationshipsPath)) {
416
- relationships = JSON.parse(fs.readFileSync(relationshipsPath, 'utf8'));
588
+ // Generate src/Model.js (base Model class) if it doesn't exist
589
+ if (!fs.existsSync(modelJsPath)) {
590
+ console.log('\nGenerating src/Model.js...');
591
+ generateBaseModelFile(modelJsPath);
592
+ }
593
+
594
+ // Generate rapidd/rapidd.js if it doesn't exist
595
+ if (!fs.existsSync(rapiddJsPath)) {
596
+ console.log('Generating rapidd/rapidd.js...');
597
+ generateRapiddFile(rapiddJsPath);
598
+ }
599
+
600
+ // Generate relationships.json
601
+ if (shouldGenerate.relationship) {
602
+ console.log(`\nGenerating relationships.json...`);
603
+
604
+ try {
605
+ if (options.model) {
606
+ // Update only specific model in relationships.json
607
+ await updateRelationshipsForModel(filteredModels, relationshipsPath, prismaClientPath, schemaPath, usedDMMF);
608
+ } else {
609
+ // Generate all relationships
610
+ if (usedDMMF) {
611
+ await generateRelationshipsFromDMMF(prismaClientPath, relationshipsPath);
612
+ } else {
613
+ generateRelationshipsFromSchema(schemaPath, relationshipsPath);
614
+ }
615
+ }
616
+ console.log(`βœ“ Relationships file generated at: ${relationshipsPath}`);
617
+ } catch (error) {
618
+ console.error('Failed to generate relationships.json:', error.message);
619
+ console.log('Note: You may need to create relationships.json manually.');
417
620
  }
418
- } catch (error) {
419
- console.warn('Could not load relationships.json:', error.message);
420
621
  }
421
622
 
422
- try {
423
- // Parse datasource from Prisma schema to get database URL
424
- const datasource = parseDatasource(schemaPath);
623
+ // Generate RLS configuration
624
+ if (shouldGenerate.rls) {
625
+ console.log(`\nGenerating RLS configuration...`);
626
+
627
+ // Load relationships for Prisma filter building
628
+ let relationships = {};
629
+ try {
630
+ if (fs.existsSync(relationshipsPath)) {
631
+ relationships = JSON.parse(fs.readFileSync(relationshipsPath, 'utf8'));
632
+ }
633
+ } catch (error) {
634
+ console.warn('Could not load relationships.json:', error.message);
635
+ }
425
636
 
426
- await generateRLS(
427
- models,
428
- rlsPath,
429
- datasource.url,
430
- datasource.isPostgreSQL,
431
- options.userTable,
432
- relationships
433
- );
434
- } catch (error) {
435
- console.error('Failed to generate RLS:', error.message);
436
- console.log('Generating permissive RLS fallback...');
437
- await generateRLS(models, rlsPath, null, false, options.userTable, relationships);
637
+ try {
638
+ // Parse datasource from Prisma schema to get database URL
639
+ const datasource = parseDatasource(schemaPath);
640
+
641
+ if (options.model) {
642
+ // Update only specific model in rls.js
643
+ await updateRLSForModel(filteredModels, models, rlsPath, datasource, options.userTable, relationships, options.debug);
644
+ } else {
645
+ // Generate RLS for all models
646
+ await generateRLS(
647
+ models,
648
+ rlsPath,
649
+ datasource.url,
650
+ datasource.isPostgreSQL,
651
+ options.userTable,
652
+ relationships,
653
+ options.debug
654
+ );
655
+ }
656
+ } catch (error) {
657
+ console.error('Failed to generate RLS:', error.message);
658
+ if (!options.model) {
659
+ console.log('Generating permissive RLS fallback...');
660
+ await generateRLS(models, rlsPath, null, false, options.userTable, relationships, options.debug);
661
+ }
662
+ }
438
663
  }
439
664
 
440
665
  // Generate routes
441
- generateAllRoutes(models, routesDir);
666
+ if (shouldGenerate.route) {
667
+ generateAllRoutes(filteredModels, routesDir);
668
+ }
442
669
 
443
670
  return { models, enums };
444
671
  }
@@ -259,7 +259,7 @@ function generateFilter(policies, expressionField) {
259
259
  * @param {boolean} isPostgreSQL - Whether database is PostgreSQL
260
260
  * @param {string} userTableOption - User-specified table name
261
261
  */
262
- async function generateRLS(models, outputPath, databaseUrl, isPostgreSQL, userTableOption) {
262
+ async function generateRLS(models, outputPath, databaseUrl, isPostgreSQL, userTableOption, debug = false) {
263
263
  const userTable = detectUserTable(models, userTableOption);
264
264
  const modelNames = Object.keys(models);
265
265
 
@@ -277,11 +277,13 @@ async function generateRLS(models, outputPath, databaseUrl, isPostgreSQL, userTa
277
277
  functionAnalysis = await analyzeFunctions(databaseUrl);
278
278
  console.log(`βœ“ Analyzed ${Object.keys(functionAnalysis.functionMappings).length} PostgreSQL functions`);
279
279
 
280
- // Save function analysis for debugging/manual adjustment
281
- const configPath = path.join(path.dirname(outputPath), 'rls-mappings.json');
282
- const mappingConfig = generateMappingConfig(functionAnalysis);
283
- fs.writeFileSync(configPath, JSON.stringify(mappingConfig, null, 2));
284
- console.log(`βœ“ Function mappings saved to ${configPath}`);
280
+ // Save function analysis for debugging/manual adjustment (only if --debug flag is set)
281
+ if (debug) {
282
+ const configPath = path.join(path.dirname(outputPath), 'rls-mappings.json');
283
+ const mappingConfig = generateMappingConfig(functionAnalysis);
284
+ fs.writeFileSync(configPath, JSON.stringify(mappingConfig, null, 2));
285
+ console.log(`βœ“ Function mappings saved to ${configPath}`);
286
+ }
285
287
  } catch (error) {
286
288
  console.warn(`⚠ Could not analyze functions: ${error.message}`);
287
289
  }
@@ -285,8 +285,10 @@ function buildConditionalFilter(filtersWithRoles) {
285
285
  /**
286
286
  * Generate complete rls.js file
287
287
  */
288
- async function generateRLS(models, outputPath, databaseUrl, isPostgreSQL, userTableOption, relationships = {}) {
289
- const userTable = detectUserTable(models, userTableOption);
288
+ async function generateRLS(models, outputPath, databaseUrl, isPostgreSQL, userTableOption, relationships = {}, debug = false, allModels = null) {
289
+ // Use allModels for user table detection if provided (when filtering by model)
290
+ const modelsForUserDetection = allModels || models;
291
+ const userTable = detectUserTable(modelsForUserDetection, userTableOption);
290
292
  const modelNames = Object.keys(models);
291
293
 
292
294
  let policies = {};
@@ -313,11 +315,13 @@ async function generateRLS(models, outputPath, databaseUrl, isPostgreSQL, userTa
313
315
  relationships
314
316
  );
315
317
 
316
- // Save function analysis for debugging
317
- const configPath = path.join(path.dirname(outputPath), 'rls-mappings.json');
318
- const mappingConfig = generateMappingConfig(functionAnalysis);
319
- fs.writeFileSync(configPath, JSON.stringify(mappingConfig, null, 2));
320
- console.log(`βœ“ Function mappings saved to ${configPath}`);
318
+ // Save function analysis for debugging (only if --debug flag is set)
319
+ if (debug) {
320
+ const configPath = path.join(path.dirname(outputPath), 'rls-mappings.json');
321
+ const mappingConfig = generateMappingConfig(functionAnalysis);
322
+ fs.writeFileSync(configPath, JSON.stringify(mappingConfig, null, 2));
323
+ console.log(`βœ“ Function mappings saved to ${configPath}`);
324
+ }
321
325
 
322
326
  // Also add user context requirements as a comment in rls.js
323
327
  if (Object.keys(functionAnalysis.userContextRequirements).length > 0) {
@@ -7,11 +7,9 @@ const path = require('path');
7
7
  * @returns {string} - Generated route code
8
8
  */
9
9
  function generateRouteFile(modelName) {
10
- const modelNameLower = modelName.toLowerCase();
11
10
  const className = modelName.charAt(0).toUpperCase() + modelName.slice(1);
12
11
 
13
12
  return `const router = require('express').Router();
14
- const {Api, ErrorResponse} = require('../../../src/Api');
15
13
  const {${className}, QueryBuilder, prisma} = require('../../../src/Model/${className}');
16
14
 
17
15
  router.all('*', async (req, res, next) => {
@@ -20,13 +18,12 @@ router.all('*', async (req, res, next) => {
20
18
  next();
21
19
  }
22
20
  else{
23
- res.status(401).send({'status_code': res.statusCode, 'message': "No valid session"});
21
+ return res.sendError(401, req.getTranslation("no_valid_session"));
24
22
  }
25
23
  });
26
24
 
27
25
  // GET ALL
28
26
  router.get('/', async function(req, res) {
29
- let response, status_code = 200;
30
27
  try {
31
28
  const { q = {}, include = "", limit = 25, offset = 0, sortBy = "id", sortOrder = "asc" } = req.query;
32
29
 
@@ -34,67 +31,64 @@ router.get('/', async function(req, res) {
34
31
  const _count = req.${className}.count(q);
35
32
  const [data, count] = await Promise.all([_data, _count]);
36
33
 
37
- response = Api.getListResponseBody(data, {'take': req.${className}.take(Number(limit)), 'skip': req.${className}.skip(Number(offset)), 'total': count});
34
+ return res.sendList(data, {'take': req.${className}.take(Number(limit)), 'skip': req.${className}.skip(Number(offset)), 'total': count});
38
35
  }
39
36
  catch(error){
40
- response = QueryBuilder.errorHandler(error);
41
- status_code = response.status_code;
37
+ const response = QueryBuilder.errorHandler(error);
38
+ return res.status(response.status_code).send(response);
42
39
  }
43
- res.status(status_code).send(response);
44
40
  });
45
41
 
46
42
  // GET BY ID
47
43
  router.get('/:id', async function(req, res) {
48
- let response, status_code = 200;
49
44
  try{
50
45
  const { include = ""} = req.query;
51
- response = await req.${className}.get(req.params.id, include);
46
+ const response = await req.${className}.get(req.params.id, include);
47
+ return res.json(response);
52
48
  }
53
49
  catch(error){
54
- response = QueryBuilder.errorHandler(error);
55
- status_code = response.status_code;
50
+ const response = QueryBuilder.errorHandler(error);
51
+ return res.status(response.status_code).send(response);
56
52
  }
57
- res.status(status_code).send(response);
58
53
  });
59
54
 
60
55
  // CREATE
61
56
  router.post('/', async function(req, res) {
62
- let response, status_code = 201, payload = req.body;
57
+ const payload = req.body;
63
58
  try{
64
- response = await req.${className}.create(payload);
59
+ const response = await req.${className}.create(payload);
60
+ return res.status(201).json(response);
65
61
  }
66
62
  catch(error){
67
- response = QueryBuilder.errorHandler(error, payload);
68
- status_code = response.status_code;
63
+ const response = QueryBuilder.errorHandler(error, payload);
64
+ return res.status(response.status_code).send(response);
69
65
  }
70
- res.status(status_code).send(response);
71
66
  });
72
67
 
73
68
  // UPDATE
74
69
  router.patch('/:id', async function(req, res) {
75
- let response, status_code = 200, payload = req.body;
70
+ const payload = req.body;
76
71
  try{
77
- response = await req.${className}.update(req.params.id, payload);
72
+ const response = await req.${className}.update(req.params.id, payload);
73
+ return res.json(response);
78
74
  }
79
75
  catch(error){
80
- response = QueryBuilder.errorHandler(error, payload);
81
- status_code = response.status_code;
76
+ const response = QueryBuilder.errorHandler(error, payload);
77
+ return res.status(response.status_code).send(response);
82
78
  }
83
- res.status(status_code).send(response);
84
79
  });
85
80
 
86
81
  // DELETE
87
82
  router.delete('/:id', async (req, res)=>{
88
- let response, status_code = 200;
89
83
  try{
90
84
  await req.${className}.delete(req.params.id);
91
- response = {'status_code': status_code, 'message': "${className} successfully deleted"}
85
+ const message = req.getTranslation("object_deleted_successfully", {modelName: "${className}"});
86
+ return res.sendResponse(200, message);
92
87
  }
93
88
  catch(error){
94
- response = QueryBuilder.errorHandler(error);
95
- status_code = response.status_code;
89
+ const response = QueryBuilder.errorHandler(error);
90
+ return res.status(response.status_code).send(response);
96
91
  }
97
- res.status(status_code).send(response);
98
92
  });
99
93
 
100
94
  module.exports = router;