dzql 0.5.25 → 0.5.27
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
CHANGED
|
@@ -247,6 +247,38 @@ $$ LANGUAGE plpgsql STABLE SECURITY DEFINER;`;
|
|
|
247
247
|
scopeTables: scopeTables
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
+
// Dashboard mode: empty paramSchema means aggregate root as array
|
|
251
|
+
if (params.length === 0) {
|
|
252
|
+
return `CREATE OR REPLACE FUNCTION get_${this.name}(
|
|
253
|
+
p_params JSONB,
|
|
254
|
+
p_user_id INT
|
|
255
|
+
) RETURNS JSONB AS $$
|
|
256
|
+
DECLARE
|
|
257
|
+
v_data JSONB;
|
|
258
|
+
BEGIN
|
|
259
|
+
-- Check access control
|
|
260
|
+
IF NOT ${this.name}_can_subscribe(p_user_id, p_params) THEN
|
|
261
|
+
RAISE EXCEPTION 'Permission denied';
|
|
262
|
+
END IF;
|
|
263
|
+
|
|
264
|
+
-- Build document with root as array (dashboard mode)
|
|
265
|
+
SELECT jsonb_build_object(
|
|
266
|
+
'${this.rootEntity}', COALESCE((
|
|
267
|
+
SELECT jsonb_agg(row_to_json(root.*))
|
|
268
|
+
FROM ${this.rootEntity} root
|
|
269
|
+
), '[]'::jsonb)${relationSelects}
|
|
270
|
+
)
|
|
271
|
+
INTO v_data;
|
|
272
|
+
|
|
273
|
+
-- Return data with embedded schema for atomic updates
|
|
274
|
+
RETURN jsonb_build_object(
|
|
275
|
+
'data', v_data,
|
|
276
|
+
'schema', '${schemaJson}'::jsonb
|
|
277
|
+
);
|
|
278
|
+
END;
|
|
279
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;`;
|
|
280
|
+
}
|
|
281
|
+
|
|
250
282
|
return `CREATE OR REPLACE FUNCTION get_${this.name}(
|
|
251
283
|
p_params JSONB,
|
|
252
284
|
p_user_id INT
|
|
@@ -462,6 +494,11 @@ $$ LANGUAGE plpgsql SECURITY DEFINER;`;
|
|
|
462
494
|
return `rel.${fk} = root.id`;
|
|
463
495
|
}
|
|
464
496
|
|
|
497
|
+
// Dashboard collection: filter="TRUE" means fetch ALL rows (no FK filter)
|
|
498
|
+
if (filter === 'TRUE') {
|
|
499
|
+
return 'TRUE';
|
|
500
|
+
}
|
|
501
|
+
|
|
465
502
|
// Parse filter expression like "venue_id=$venue_id"
|
|
466
503
|
// Replace $param with v_param variable
|
|
467
504
|
return filter.replace(/\$(\w+)/g, 'v_$1');
|
|
@@ -473,13 +510,19 @@ $$ LANGUAGE plpgsql SECURITY DEFINER;`;
|
|
|
473
510
|
*/
|
|
474
511
|
_generateAffectedDocumentsFunction() {
|
|
475
512
|
const cases = [];
|
|
513
|
+
const seenEntities = new Set();
|
|
476
514
|
|
|
477
515
|
// Case 1: Root entity changed
|
|
478
516
|
cases.push(this._generateRootAffectedCase());
|
|
517
|
+
seenEntities.add(this.rootEntity);
|
|
479
518
|
|
|
480
|
-
// Case 2: Related entities changed
|
|
519
|
+
// Case 2: Related entities changed (skip duplicates)
|
|
481
520
|
for (const [relName, relConfig] of Object.entries(this.relations)) {
|
|
482
|
-
|
|
521
|
+
const relEntity = typeof relConfig === 'string' ? relConfig : relConfig.entity;
|
|
522
|
+
if (!seenEntities.has(relEntity)) {
|
|
523
|
+
cases.push(this._generateRelationAffectedCase(relName, relConfig));
|
|
524
|
+
seenEntities.add(relEntity);
|
|
525
|
+
}
|
|
483
526
|
}
|
|
484
527
|
|
|
485
528
|
const casesSQL = cases.join('\n\n ');
|
|
@@ -510,7 +553,15 @@ $$ LANGUAGE plpgsql IMMUTABLE;`;
|
|
|
510
553
|
*/
|
|
511
554
|
_generateRootAffectedCase() {
|
|
512
555
|
const params = Object.keys(this.paramSchema);
|
|
513
|
-
|
|
556
|
+
|
|
557
|
+
// Dashboard mode: empty paramSchema means notify ALL subscribers
|
|
558
|
+
if (params.length === 0) {
|
|
559
|
+
return `-- Root entity (${this.rootEntity}) changed - dashboard mode, notify all
|
|
560
|
+
WHEN '${this.rootEntity}' THEN
|
|
561
|
+
v_affected := ARRAY['{}'::jsonb];`;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const firstParam = params[0];
|
|
514
565
|
|
|
515
566
|
return `-- Root entity (${this.rootEntity}) changed
|
|
516
567
|
WHEN '${this.rootEntity}' THEN
|
|
@@ -529,10 +580,19 @@ $$ LANGUAGE plpgsql IMMUTABLE;`;
|
|
|
529
580
|
? relConfig.foreignKey
|
|
530
581
|
: `${this.rootEntity}_id`;
|
|
531
582
|
const relVia = typeof relConfig === 'object' ? relConfig.via : null;
|
|
583
|
+
const relFilter = typeof relConfig === 'object' ? relConfig.filter : null;
|
|
532
584
|
|
|
533
585
|
const params = Object.keys(this.paramSchema);
|
|
534
586
|
const firstParam = params[0] || 'id';
|
|
535
587
|
|
|
588
|
+
// Dashboard collection: filter="TRUE" means notify ALL subscribers
|
|
589
|
+
// This relation is independent from the root entity
|
|
590
|
+
if (relFilter === 'TRUE') {
|
|
591
|
+
return `-- Dashboard collection (${relEntity}) - notify all subscribers
|
|
592
|
+
WHEN '${relEntity}' THEN
|
|
593
|
+
v_affected := ARRAY['{}'::jsonb];`;
|
|
594
|
+
}
|
|
595
|
+
|
|
536
596
|
// Check if this is a nested relation (has parent FK)
|
|
537
597
|
const nestedIncludes = typeof relConfig === 'object' ? relConfig.include : null;
|
|
538
598
|
|