@restforgejs/platform 5.1.7 → 5.1.16
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/bin/restforge-hwinfo-linux +0 -0
- package/bin/restforge-hwinfo.exe +0 -0
- package/build-info.json +2 -2
- package/cli/consumer-deploy.js +1 -1
- package/cli/consumer.js +1 -1
- package/generators/cli/payload/generate.js +10 -2
- package/generators/cli/schema/apply.js +6 -1
- package/generators/cli/schema/diff.js +6 -1
- package/generators/cli/schema/introspect.js +32 -11
- package/generators/lib/data/db-executor.js +8 -8
- package/generators/lib/data/envelope.js +3 -3
- package/generators/lib/dbschema-kit/apply-engine.js +20 -0
- package/generators/lib/dbschema-kit/dialect/mysql.js +2 -0
- package/generators/lib/dbschema-kit/dialect/oracle.js +2 -0
- package/generators/lib/dbschema-kit/dialect/postgres.js +4 -0
- package/generators/lib/dbschema-kit/dialect/sqlite.js +5 -0
- package/generators/lib/dbschema-kit/diff-engine.js +22 -1
- package/generators/lib/dbschema-kit/diff-reporter.js +293 -272
- package/generators/lib/dbschema-kit/emitters/create-index.js +23 -1
- package/generators/lib/dbschema-kit/emitters/create-table.js +48 -0
- package/generators/lib/dbschema-kit/introspect-mapper.js +154 -2
- package/generators/lib/dbschema-kit/ir-builder.js +84 -1
- package/generators/lib/dbschema-kit/schema-printer.js +20 -0
- package/generators/lib/dbschema-kit/soft-delete-constants.js +111 -0
- package/generators/lib/dbschema-kit/validator/schema-validator.js +231 -0
- package/generators/lib/generators/processor-validation-generator.js +16 -16
- package/generators/lib/payload/payload-runner.js +711 -1
- package/generators/lib/payload/schema-diff.js +7 -0
- package/generators/lib/templates/dashboard-catalog.js +1 -1
- package/generators/lib/templates/db-connection-env.js +1 -1
- package/generators/lib/templates/dbschema-catalog.js +1 -1
- package/generators/lib/templates/field-validation-catalog.js +1 -1
- package/generators/lib/templates/mysql-template.js +1 -1
- package/generators/lib/templates/oracle-template.js +1 -1
- package/generators/lib/templates/postgres-template.js +1 -1
- package/generators/lib/templates/query-declarative-catalog.js +1 -1
- package/generators/lib/templates/sqlite-template.js +1 -1
- package/generators/lib/utils/database-introspector.js +48 -0
- package/generators/lib/utils/env-manager.js +4 -4
- package/generators/lib/utils/file-utils.js +6 -6
- package/generators/lib/utils/payload-processor.js +18 -2
- package/generators/lib/validators/argument-validator.js +2 -2
- package/generators/lib/validators/dashboard-validator.js +35 -1
- package/generators/lib/validators/payload-validator.js +460 -33
- package/integrity-manifest.json +20 -20
- package/package.json +2 -1
- package/scripts/verify-integrity.js +1 -1
- package/server.js +1 -1
- package/src/components/handlers/adjust_handler.js +1 -1
- package/src/components/handlers/audit_handler.js +1 -1
- package/src/components/handlers/delete_handler.js +1 -1
- package/src/components/handlers/export_handler.js +1 -1
- package/src/components/handlers/import_handler.js +1 -1
- package/src/components/handlers/insert_handler.js +1 -1
- package/src/components/handlers/update_handler.js +1 -1
- package/src/components/handlers/upload_handler.js +1 -1
- package/src/components/handlers/workflow_handler.js +1 -1
- package/src/components/integrations/webhook.js +1 -1
- package/src/consumers/baseConsumer.js +1 -1
- package/src/consumers/declarativeMapper.js +1 -1
- package/src/consumers/handlers/apiHandler.js +1 -1
- package/src/consumers/handlers/consoleHandler.js +1 -1
- package/src/consumers/handlers/databaseHandler.js +1 -1
- package/src/consumers/handlers/index.js +1 -1
- package/src/consumers/handlers/kafkaHandler.js +1 -1
- package/src/consumers/index.js +1 -1
- package/src/consumers/messageTransformer.js +1 -1
- package/src/consumers/validator.js +1 -1
- package/src/core/db/dialect/base-dialect.js +1 -1
- package/src/core/db/dialect/index.js +1 -1
- package/src/core/db/dialect/mysql-dialect.js +1 -1
- package/src/core/db/dialect/oracle-dialect.js +1 -1
- package/src/core/db/dialect/postgres-dialect.js +1 -1
- package/src/core/db/dialect/sqlite-dialect.js +1 -1
- package/src/core/db/flatten-helper.js +1 -1
- package/src/core/db/query-builder-error.js +1 -1
- package/src/core/db/query-builder.js +1 -1
- package/src/core/db/relation-helper.js +1 -1
- package/src/core/handlers/delete_handler.js +1 -1
- package/src/core/handlers/insert_handler.js +1 -1
- package/src/core/handlers/update_handler.js +1 -1
- package/src/core/models/base-model.js +1 -1
- package/src/core/utils/cache-manager.js +1 -1
- package/src/core/utils/component-engine.js +1 -1
- package/src/core/utils/context-builder.js +1 -1
- package/src/core/utils/datetime-formatter.js +1 -1
- package/src/core/utils/datetime-parser.js +1 -1
- package/src/core/utils/db.js +1 -1
- package/src/core/utils/logger.js +1 -1
- package/src/core/utils/payload-loader.js +1 -1
- package/src/core/utils/security-checks.js +1 -1
- package/src/middleware/body-options.js +1 -1
- package/src/middleware/cors.js +1 -1
- package/src/middleware/idempotency.js +1 -1
- package/src/middleware/rate-limiter.js +1 -1
- package/src/middleware/request-logger.js +1 -1
- package/src/middleware/security-headers.js +1 -1
- package/src/models/base-model-mysql.js +1 -1
- package/src/models/base-model-oracle.js +1 -1
- package/src/models/base-model-sqlite.js +1 -1
- package/src/models/base-model.js +1 -1
- package/src/pro/caching/redis-client.js +1 -1
- package/src/pro/caching/redis-helper.js +1 -1
- package/src/pro/consumers/baseConsumer.js +1 -1
- package/src/pro/consumers/declarativeMapper.js +1 -1
- package/src/pro/consumers/handlers/apiHandler.js +1 -1
- package/src/pro/consumers/handlers/consoleHandler.js +1 -1
- package/src/pro/consumers/handlers/databaseHandler.js +1 -1
- package/src/pro/consumers/handlers/index.js +1 -1
- package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
- package/src/pro/consumers/index.js +1 -1
- package/src/pro/consumers/messageTransformer.js +1 -1
- package/src/pro/consumers/validator.js +1 -1
- package/src/pro/database/base-model-mysql.js +1 -1
- package/src/pro/database/base-model-oracle.js +1 -1
- package/src/pro/database/base-model-sqlite.js +1 -1
- package/src/pro/database/db-mysql.js +1 -1
- package/src/pro/database/db-oracle.js +1 -1
- package/src/pro/database/db-sqlite.js +1 -1
- package/src/pro/excel/excel-generator.js +1 -1
- package/src/pro/excel/excel-parser.js +1 -1
- package/src/pro/excel/export-service.js +1 -1
- package/src/pro/excel/export_handler.js +1 -1
- package/src/pro/excel/import-service.js +1 -1
- package/src/pro/excel/import-validator.js +1 -1
- package/src/pro/excel/import_handler.js +1 -1
- package/src/pro/excel/upsert-builder.js +1 -1
- package/src/pro/idgen/idgen-routes.js +1 -1
- package/src/pro/integrations/lookup-resolver.js +1 -1
- package/src/pro/integrations/upload-handler-v2.js +1 -1
- package/src/pro/integrations/upload-handler.js +1 -1
- package/src/pro/integrations/webhook.js +1 -1
- package/src/pro/locking/lock-routes.js +1 -1
- package/src/pro/locking/resource-lock-manager.js +1 -1
- package/src/pro/messaging/kafkaConsumerService.js +1 -1
- package/src/pro/messaging/kafkaService.js +1 -1
- package/src/pro/messaging/messagehubService.js +1 -1
- package/src/pro/messaging/rabbitmqService.js +1 -1
- package/src/pro/scheduler/job-manager.js +1 -1
- package/src/pro/scheduler/job-routes.js +1 -1
- package/src/pro/scheduler/job-validator.js +1 -1
- package/src/pro/storage/base-storage-provider.js +1 -1
- package/src/pro/storage/file-metadata-helper.js +1 -1
- package/src/pro/storage/index.js +1 -1
- package/src/pro/storage/local-storage-provider.js +1 -1
- package/src/pro/storage/s3-storage-provider.js +1 -1
- package/src/pro/storage/upload-cleanup-job.js +1 -1
- package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
- package/src/pro/storage/upload-pending-tracker.js +1 -1
- package/src/pro/websocket/broadcast-helper.js +1 -1
- package/src/pro/websocket/index.js +1 -1
- package/src/pro/websocket/livesync-server.js +1 -1
- package/src/pro/websocket/ws-broadcaster.js +1 -1
- package/src/services/export-service.js +1 -1
- package/src/services/import-service.js +1 -1
- package/src/services/kafkaConsumerService.js +1 -1
- package/src/services/kafkaService.js +1 -1
- package/src/services/messagehubService.js +1 -1
- package/src/services/rabbitmqService.js +1 -1
- package/src/utils/cache-invalidation-registry.js +1 -1
- package/src/utils/cache-manager.js +1 -1
- package/src/utils/component-engine.js +1 -1
- package/src/utils/config-extractor.js +1 -1
- package/src/utils/consumerLogger.js +1 -1
- package/src/utils/context-builder.js +1 -1
- package/src/utils/dashboard-helpers.js +1 -1
- package/src/utils/dateHelper.js +1 -1
- package/src/utils/datetime-formatter.js +1 -1
- package/src/utils/datetime-parser.js +1 -1
- package/src/utils/db-bootstrap.js +1 -1
- package/src/utils/db-mysql.js +1 -1
- package/src/utils/db-oracle.js +1 -1
- package/src/utils/db-sqlite.js +1 -1
- package/src/utils/db.js +1 -1
- package/src/utils/demo-generator.js +1 -1
- package/src/utils/excel-generator.js +1 -1
- package/src/utils/excel-parser.js +1 -1
- package/src/utils/file-watcher.js +1 -1
- package/src/utils/id-generator.js +1 -1
- package/src/utils/idempotency-manager.js +1 -1
- package/src/utils/import-validator.js +1 -1
- package/src/utils/license-client.js +1 -1
- package/src/utils/lock-manager.js +1 -1
- package/src/utils/logger.js +1 -1
- package/src/utils/lookup-resolver.js +1 -1
- package/src/utils/payload-loader.js +1 -1
- package/src/utils/processor-response.js +1 -1
- package/src/utils/rabbitmq.js +1 -1
- package/src/utils/redis-client.js +1 -1
- package/src/utils/redis-helper.js +1 -1
- package/src/utils/request-scope.js +1 -1
- package/src/utils/security-checks.js +1 -1
- package/src/utils/service-resolver.js +1 -1
- package/src/utils/shutdown-coordinator.js +1 -1
- package/src/utils/soft-delete-dashboard-guard.js +1 -0
- package/src/utils/sql-table-extractor.js +1 -0
- package/src/utils/trusted-keys.js +1 -1
- package/src/utils/upload-handler.js +1 -1
- package/src/utils/upsert-builder.js +1 -1
- package/src/utils/workflow-hook-executor.js +1 -1
- package/generators/lib/utils/sql-table-extractor.js +0 -83
|
@@ -975,6 +975,54 @@ class DatabaseIntrospector {
|
|
|
975
975
|
}
|
|
976
976
|
}
|
|
977
977
|
|
|
978
|
+
/**
|
|
979
|
+
* Get CHECK constraints for a table (PostgreSQL, Fase 1).
|
|
980
|
+
*
|
|
981
|
+
* Mengembalikan `{ name, clause }` per CHECK constraint, di mana `clause` adalah
|
|
982
|
+
* definisi lengkap dari `pg_get_constraintdef` (mis.
|
|
983
|
+
* `CHECK ((is_deleted = ...) OR (...))`). Cukup untuk:
|
|
984
|
+
* - mengenali CHECK konsistensi soft-delete lewat NAMA deterministik
|
|
985
|
+
* (`chk_<table>_soft_delete_consistency`, R8) di introspect-mapper (R6), dan
|
|
986
|
+
* - parse CHECK generik single-field (`age >= 0`, dll.) di introspect-mapper.
|
|
987
|
+
*
|
|
988
|
+
* Hanya PostgreSQL yang diimplementasikan: Fase 1 soft-delete adalah PostgreSQL-only,
|
|
989
|
+
* dan dialect lain (mysql/oracle) memakai penanda boolean `col IN ('true','false')`
|
|
990
|
+
* yang sudah ditangani jalur `getBooleanColumns` terpisah. Memasukkan penanda boolean
|
|
991
|
+
* itu ke `checks` justru akan menimbulkan false drift di diff dbschema-kit (sisi SDF
|
|
992
|
+
* tidak menyimpan penanda boolean di `checks`), maka non-PostgreSQL mengembalikan [].
|
|
993
|
+
*
|
|
994
|
+
* Degradasi anggun: bila katalog tidak tersedia, error di-catch dan dikembalikan []
|
|
995
|
+
* (mengikuti pola `getBooleanColumns`).
|
|
996
|
+
*
|
|
997
|
+
* @param {string} tableName - Nama table (boleh schema.tablename atau tablename)
|
|
998
|
+
* @returns {Promise<Array<{name:string, clause:string}>>}
|
|
999
|
+
*/
|
|
1000
|
+
async getCheckConstraints(tableName) {
|
|
1001
|
+
if (!this.pool) return [];
|
|
1002
|
+
|
|
1003
|
+
// Fase 1: introspeksi CHECK hanya untuk PostgreSQL (lihat doc di atas).
|
|
1004
|
+
if (this.dbType !== 'postgresql') return [];
|
|
1005
|
+
|
|
1006
|
+
try {
|
|
1007
|
+
const { schema, table } = this.parseTableName(tableName);
|
|
1008
|
+
const rows = await this.query(
|
|
1009
|
+
`SELECT con.conname AS name,
|
|
1010
|
+
pg_get_constraintdef(con.oid) AS clause
|
|
1011
|
+
FROM pg_constraint con
|
|
1012
|
+
JOIN pg_class rel ON rel.oid = con.conrelid
|
|
1013
|
+
JOIN pg_namespace ns ON ns.oid = rel.relnamespace
|
|
1014
|
+
WHERE con.contype = 'c'
|
|
1015
|
+
AND ns.nspname = $1 AND rel.relname = $2
|
|
1016
|
+
ORDER BY con.conname`,
|
|
1017
|
+
[schema, table]
|
|
1018
|
+
);
|
|
1019
|
+
return rows.map(r => ({ name: r.name, clause: r.clause }));
|
|
1020
|
+
} catch (error) {
|
|
1021
|
+
console.warn(` Warning: Could not fetch check constraints: ${error.message}`);
|
|
1022
|
+
return [];
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
978
1026
|
/**
|
|
979
1027
|
* Deteksi kolom boolean pada dialect varchar (mysql/oracle/sqlite) lewat
|
|
980
1028
|
* penanda CHECK `<col> IN ('true','false')` yang di-emit dbschema-kit.
|
|
@@ -133,7 +133,7 @@ function updateEnvKey(filePath, key, value, createIfNotExist = true) {
|
|
|
133
133
|
if (fs.existsSync(filePath)) {
|
|
134
134
|
envData = readEnvFile(filePath);
|
|
135
135
|
} else if (!createIfNotExist) {
|
|
136
|
-
throw new Error(`File ${filePath}
|
|
136
|
+
throw new Error(`File ${filePath} not found`);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// Update data
|
|
@@ -161,7 +161,7 @@ function updateEnvKey(filePath, key, value, createIfNotExist = true) {
|
|
|
161
161
|
*/
|
|
162
162
|
function removeEnvKey(filePath, key, softDelete = true) {
|
|
163
163
|
if (!fs.existsSync(filePath)) {
|
|
164
|
-
throw new Error(`File ${filePath}
|
|
164
|
+
throw new Error(`File ${filePath} not found`);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
const envData = readEnvFile(filePath);
|
|
@@ -252,7 +252,7 @@ function validateEnvFile(filePath) {
|
|
|
252
252
|
if (!fs.existsSync(filePath)) {
|
|
253
253
|
return {
|
|
254
254
|
valid: false,
|
|
255
|
-
error: `File ${filePath}
|
|
255
|
+
error: `File ${filePath} not found`
|
|
256
256
|
};
|
|
257
257
|
}
|
|
258
258
|
|
|
@@ -294,7 +294,7 @@ function validateEnvFile(filePath) {
|
|
|
294
294
|
*/
|
|
295
295
|
function backupEnvFile(filePath) {
|
|
296
296
|
if (!fs.existsSync(filePath)) {
|
|
297
|
-
throw new Error(`File ${filePath}
|
|
297
|
+
throw new Error(`File ${filePath} not found`);
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
@@ -286,7 +286,7 @@ class FileUtils {
|
|
|
286
286
|
*/
|
|
287
287
|
static copyFileWithValidation(fileInfo) {
|
|
288
288
|
if (!fs.existsSync(fileInfo.source)) {
|
|
289
|
-
throw new Error(`Source file
|
|
289
|
+
throw new Error(`Source file not found: ${fileInfo.source}`);
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
console.log(`Copying SQL file: ${fileInfo.originalPath} -> ${path.basename(fileInfo.dest)}`);
|
|
@@ -297,14 +297,14 @@ class FileUtils {
|
|
|
297
297
|
|
|
298
298
|
// Verifikasi copy result
|
|
299
299
|
if (!fs.existsSync(fileInfo.dest)) {
|
|
300
|
-
throw new Error('Destination file
|
|
300
|
+
throw new Error('Destination file was not created');
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
const sourceStats = fs.statSync(fileInfo.source);
|
|
304
304
|
const destStats = fs.statSync(fileInfo.dest);
|
|
305
305
|
|
|
306
306
|
if (destStats.size === 0) {
|
|
307
|
-
throw new Error('Destination file
|
|
307
|
+
throw new Error('Destination file is empty after copy');
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
if (sourceStats.size !== destStats.size) {
|
|
@@ -329,12 +329,12 @@ class FileUtils {
|
|
|
329
329
|
static readFileWithErrorHandling(filePath, encoding = 'utf8') {
|
|
330
330
|
try {
|
|
331
331
|
if (!fs.existsSync(filePath)) {
|
|
332
|
-
throw new Error(`File
|
|
332
|
+
throw new Error(`File not found: ${filePath}`);
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
const stats = fs.statSync(filePath);
|
|
336
336
|
if (stats.size === 0) {
|
|
337
|
-
console.warn(`Warning: File
|
|
337
|
+
console.warn(`Warning: File is empty: ${filePath}`);
|
|
338
338
|
return '';
|
|
339
339
|
}
|
|
340
340
|
|
|
@@ -374,7 +374,7 @@ class FileUtils {
|
|
|
374
374
|
|
|
375
375
|
// Verifikasi
|
|
376
376
|
if (!fs.existsSync(filePath)) {
|
|
377
|
-
throw new Error('File
|
|
377
|
+
throw new Error('File was not created after the write operation');
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
const stats = fs.statSync(filePath);
|
|
@@ -675,6 +675,14 @@ class PayloadProcessor {
|
|
|
675
675
|
// Note: 'add' dan 'list' sudah di-convert ke 'create' dan 'read' oleh applyActionAliases()
|
|
676
676
|
const validActions = ['datatables', 'create', 'update', 'delete', 'first', 'lookup', 'read', 'export', 'import', 'adjust', 'aggregate', 'createComposite', 'updateComposite', 'readComposite', 'workflow'];
|
|
677
677
|
|
|
678
|
+
// Opt-in actions (soft-delete R20): dikenali generator TAPI TIDAK diberi default-false.
|
|
679
|
+
// Hanya disertakan di output normalized bila dideklarasikan eksplisit di payload. Ini
|
|
680
|
+
// menjaga gate diff-zero: objek action untuk payload non-restore tetap byte-identik
|
|
681
|
+
// baseline (tidak ada key `restore` tambahan yang bisa bocor ke output ter-generate,
|
|
682
|
+
// mis. /info). Bila restore=true, key dipertahankan sehingga route `/restore` +
|
|
683
|
+
// `restoreData` ter-generate (validator R17 memaksa softDelete.enabled bila restore=true).
|
|
684
|
+
const optInActions = ['restore'];
|
|
685
|
+
|
|
678
686
|
// Action aliases yang dikenali di payload tapi tidak perlu di-normalize lagi
|
|
679
687
|
// (sudah di-handle oleh applyActionAliases sebelum sampai di sini)
|
|
680
688
|
const knownAliases = Object.keys(this.ACTION_ALIASES);
|
|
@@ -690,9 +698,17 @@ class PayloadProcessor {
|
|
|
690
698
|
}
|
|
691
699
|
}
|
|
692
700
|
|
|
693
|
-
//
|
|
701
|
+
// Process opt-in actions: TANPA default-false (lihat catatan optInActions di atas).
|
|
702
|
+
// Hanya muncul di normalized bila key hadir di payload, menjaga diff-zero non-restore.
|
|
703
|
+
for (const action of optInActions) {
|
|
704
|
+
if (action in actions) {
|
|
705
|
+
normalized[action] = Boolean(actions[action]);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Warn about unknown actions (skip known aliases + opt-in actions yang dikenali)
|
|
694
710
|
for (const action of Object.keys(actions)) {
|
|
695
|
-
if (!validActions.includes(action) && !knownAliases.includes(action)) {
|
|
711
|
+
if (!validActions.includes(action) && !optInActions.includes(action) && !knownAliases.includes(action)) {
|
|
696
712
|
console.warn(`Unknown action '${action}' will be ignored`);
|
|
697
713
|
}
|
|
698
714
|
}
|
|
@@ -519,12 +519,12 @@ class ArgumentValidator {
|
|
|
519
519
|
|
|
520
520
|
// Warning untuk force overwrite
|
|
521
521
|
if (args.force) {
|
|
522
|
-
summary.warnings.push('
|
|
522
|
+
summary.warnings.push('Force overwrite mode is active - existing files will be overwritten without confirmation');
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
// Warning untuk Oracle database
|
|
526
526
|
if (args.database === 'oracle') {
|
|
527
|
-
summary.warnings.push('
|
|
527
|
+
summary.warnings.push('Using Oracle database - make sure utils/oracle.js is available');
|
|
528
528
|
}
|
|
529
529
|
|
|
530
530
|
return summary;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const PayloadProcessor = require('../utils/payload-processor');
|
|
4
|
-
const sqlTableExtractor = require('
|
|
4
|
+
const sqlTableExtractor = require('../../../src/utils/sql-table-extractor');
|
|
5
|
+
const softDeleteDashboardGuard = require('../../../src/utils/soft-delete-dashboard-guard');
|
|
5
6
|
|
|
6
7
|
const FORBIDDEN_FRONTEND_FIELDS = ['widgetType', 'layout', 'title', 'subtitle', 'color'];
|
|
7
8
|
const ALLOWED_PARAM_TYPES = ['string', 'number', 'boolean', 'date'];
|
|
@@ -40,6 +41,8 @@ class DashboardValidator {
|
|
|
40
41
|
|
|
41
42
|
this._validateWidgets(payload.widgets, declaredParams, payloadDir);
|
|
42
43
|
|
|
44
|
+
this._validateSoftDeleteGuard(payload, payloadDir);
|
|
45
|
+
|
|
43
46
|
this._validateCacheBlock(payload.cache);
|
|
44
47
|
|
|
45
48
|
if (
|
|
@@ -167,6 +170,37 @@ class DashboardValidator {
|
|
|
167
170
|
}
|
|
168
171
|
}
|
|
169
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Checkpoint soft-delete (ERROR) untuk `dashboard create`.
|
|
175
|
+
*
|
|
176
|
+
* Bangun registry tabel soft-delete dari scan payloadDir, lalu cek tiap widget.
|
|
177
|
+
* Bila ada widget yang mereferensikan tabel soft-delete tanpa token `is_deleted`,
|
|
178
|
+
* lempar Error yang memblok `dashboard create`. Registry kosong (project tanpa
|
|
179
|
+
* tabel soft-delete) → no-op, sehingga perilaku project lama tidak berubah.
|
|
180
|
+
*
|
|
181
|
+
* Resolusi SQL di-delegasikan ke `_resolveSqlText` (sudah memverifikasi file:
|
|
182
|
+
* reference pada `_validateWidgets`, jadi di titik ini file dijamin ada).
|
|
183
|
+
*/
|
|
184
|
+
static _validateSoftDeleteGuard(payload, payloadDir) {
|
|
185
|
+
const registry = softDeleteDashboardGuard.buildSoftDeleteRegistry(payloadDir);
|
|
186
|
+
if (registry.size === 0) return;
|
|
187
|
+
|
|
188
|
+
const violations = softDeleteDashboardGuard.checkDashboardViolations(
|
|
189
|
+
payload,
|
|
190
|
+
(sqlOrRef, widgetId, key) => this._resolveSqlText(
|
|
191
|
+
widgetId,
|
|
192
|
+
key === null ? 'query' : `queries.${key}`,
|
|
193
|
+
sqlOrRef,
|
|
194
|
+
payloadDir
|
|
195
|
+
),
|
|
196
|
+
registry
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
if (violations.length > 0) {
|
|
200
|
+
throw new Error(softDeleteDashboardGuard.formatViolationMessage(violations[0]));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
170
204
|
static _resolveSqlText(widgetId, queryLabel, sqlOrRef, payloadDir) {
|
|
171
205
|
if (!PayloadProcessor.isFileReference(sqlOrRef)) {
|
|
172
206
|
return sqlOrRef;
|