@pgpm/database-jobs 0.26.1 → 0.26.3

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/Makefile CHANGED
@@ -1,5 +1,5 @@
1
1
  EXTENSION = pgpm-database-jobs
2
- DATA = sql/pgpm-database-jobs--0.26.0.sql
2
+ DATA = sql/pgpm-database-jobs--0.26.2.sql
3
3
 
4
4
  PG_CONFIG = pg_config
5
5
  PGXS := $(shell $(PG_CONFIG) --pgxs)
@@ -14,7 +14,9 @@ CREATE FUNCTION app_jobs.add_job (
14
14
  run_at timestamptz DEFAULT now(),
15
15
  max_attempts integer DEFAULT 25,
16
16
  priority integer DEFAULT 0,
17
- entity_id uuid DEFAULT NULL
17
+ entity_id uuid DEFAULT NULL,
18
+ organization_id uuid DEFAULT NULL,
19
+ entity_type text DEFAULT NULL
18
20
  )
19
21
  RETURNS app_jobs.jobs
20
22
  AS $$
@@ -33,6 +35,8 @@ BEGIN
33
35
  database_id,
34
36
  actor_id,
35
37
  entity_id,
38
+ organization_id,
39
+ entity_type,
36
40
  task_identifier,
37
41
  payload,
38
42
  queue_name,
@@ -44,6 +48,8 @@ BEGIN
44
48
  v_database_id,
45
49
  v_actor_id,
46
50
  add_job.entity_id,
51
+ add_job.organization_id,
52
+ add_job.entity_type,
47
53
  identifier,
48
54
  coalesce(payload, '{}'::json),
49
55
  queue_name,
@@ -88,6 +94,8 @@ BEGIN
88
94
  database_id,
89
95
  actor_id,
90
96
  entity_id,
97
+ organization_id,
98
+ entity_type,
91
99
  task_identifier,
92
100
  payload,
93
101
  queue_name,
@@ -98,6 +106,8 @@ BEGIN
98
106
  v_database_id,
99
107
  v_actor_id,
100
108
  add_job.entity_id,
109
+ add_job.organization_id,
110
+ add_job.entity_type,
101
111
  identifier,
102
112
  payload,
103
113
  queue_name,
@@ -0,0 +1,10 @@
1
+ -- Deploy schemas/app_jobs/procedures/grants/grant_execute_add_job_to_authenticated to pg
2
+
3
+ -- requires: schemas/app_jobs/schema
4
+ -- requires: schemas/app_jobs/procedures/add_job
5
+
6
+ BEGIN;
7
+
8
+ GRANT EXECUTE ON FUNCTION app_jobs.add_job(text, json, text, text, timestamptz, integer, integer, uuid, uuid, text) TO authenticated;
9
+
10
+ COMMIT;
@@ -2,6 +2,7 @@
2
2
  BEGIN;
3
3
  CREATE SCHEMA IF NOT EXISTS app_jobs;
4
4
  GRANT USAGE ON SCHEMA app_jobs TO administrator;
5
+ GRANT USAGE ON SCHEMA app_jobs TO authenticated;
5
6
  ALTER DEFAULT PRIVILEGES IN SCHEMA app_jobs GRANT EXECUTE ON FUNCTIONS TO administrator;
6
7
  COMMIT;
7
8
 
@@ -7,6 +7,8 @@ CREATE TABLE app_jobs.jobs (
7
7
  database_id uuid,
8
8
  actor_id uuid,
9
9
  entity_id uuid,
10
+ organization_id uuid,
11
+ entity_type text,
10
12
  queue_name text DEFAULT NULL,
11
13
  task_identifier text NOT NULL,
12
14
  payload json DEFAULT '{}' ::json NOT NULL,
@@ -32,6 +34,8 @@ COMMENT ON COLUMN app_jobs.jobs.id IS 'Auto-incrementing job identifier';
32
34
  COMMENT ON COLUMN app_jobs.jobs.database_id IS 'Database this job belongs to (nullable for system-level jobs without tenant context)';
33
35
  COMMENT ON COLUMN app_jobs.jobs.actor_id IS 'User who triggered this job, read from JWT claims at enqueue time';
34
36
  COMMENT ON COLUMN app_jobs.jobs.entity_id IS 'Entity (org/team) this job is scoped to for billing; NULL means platform-level (resolved via database_id → owner_id)';
37
+ COMMENT ON COLUMN app_jobs.jobs.organization_id IS 'Top-level organization for this entity; resolved at enqueue time via get_organization_id(entity_type, entity_id)';
38
+ COMMENT ON COLUMN app_jobs.jobs.entity_type IS 'Entity type prefix (org, team, app, etc.) for interpreting entity_id';
35
39
  COMMENT ON COLUMN app_jobs.jobs.queue_name IS 'Name of the queue this job belongs to; used for worker routing and concurrency control';
36
40
  COMMENT ON COLUMN app_jobs.jobs.task_identifier IS 'Identifier for the task type (maps to a worker handler function)';
37
41
  COMMENT ON COLUMN app_jobs.jobs.payload IS 'JSON payload of arguments passed to the task handler';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgpm/database-jobs",
3
- "version": "0.26.1",
3
+ "version": "0.26.3",
4
4
  "description": "Database-specific job handling and queue management",
5
5
  "author": "Dan Lynch <pyramation@gmail.com>",
6
6
  "contributors": [
@@ -35,5 +35,5 @@
35
35
  "bugs": {
36
36
  "url": "https://github.com/constructive-io/pgpm-modules/issues"
37
37
  },
38
- "gitHead": "e1f2e9bec1c91d0751ebe49e9193459191d3c464"
38
+ "gitHead": "fc269eb5f9521c96a0a618cdbcc99c451830d002"
39
39
  }
@@ -1,6 +1,6 @@
1
1
  # pgpm-database-jobs extension
2
2
  comment = 'pgpm-database-jobs extension'
3
- default_version = '0.26.0'
3
+ default_version = '0.26.2'
4
4
  module_pathname = '$libdir/pgpm-database-jobs'
5
5
  requires = 'plpgsql,pgcrypto,pgpm-verify,pgpm-jwt-claims'
6
6
  relocatable = false
package/pgpm.plan CHANGED
@@ -36,5 +36,6 @@ schemas/app_jobs/procedures/complete_jobs [schemas/app_jobs/schema schemas/app_j
36
36
  schemas/app_jobs/procedures/complete_job [schemas/app_jobs/schema schemas/app_jobs/tables/jobs/table schemas/app_jobs/tables/job_queues/table] 2025-08-26T23:57:41Z pgpm <pgpm@5b0c196eeb62> # add schemas/app_jobs/procedures/complete_job
37
37
  schemas/app_jobs/procedures/add_scheduled_job [schemas/app_jobs/schema schemas/app_jobs/tables/scheduled_jobs/table pgpm-jwt-claims:schemas/jwt_private/procedures/current_database_id] 2025-08-26T23:57:41Z pgpm <pgpm@5b0c196eeb62> # add schemas/app_jobs/procedures/add_scheduled_job
38
38
  schemas/app_jobs/procedures/add_job [schemas/app_jobs/schema schemas/app_jobs/tables/jobs/table schemas/app_jobs/tables/job_queues/table pgpm-jwt-claims:schemas/jwt_private/procedures/current_database_id pgpm-jwt-claims:schemas/jwt_public/procedures/current_user_id] 2025-08-26T23:57:41Z pgpm <pgpm@5b0c196eeb62> # add schemas/app_jobs/procedures/add_job
39
+ schemas/app_jobs/procedures/grants/grant_execute_add_job_to_authenticated [schemas/app_jobs/schema schemas/app_jobs/procedures/add_job] 2026-06-03T01:15:00Z pgpm <pgpm@localhost> # grant authenticated EXECUTE on add_job for INVOKER trigger support
39
40
  schemas/app_jobs/procedures/remove_job [schemas/app_jobs/schema schemas/app_jobs/tables/jobs/table] 2025-08-26T23:57:41Z pgpm <pgpm@5b0c196eeb62> # add schemas/app_jobs/procedures/remove_job
40
41
  schemas/app_jobs/procedures/force_unlock_workers [schemas/app_jobs/schema schemas/app_jobs/tables/jobs/table schemas/app_jobs/tables/job_queues/table] 2025-08-26T23:57:41Z pgpm <pgpm@5b0c196eeb62> # add schemas/app_jobs/procedures/force_unlock_workers
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/app_jobs/procedures/grants/grant_execute_add_job_to_authenticated from pg
2
+
3
+ BEGIN;
4
+
5
+ REVOKE EXECUTE ON FUNCTION app_jobs.add_job(text, json, text, text, timestamptz, integer, integer, uuid, uuid, text) FROM authenticated;
6
+
7
+ COMMIT;
@@ -3,6 +3,8 @@ CREATE SCHEMA IF NOT EXISTS app_jobs;
3
3
 
4
4
  GRANT USAGE ON SCHEMA app_jobs TO administrator;
5
5
 
6
+ GRANT USAGE ON SCHEMA app_jobs TO authenticated;
7
+
6
8
  ALTER DEFAULT PRIVILEGES IN SCHEMA app_jobs
7
9
  GRANT EXECUTE ON FUNCTIONS TO administrator;
8
10
 
@@ -53,9 +55,7 @@ $EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
53
55
 
54
56
  COMMENT ON FUNCTION app_jobs.tg_add_job_with_row IS 'Useful shortcut to create a job on insert or update. Pass the task name as the trigger argument, and the record data will automatically be available on the JSON payload.';
55
57
 
56
- CREATE FUNCTION app_jobs.json_build_object_apply(
57
- arguments text[]
58
- ) RETURNS pg_catalog.json AS $EOFCODE$
58
+ CREATE FUNCTION app_jobs.json_build_object_apply(arguments text[]) RETURNS pg_catalog.json AS $EOFCODE$
59
59
  DECLARE
60
60
  arg text;
61
61
  _sql text;
@@ -195,6 +195,8 @@ CREATE TABLE app_jobs.jobs (
195
195
  database_id uuid,
196
196
  actor_id uuid,
197
197
  entity_id uuid,
198
+ organization_id uuid,
199
+ entity_type text,
198
200
  queue_name text DEFAULT NULL,
199
201
  task_identifier text NOT NULL,
200
202
  payload pg_catalog.json DEFAULT '{}'::json NOT NULL,
@@ -226,6 +228,10 @@ COMMENT ON COLUMN app_jobs.jobs.actor_id IS 'User who triggered this job, read f
226
228
 
227
229
  COMMENT ON COLUMN app_jobs.jobs.entity_id IS 'Entity (org/team) this job is scoped to for billing; NULL means platform-level (resolved via database_id → owner_id)';
228
230
 
231
+ COMMENT ON COLUMN app_jobs.jobs.organization_id IS 'Top-level organization for this entity; resolved at enqueue time via get_organization_id(entity_type, entity_id)';
232
+
233
+ COMMENT ON COLUMN app_jobs.jobs.entity_type IS 'Entity type prefix (org, team, app, etc.) for interpreting entity_id';
234
+
229
235
  COMMENT ON COLUMN app_jobs.jobs.queue_name IS 'Name of the queue this job belongs to; used for worker routing and concurrency control';
230
236
 
231
237
  COMMENT ON COLUMN app_jobs.jobs.task_identifier IS 'Identifier for the task type (maps to a worker handler function)';
@@ -374,10 +380,7 @@ CREATE INDEX job_queues_locked_by_idx ON app_jobs.job_queues (locked_by);
374
380
 
375
381
  GRANT SELECT, INSERT, UPDATE, DELETE ON app_jobs.job_queues TO administrator;
376
382
 
377
- CREATE FUNCTION app_jobs.run_scheduled_job(
378
- id bigint,
379
- job_expiry interval DEFAULT '1 hours'
380
- ) RETURNS app_jobs.jobs AS $EOFCODE$
383
+ CREATE FUNCTION app_jobs.run_scheduled_job(id bigint, job_expiry interval DEFAULT '1 hours') RETURNS app_jobs.jobs AS $EOFCODE$
381
384
  DECLARE
382
385
  j app_jobs.jobs;
383
386
  last_id bigint;
@@ -448,13 +451,7 @@ BEGIN
448
451
  END;
449
452
  $EOFCODE$ LANGUAGE plpgsql VOLATILE;
450
453
 
451
- CREATE FUNCTION app_jobs.reschedule_jobs(
452
- job_ids bigint[],
453
- run_at timestamptz DEFAULT NULL,
454
- priority int DEFAULT NULL,
455
- attempts int DEFAULT NULL,
456
- max_attempts int DEFAULT NULL
457
- ) RETURNS SETOF app_jobs.jobs LANGUAGE sql AS $EOFCODE$
454
+ CREATE FUNCTION app_jobs.reschedule_jobs(job_ids bigint[], run_at timestamptz DEFAULT NULL, priority int DEFAULT NULL, attempts int DEFAULT NULL, max_attempts int DEFAULT NULL) RETURNS SETOF app_jobs.jobs LANGUAGE sql AS $EOFCODE$
458
455
  UPDATE
459
456
  app_jobs.jobs
460
457
  SET
@@ -470,10 +467,7 @@ CREATE FUNCTION app_jobs.reschedule_jobs(
470
467
  *;
471
468
  $EOFCODE$;
472
469
 
473
- CREATE FUNCTION app_jobs.release_scheduled_jobs(
474
- worker_id text,
475
- ids bigint[] DEFAULT NULL
476
- ) RETURNS void AS $EOFCODE$
470
+ CREATE FUNCTION app_jobs.release_scheduled_jobs(worker_id text, ids bigint[] DEFAULT NULL) RETURNS void AS $EOFCODE$
477
471
  DECLARE
478
472
  BEGIN
479
473
  -- clear the scheduled job
@@ -489,9 +483,7 @@ BEGIN
489
483
  END;
490
484
  $EOFCODE$ LANGUAGE plpgsql VOLATILE;
491
485
 
492
- CREATE FUNCTION app_jobs.release_jobs(
493
- worker_id text
494
- ) RETURNS void AS $EOFCODE$
486
+ CREATE FUNCTION app_jobs.release_jobs(worker_id text) RETURNS void AS $EOFCODE$
495
487
  DECLARE
496
488
  BEGIN
497
489
  -- clear the job
@@ -514,10 +506,7 @@ BEGIN
514
506
  END;
515
507
  $EOFCODE$ LANGUAGE plpgsql VOLATILE;
516
508
 
517
- CREATE FUNCTION app_jobs.permanently_fail_jobs(
518
- job_ids bigint[],
519
- error_message text DEFAULT NULL
520
- ) RETURNS SETOF app_jobs.jobs LANGUAGE sql AS $EOFCODE$
509
+ CREATE FUNCTION app_jobs.permanently_fail_jobs(job_ids bigint[], error_message text DEFAULT NULL) RETURNS SETOF app_jobs.jobs LANGUAGE sql AS $EOFCODE$
521
510
  UPDATE
522
511
  app_jobs.jobs
523
512
  SET
@@ -531,10 +520,7 @@ CREATE FUNCTION app_jobs.permanently_fail_jobs(
531
520
  *;
532
521
  $EOFCODE$;
533
522
 
534
- CREATE FUNCTION app_jobs.get_scheduled_job(
535
- worker_id text,
536
- task_identifiers text[] DEFAULT NULL
537
- ) RETURNS app_jobs.scheduled_jobs LANGUAGE plpgsql AS $EOFCODE$
523
+ CREATE FUNCTION app_jobs.get_scheduled_job(worker_id text, task_identifiers text[] DEFAULT NULL) RETURNS app_jobs.scheduled_jobs LANGUAGE plpgsql AS $EOFCODE$
538
524
  DECLARE
539
525
  v_job_id bigint;
540
526
  v_row app_jobs.scheduled_jobs;
@@ -586,11 +572,7 @@ BEGIN
586
572
  END;
587
573
  $EOFCODE$;
588
574
 
589
- CREATE FUNCTION app_jobs.get_job(
590
- worker_id text,
591
- task_identifiers text[] DEFAULT NULL,
592
- job_expiry interval DEFAULT '4 hours'
593
- ) RETURNS app_jobs.jobs LANGUAGE plpgsql AS $EOFCODE$
575
+ CREATE FUNCTION app_jobs.get_job(worker_id text, task_identifiers text[] DEFAULT NULL, job_expiry interval DEFAULT '4 hours') RETURNS app_jobs.jobs LANGUAGE plpgsql AS $EOFCODE$
594
576
  DECLARE
595
577
  v_job_id bigint;
596
578
  v_queue_name text;
@@ -645,11 +627,7 @@ BEGIN
645
627
  END;
646
628
  $EOFCODE$;
647
629
 
648
- CREATE FUNCTION app_jobs.fail_job(
649
- worker_id text,
650
- job_id bigint,
651
- error_message text
652
- ) RETURNS app_jobs.jobs LANGUAGE plpgsql STRICT AS $EOFCODE$
630
+ CREATE FUNCTION app_jobs.fail_job(worker_id text, job_id bigint, error_message text) RETURNS app_jobs.jobs LANGUAGE plpgsql STRICT AS $EOFCODE$
653
631
  DECLARE
654
632
  v_row app_jobs.jobs;
655
633
  BEGIN
@@ -679,9 +657,7 @@ BEGIN
679
657
  END;
680
658
  $EOFCODE$;
681
659
 
682
- CREATE FUNCTION app_jobs.complete_jobs(
683
- job_ids bigint[]
684
- ) RETURNS SETOF app_jobs.jobs LANGUAGE sql AS $EOFCODE$
660
+ CREATE FUNCTION app_jobs.complete_jobs(job_ids bigint[]) RETURNS SETOF app_jobs.jobs LANGUAGE sql AS $EOFCODE$
685
661
  DELETE FROM app_jobs.jobs
686
662
  WHERE id = ANY (job_ids)
687
663
  AND (locked_by IS NULL
@@ -690,10 +666,7 @@ CREATE FUNCTION app_jobs.complete_jobs(
690
666
  *;
691
667
  $EOFCODE$;
692
668
 
693
- CREATE FUNCTION app_jobs.complete_job(
694
- worker_id text,
695
- job_id bigint
696
- ) RETURNS app_jobs.jobs LANGUAGE plpgsql AS $EOFCODE$
669
+ CREATE FUNCTION app_jobs.complete_job(worker_id text, job_id bigint) RETURNS app_jobs.jobs LANGUAGE plpgsql AS $EOFCODE$
697
670
  DECLARE
698
671
  v_row app_jobs.jobs;
699
672
  BEGIN
@@ -715,16 +688,7 @@ BEGIN
715
688
  END;
716
689
  $EOFCODE$;
717
690
 
718
- CREATE FUNCTION app_jobs.add_scheduled_job(
719
- identifier text,
720
- payload pg_catalog.json DEFAULT '{}'::json,
721
- schedule_info pg_catalog.json DEFAULT '{}'::json,
722
- job_key text DEFAULT NULL,
723
- queue_name text DEFAULT NULL,
724
- max_attempts int DEFAULT 25,
725
- priority int DEFAULT 0,
726
- entity_id uuid DEFAULT NULL
727
- ) RETURNS app_jobs.scheduled_jobs AS $EOFCODE$
691
+ CREATE FUNCTION app_jobs.add_scheduled_job(identifier text, payload pg_catalog.json DEFAULT '{}'::json, schedule_info pg_catalog.json DEFAULT '{}'::json, job_key text DEFAULT NULL, queue_name text DEFAULT NULL, max_attempts int DEFAULT 25, priority int DEFAULT 0, entity_id uuid DEFAULT NULL) RETURNS app_jobs.scheduled_jobs AS $EOFCODE$
728
692
  DECLARE
729
693
  v_job app_jobs.scheduled_jobs;
730
694
  v_database_id uuid;
@@ -812,16 +776,7 @@ BEGIN
812
776
  END;
813
777
  $EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
814
778
 
815
- CREATE FUNCTION app_jobs.add_job(
816
- identifier text,
817
- payload pg_catalog.json DEFAULT '{}'::json,
818
- job_key text DEFAULT NULL,
819
- queue_name text DEFAULT NULL,
820
- run_at timestamptz DEFAULT now(),
821
- max_attempts int DEFAULT 25,
822
- priority int DEFAULT 0,
823
- entity_id uuid DEFAULT NULL
824
- ) RETURNS app_jobs.jobs AS $EOFCODE$
779
+ CREATE FUNCTION app_jobs.add_job(identifier text, payload pg_catalog.json DEFAULT '{}'::json, job_key text DEFAULT NULL, queue_name text DEFAULT NULL, run_at timestamptz DEFAULT now(), max_attempts int DEFAULT 25, priority int DEFAULT 0, entity_id uuid DEFAULT NULL, organization_id uuid DEFAULT NULL, entity_type text DEFAULT NULL) RETURNS app_jobs.jobs AS $EOFCODE$
825
780
  DECLARE
826
781
  v_job app_jobs.jobs;
827
782
  v_database_id uuid;
@@ -837,6 +792,8 @@ BEGIN
837
792
  database_id,
838
793
  actor_id,
839
794
  entity_id,
795
+ organization_id,
796
+ entity_type,
840
797
  task_identifier,
841
798
  payload,
842
799
  queue_name,
@@ -848,6 +805,8 @@ BEGIN
848
805
  v_database_id,
849
806
  v_actor_id,
850
807
  add_job.entity_id,
808
+ add_job.organization_id,
809
+ add_job.entity_type,
851
810
  identifier,
852
811
  coalesce(payload, '{}'::json),
853
812
  queue_name,
@@ -892,6 +851,8 @@ BEGIN
892
851
  database_id,
893
852
  actor_id,
894
853
  entity_id,
854
+ organization_id,
855
+ entity_type,
895
856
  task_identifier,
896
857
  payload,
897
858
  queue_name,
@@ -902,6 +863,8 @@ BEGIN
902
863
  v_database_id,
903
864
  v_actor_id,
904
865
  add_job.entity_id,
866
+ add_job.organization_id,
867
+ add_job.entity_type,
905
868
  identifier,
906
869
  payload,
907
870
  queue_name,
@@ -915,9 +878,9 @@ BEGIN
915
878
  END;
916
879
  $EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
917
880
 
918
- CREATE FUNCTION app_jobs.remove_job(
919
- job_key text
920
- ) RETURNS app_jobs.jobs LANGUAGE plpgsql STRICT AS $EOFCODE$
881
+ GRANT EXECUTE ON FUNCTION app_jobs.add_job(text, pg_catalog.json, text, text, timestamptz, int, int, uuid, uuid, text) TO authenticated;
882
+
883
+ CREATE FUNCTION app_jobs.remove_job(job_key text) RETURNS app_jobs.jobs LANGUAGE plpgsql STRICT AS $EOFCODE$
921
884
  DECLARE
922
885
  v_job app_jobs.jobs;
923
886
  BEGIN
@@ -942,9 +905,7 @@ BEGIN
942
905
  END;
943
906
  $EOFCODE$;
944
907
 
945
- CREATE FUNCTION app_jobs.force_unlock_workers(
946
- worker_ids text[]
947
- ) RETURNS void LANGUAGE sql VOLATILE AS $EOFCODE$
908
+ CREATE FUNCTION app_jobs.force_unlock_workers(worker_ids text[]) RETURNS void LANGUAGE sql VOLATILE AS $EOFCODE$
948
909
  UPDATE app_jobs.jobs
949
910
  SET locked_at = NULL, locked_by = NULL
950
911
  WHERE locked_by = ANY (worker_ids);
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/app_jobs/procedures/grants/grant_execute_add_job_to_authenticated on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT has_function_privilege('authenticated', 'app_jobs.add_job(text, json, text, text, timestamptz, integer, integer, uuid, uuid, text)', 'EXECUTE');
6
+
7
+ ROLLBACK;