dirigent-core 0.23.3__tar.gz → 0.24.0__tar.gz
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.
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/PKG-INFO +4 -4
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/pyproject.toml +4 -4
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/pyproject.toml.orig +4 -4
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/alembic/versions/0001_baseline_schema.py +118 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/blockdocs.py +43 -2
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/config.py +30 -2
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/documents.py +33 -2
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/__init__.py +2 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/definition.py +176 -6
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/executor.py +14 -1
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/expansion.py +32 -3
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/references.py +12 -3
- dirigent_core-0.24.0/src/dirigent_core/engine/replies.py +194 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/runs.py +117 -4
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/services.py +8 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/state.py +154 -7
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/examples.py +31 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/messages.py +215 -13
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/models.py +88 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/pipelines.py +13 -1
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/plugins.py +33 -2
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/ratelimit.py +9 -4
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/reporting.py +1 -1
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/retention.py +15 -2
- dirigent_core-0.24.0/src/dirigent_core/sources/__init__.py +1 -0
- dirigent_core-0.24.0/src/dirigent_core/sources/endpoint.py +281 -0
- dirigent_core-0.24.0/src/dirigent_core/sources/entries.py +79 -0
- dirigent_core-0.24.0/src/dirigent_core/sources/intake.py +416 -0
- dirigent_core-0.24.0/src/dirigent_core/sources/kinds.py +22 -0
- dirigent_core-0.24.0/src/dirigent_core/sources/rows.py +407 -0
- dirigent_core-0.24.0/src/dirigent_core/sources/rules.py +310 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/telemetry.py +10 -2
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/triggers/watches.py +2 -2
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/wakeups.py +7 -2
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/LICENSE +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/README.md +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/__init__.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/alembic/env.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/alembic/script.py.mako +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/alerting.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/artifacts.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/auth.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/configdocs.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/database.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/directory.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/documentschema.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/claim.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/context.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/failure.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/engine/recovery.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/errors.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/ids.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/logging.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/migrations.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/protocol.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/py.typed +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/registry.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/scheduler.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/schemas.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/secrets.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/storage.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/trigger_documents.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/triggers/__init__.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/triggers/backfill.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/triggers/materialize.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/triggers/schedules.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/triggers/webhooks.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/types.py +0 -0
- {dirigent_core-0.23.3 → dirigent_core-0.24.0}/src/dirigent_core/worker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dirigent-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24.0
|
|
4
4
|
Summary: Dirigent engine core: schema, configuration, and plugin host.
|
|
5
5
|
License-Expression: LicenseRef-Proprietary
|
|
6
6
|
License-File: LICENSE
|
|
@@ -12,9 +12,9 @@ Requires-Dist: argon2-cffi>=25.1.0
|
|
|
12
12
|
Requires-Dist: asyncpg>=0.31.0
|
|
13
13
|
Requires-Dist: cronsim>=2.7
|
|
14
14
|
Requires-Dist: cryptography>=46.0.5
|
|
15
|
-
Requires-Dist: dirigent-client==0.
|
|
16
|
-
Requires-Dist: dirigent-common==0.
|
|
17
|
-
Requires-Dist: dirigent-plugin==0.
|
|
15
|
+
Requires-Dist: dirigent-client==0.24.0
|
|
16
|
+
Requires-Dist: dirigent-common==0.24.0
|
|
17
|
+
Requires-Dist: dirigent-plugin==0.24.0
|
|
18
18
|
Requires-Dist: jsonschema>=4.26.0
|
|
19
19
|
Requires-Dist: opentelemetry-api>=1.44.0
|
|
20
20
|
Requires-Dist: pydantic-settings>=2.15.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-core"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.24.0"
|
|
4
4
|
description = "Dirigent engine core: schema, configuration, and plugin host."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -17,9 +17,9 @@ dependencies = [
|
|
|
17
17
|
"asyncpg>=0.31.0",
|
|
18
18
|
"cronsim>=2.7",
|
|
19
19
|
"cryptography>=46.0.5",
|
|
20
|
-
"dirigent-client==0.
|
|
21
|
-
"dirigent-common==0.
|
|
22
|
-
"dirigent-plugin==0.
|
|
20
|
+
"dirigent-client==0.24.0",
|
|
21
|
+
"dirigent-common==0.24.0",
|
|
22
|
+
"dirigent-plugin==0.24.0",
|
|
23
23
|
"jsonschema>=4.26.0",
|
|
24
24
|
"opentelemetry-api>=1.44.0",
|
|
25
25
|
"pydantic-settings>=2.15.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-core"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.24.0"
|
|
4
4
|
description = "Dirigent engine core: schema, configuration, and plugin host."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -17,9 +17,9 @@ dependencies = [
|
|
|
17
17
|
"asyncpg>=0.31.0",
|
|
18
18
|
"cronsim>=2.7",
|
|
19
19
|
"cryptography>=46.0.5",
|
|
20
|
-
"dirigent-client==0.
|
|
21
|
-
"dirigent-common==0.
|
|
22
|
-
"dirigent-plugin==0.
|
|
20
|
+
"dirigent-client==0.24.0",
|
|
21
|
+
"dirigent-common==0.24.0",
|
|
22
|
+
"dirigent-plugin==0.24.0",
|
|
23
23
|
"jsonschema>=4.26.0",
|
|
24
24
|
"opentelemetry-api>=1.44.0",
|
|
25
25
|
"pydantic-settings>=2.15.0",
|
|
@@ -545,6 +545,71 @@ def upgrade() -> None:
|
|
|
545
545
|
batch_op.create_index(batch_op.f("ix_watches_pipeline_id"), ["pipeline_id"], unique=False)
|
|
546
546
|
batch_op.create_index(batch_op.f("ix_watches_trigger_document_id"), ["trigger_document_id"], unique=False)
|
|
547
547
|
|
|
548
|
+
op.create_table(
|
|
549
|
+
"sources",
|
|
550
|
+
sa.Column("id", sa.Uuid(), nullable=False),
|
|
551
|
+
sa.Column("pipeline_id", sa.Uuid(), nullable=False),
|
|
552
|
+
sa.Column("code", sa.String(length=200), nullable=False),
|
|
553
|
+
sa.Column("name", sa.String(length=200), nullable=True),
|
|
554
|
+
sa.Column("description", sa.Text(), nullable=True),
|
|
555
|
+
sa.Column("kind", sa.String(length=200), nullable=False),
|
|
556
|
+
sa.Column("config", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
|
|
557
|
+
sa.Column("token_hash", sa.String(length=128), nullable=True),
|
|
558
|
+
sa.Column("token_prefix", sa.String(length=16), nullable=True),
|
|
559
|
+
sa.Column("paused", sa.Boolean(), server_default=sa.false(), nullable=False),
|
|
560
|
+
sa.Column("accepted", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
|
561
|
+
sa.Column("refused", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
|
562
|
+
sa.Column("last_input_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
|
|
563
|
+
sa.Column(
|
|
564
|
+
"created_at",
|
|
565
|
+
dirigent_core.types.UtcDateTime(timezone=True),
|
|
566
|
+
server_default=sa.func.now(),
|
|
567
|
+
nullable=False,
|
|
568
|
+
),
|
|
569
|
+
sa.Column(
|
|
570
|
+
"updated_at",
|
|
571
|
+
dirigent_core.types.UtcDateTime(timezone=True),
|
|
572
|
+
server_default=sa.func.now(),
|
|
573
|
+
nullable=False,
|
|
574
|
+
),
|
|
575
|
+
sa.ForeignKeyConstraint(
|
|
576
|
+
["pipeline_id"], ["pipelines.id"], name=op.f("fk_sources_pipeline_id_pipelines"), ondelete="CASCADE"
|
|
577
|
+
),
|
|
578
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_sources")),
|
|
579
|
+
sa.UniqueConstraint("pipeline_id", "code", name=op.f("uq_sources_pipeline_id_code")),
|
|
580
|
+
sa.UniqueConstraint("token_hash", name=op.f("uq_sources_token_hash")),
|
|
581
|
+
)
|
|
582
|
+
with op.batch_alter_table("sources", schema=None) as batch_op:
|
|
583
|
+
batch_op.create_index(batch_op.f("ix_sources_pipeline_id"), ["pipeline_id"], unique=False)
|
|
584
|
+
|
|
585
|
+
op.create_table(
|
|
586
|
+
"source_routes",
|
|
587
|
+
sa.Column("id", sa.Uuid(), nullable=False),
|
|
588
|
+
sa.Column("source_id", sa.Uuid(), nullable=False),
|
|
589
|
+
sa.Column("method", sa.String(length=16), nullable=False),
|
|
590
|
+
sa.Column("template", sa.String(length=1000), nullable=False),
|
|
591
|
+
sa.Column("normalised", sa.String(length=1000), nullable=False),
|
|
592
|
+
sa.Column(
|
|
593
|
+
"created_at",
|
|
594
|
+
dirigent_core.types.UtcDateTime(timezone=True),
|
|
595
|
+
server_default=sa.func.now(),
|
|
596
|
+
nullable=False,
|
|
597
|
+
),
|
|
598
|
+
sa.Column(
|
|
599
|
+
"updated_at",
|
|
600
|
+
dirigent_core.types.UtcDateTime(timezone=True),
|
|
601
|
+
server_default=sa.func.now(),
|
|
602
|
+
nullable=False,
|
|
603
|
+
),
|
|
604
|
+
sa.ForeignKeyConstraint(
|
|
605
|
+
["source_id"], ["sources.id"], name=op.f("fk_source_routes_source_id_sources"), ondelete="CASCADE"
|
|
606
|
+
),
|
|
607
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_source_routes")),
|
|
608
|
+
sa.UniqueConstraint("method", "normalised", name=op.f("uq_source_routes_method_normalised")),
|
|
609
|
+
)
|
|
610
|
+
with op.batch_alter_table("source_routes", schema=None) as batch_op:
|
|
611
|
+
batch_op.create_index(batch_op.f("ix_source_routes_source_id"), ["source_id"], unique=False)
|
|
612
|
+
|
|
548
613
|
op.create_table(
|
|
549
614
|
"runs",
|
|
550
615
|
sa.Column("id", sa.Uuid(), nullable=False),
|
|
@@ -591,6 +656,7 @@ def upgrade() -> None:
|
|
|
591
656
|
),
|
|
592
657
|
sa.Column("triggered_by_id", sa.Uuid(), nullable=True),
|
|
593
658
|
sa.Column("triggered_by_label", sa.String(length=200), nullable=True),
|
|
659
|
+
sa.Column("source_step", sa.String(length=200), nullable=True),
|
|
594
660
|
sa.Column("traceparent", sa.String(length=64), nullable=True),
|
|
595
661
|
sa.Column("error", sa.Text(), nullable=True),
|
|
596
662
|
sa.Column("error_code", sa.String(length=128), nullable=True),
|
|
@@ -639,6 +705,7 @@ def upgrade() -> None:
|
|
|
639
705
|
"ix_runs_pipeline_id_status_created_at", ["pipeline_id", "status", "created_at"], unique=False
|
|
640
706
|
)
|
|
641
707
|
batch_op.create_index(batch_op.f("ix_runs_status"), ["status"], unique=False)
|
|
708
|
+
batch_op.create_index("ix_runs_triggered_by_id_status", ["triggered_by_id", "status"], unique=False)
|
|
642
709
|
|
|
643
710
|
op.create_table(
|
|
644
711
|
"notifications",
|
|
@@ -838,6 +905,43 @@ def upgrade() -> None:
|
|
|
838
905
|
batch_op.create_index("ix_webhook_deliveries_created_at", ["created_at"], unique=False)
|
|
839
906
|
batch_op.create_index("ix_webhook_deliveries_webhook_id_id", ["webhook_id", "id"], unique=False)
|
|
840
907
|
|
|
908
|
+
op.create_table(
|
|
909
|
+
"source_inputs",
|
|
910
|
+
sa.Column("id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), autoincrement=True, nullable=False),
|
|
911
|
+
sa.Column("source_id", sa.Uuid(), nullable=False),
|
|
912
|
+
sa.Column("run_id", sa.Uuid(), nullable=True),
|
|
913
|
+
sa.Column(
|
|
914
|
+
"outcome",
|
|
915
|
+
sa.Enum("accepted", "refused", name="input_outcome", native_enum=False, length=32),
|
|
916
|
+
nullable=False,
|
|
917
|
+
),
|
|
918
|
+
sa.Column("status", sa.Integer(), nullable=False),
|
|
919
|
+
sa.Column("code", sa.String(length=128), nullable=True),
|
|
920
|
+
sa.Column("reason", sa.Text(), nullable=True),
|
|
921
|
+
sa.Column("params", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True),
|
|
922
|
+
sa.Column("method", sa.String(length=16), nullable=False),
|
|
923
|
+
sa.Column("path", sa.String(length=2000), nullable=False),
|
|
924
|
+
sa.Column("body_bytes", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
|
925
|
+
sa.Column("duration_ms", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
|
926
|
+
sa.Column("caller", sa.String(length=64), nullable=True),
|
|
927
|
+
sa.Column(
|
|
928
|
+
"created_at",
|
|
929
|
+
dirigent_core.types.UtcDateTime(timezone=True),
|
|
930
|
+
server_default=sa.func.now(),
|
|
931
|
+
nullable=False,
|
|
932
|
+
),
|
|
933
|
+
sa.ForeignKeyConstraint(
|
|
934
|
+
["run_id"], ["runs.id"], name=op.f("fk_source_inputs_run_id_runs"), ondelete="SET NULL"
|
|
935
|
+
),
|
|
936
|
+
sa.ForeignKeyConstraint(
|
|
937
|
+
["source_id"], ["sources.id"], name=op.f("fk_source_inputs_source_id_sources"), ondelete="CASCADE"
|
|
938
|
+
),
|
|
939
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_source_inputs")),
|
|
940
|
+
)
|
|
941
|
+
with op.batch_alter_table("source_inputs", schema=None) as batch_op:
|
|
942
|
+
batch_op.create_index("ix_source_inputs_created_at", ["created_at"], unique=False)
|
|
943
|
+
batch_op.create_index("ix_source_inputs_source_id_id", ["source_id", "id"], unique=False)
|
|
944
|
+
|
|
841
945
|
op.create_table(
|
|
842
946
|
"step_attempts",
|
|
843
947
|
sa.Column("id", sa.Uuid(), nullable=False),
|
|
@@ -1034,6 +1138,11 @@ def downgrade() -> None:
|
|
|
1034
1138
|
batch_op.drop_index("ix_webhook_deliveries_created_at")
|
|
1035
1139
|
|
|
1036
1140
|
op.drop_table("webhook_deliveries")
|
|
1141
|
+
with op.batch_alter_table("source_inputs", schema=None) as batch_op:
|
|
1142
|
+
batch_op.drop_index("ix_source_inputs_source_id_id")
|
|
1143
|
+
batch_op.drop_index("ix_source_inputs_created_at")
|
|
1144
|
+
|
|
1145
|
+
op.drop_table("source_inputs")
|
|
1037
1146
|
with op.batch_alter_table("schedule_firings", schema=None) as batch_op:
|
|
1038
1147
|
batch_op.drop_index("ix_schedule_firings_schedule_id_id")
|
|
1039
1148
|
batch_op.drop_index("ix_schedule_firings_created_at")
|
|
@@ -1052,6 +1161,7 @@ def downgrade() -> None:
|
|
|
1052
1161
|
|
|
1053
1162
|
op.drop_table("notifications")
|
|
1054
1163
|
with op.batch_alter_table("runs", schema=None) as batch_op:
|
|
1164
|
+
batch_op.drop_index("ix_runs_triggered_by_id_status")
|
|
1055
1165
|
batch_op.drop_index(batch_op.f("ix_runs_status"))
|
|
1056
1166
|
batch_op.drop_index("ix_runs_pipeline_id_status_created_at")
|
|
1057
1167
|
batch_op.drop_index("ix_runs_finished_at")
|
|
@@ -1062,6 +1172,14 @@ def downgrade() -> None:
|
|
|
1062
1172
|
batch_op.drop_index(batch_op.f("ix_watches_pipeline_id"))
|
|
1063
1173
|
|
|
1064
1174
|
op.drop_table("watches")
|
|
1175
|
+
with op.batch_alter_table("source_routes", schema=None) as batch_op:
|
|
1176
|
+
batch_op.drop_index(batch_op.f("ix_source_routes_source_id"))
|
|
1177
|
+
|
|
1178
|
+
op.drop_table("source_routes")
|
|
1179
|
+
with op.batch_alter_table("sources", schema=None) as batch_op:
|
|
1180
|
+
batch_op.drop_index(batch_op.f("ix_sources_pipeline_id"))
|
|
1181
|
+
|
|
1182
|
+
op.drop_table("sources")
|
|
1065
1183
|
with op.batch_alter_table("webhook_triggers", schema=None) as batch_op:
|
|
1066
1184
|
batch_op.drop_index(batch_op.f("ix_webhook_triggers_trigger_document_id"))
|
|
1067
1185
|
batch_op.drop_index(batch_op.f("ix_webhook_triggers_pipeline_id"))
|
|
@@ -7,7 +7,7 @@ catalog disagree.
|
|
|
7
7
|
import json
|
|
8
8
|
from typing import Final, cast
|
|
9
9
|
|
|
10
|
-
from dirigent_client.schemas import BlockEntry, BlockKind, Catalog, SurfaceEntry
|
|
10
|
+
from dirigent_client.schemas import BlockEntry, BlockKind, Catalog, SourceEntry, SurfaceEntry
|
|
11
11
|
from dirigent_common import JsonMap
|
|
12
12
|
from dirigent_core.configdocs import first_paragraph, one_line
|
|
13
13
|
|
|
@@ -53,7 +53,8 @@ SURFACES_PREAMBLE: Final = """## Other surfaces
|
|
|
53
53
|
Blocks are two of the six surfaces a plugin contributes to. Three of the other four -- storage
|
|
54
54
|
backends, notifiers and connection kinds -- are listed here by id, since a document references
|
|
55
55
|
them by code and never by id of any other kind. The sixth, a JSON Schema format checker, is in
|
|
56
|
-
[JSON Schema](json-schema.md).
|
|
56
|
+
[JSON Schema](json-schema.md). Source kinds, which a source node names as ``source.kind``, are
|
|
57
|
+
listed after them; see [Source and reply](concepts.md#source-and-reply).
|
|
57
58
|
"""
|
|
58
59
|
|
|
59
60
|
NO_DESCRIPTION: Final = "--"
|
|
@@ -73,6 +74,7 @@ def render(catalog: Catalog) -> str:
|
|
|
73
74
|
parts.append(_surfaces("Storage schemes", "scheme", catalog.storage_schemes))
|
|
74
75
|
parts.append(_surfaces("Notifiers", "id", catalog.notifiers))
|
|
75
76
|
parts.append(_surfaces("Connection kinds", "id", catalog.connection_kinds))
|
|
77
|
+
parts.append(_sources(catalog.sources))
|
|
76
78
|
return "\n".join(parts).rstrip() + "\n"
|
|
77
79
|
|
|
78
80
|
|
|
@@ -210,6 +212,45 @@ def _surfaces(title: str, label: str, entries: list[SurfaceEntry]) -> str:
|
|
|
210
212
|
)
|
|
211
213
|
|
|
212
214
|
|
|
215
|
+
def _sources(entries: list[SourceEntry]) -> str:
|
|
216
|
+
"""Render the source kinds as a short table, and the input each one hands on."""
|
|
217
|
+
if not entries:
|
|
218
|
+
return "### Source kinds\n\n_None installed._\n"
|
|
219
|
+
rows = [
|
|
220
|
+
f"| `{entry.kind}` | {entry.hosting} | {_yes(entry.replies)} | {entry.summary} | `{entry.plugin}` |"
|
|
221
|
+
for entry in entries
|
|
222
|
+
]
|
|
223
|
+
lines = [
|
|
224
|
+
"### Source kinds",
|
|
225
|
+
"",
|
|
226
|
+
"| Kind | Hosting | Replies | Summary | Contributed by |",
|
|
227
|
+
"| --- | --- | --- | --- | --- |",
|
|
228
|
+
*rows,
|
|
229
|
+
"",
|
|
230
|
+
]
|
|
231
|
+
for entry in entries:
|
|
232
|
+
lines.extend(
|
|
233
|
+
[
|
|
234
|
+
f"#### `{entry.kind}`",
|
|
235
|
+
"",
|
|
236
|
+
"**Config**",
|
|
237
|
+
"",
|
|
238
|
+
_schema_table(entry.config_schema),
|
|
239
|
+
"",
|
|
240
|
+
"**Input it hands on**",
|
|
241
|
+
"",
|
|
242
|
+
_schema_table(entry.output_schema),
|
|
243
|
+
"",
|
|
244
|
+
]
|
|
245
|
+
)
|
|
246
|
+
return "\n".join(lines)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _yes(flag: bool) -> str:
|
|
250
|
+
"""Render a flag as a table cell reads it."""
|
|
251
|
+
return "yes" if flag else "no"
|
|
252
|
+
|
|
253
|
+
|
|
213
254
|
def _anchor(block_id: str) -> str:
|
|
214
255
|
"""Render the anchor mkdocs gives a heading whose text is a backticked block id."""
|
|
215
256
|
return block_id.replace(".", "")
|
|
@@ -290,6 +290,9 @@ class Settings(BaseSettings):
|
|
|
290
290
|
retention_deliveries: Duration | None = None
|
|
291
291
|
"""How long a webhook delivery is kept."""
|
|
292
292
|
|
|
293
|
+
retention_inputs: Duration | None = None
|
|
294
|
+
"""How long the record of an input that reached a source is kept."""
|
|
295
|
+
|
|
293
296
|
retention_firings: Duration | None = None
|
|
294
297
|
"""How long a schedule firing is kept."""
|
|
295
298
|
|
|
@@ -355,6 +358,31 @@ class Settings(BaseSettings):
|
|
|
355
358
|
|
|
356
359
|
A real webhook's own ``rate_limit_per_minute`` applies on top of this."""
|
|
357
360
|
|
|
361
|
+
endpoint_prefix: str = "/endpoints"
|
|
362
|
+
"""Where endpoint sources are served, outside the versioned API.
|
|
363
|
+
|
|
364
|
+
A proxy in front of an instance publishes this prefix and ``/hooks`` and keeps
|
|
365
|
+
``/api/v1`` inside, so a source's route is public without the API being so."""
|
|
366
|
+
|
|
367
|
+
source_max_body: Size = Field(default=1_048_576, ge=1)
|
|
368
|
+
"""The largest body any endpoint reads, whatever its own ``max_body`` says; reading stops one byte past it."""
|
|
369
|
+
|
|
370
|
+
source_intake_rate_per_minute: int = Field(default=600, ge=1)
|
|
371
|
+
"""How fast calls to one method and path are taken, before any source is looked up.
|
|
372
|
+
|
|
373
|
+
A source's own ``rate`` applies on top of this."""
|
|
374
|
+
|
|
375
|
+
source_reply_max: Duration = Field(default=timedelta(seconds=60), gt=timedelta(0))
|
|
376
|
+
"""The longest a caller is held for a reply, whatever a reply node's ``timeout`` says."""
|
|
377
|
+
|
|
378
|
+
source_max_waiting: int = Field(default=256, ge=1)
|
|
379
|
+
"""How many callers one server process holds open for their replies at once.
|
|
380
|
+
|
|
381
|
+
A call past it is answered 503 before its run is created."""
|
|
382
|
+
|
|
383
|
+
source_reply_poll: Duration = Field(default=timedelta(milliseconds=50), gt=timedelta(0))
|
|
384
|
+
"""How often one server process reads the replies its held callers wait for, in one query."""
|
|
385
|
+
|
|
358
386
|
login_rate_per_minute: int = Field(default=10, ge=1)
|
|
359
387
|
"""How many login attempts one address, and one username, get a minute.
|
|
360
388
|
|
|
@@ -383,10 +411,10 @@ class Settings(BaseSettings):
|
|
|
383
411
|
"""Accept a comma-separated ``key=value`` string for a mapping setting."""
|
|
384
412
|
return parse_string_mapping(value)
|
|
385
413
|
|
|
386
|
-
@field_validator("api_prefix")
|
|
414
|
+
@field_validator("api_prefix", "endpoint_prefix")
|
|
387
415
|
@classmethod
|
|
388
416
|
def _normalise_prefix(cls, value: str) -> str:
|
|
389
|
-
"""Keep
|
|
417
|
+
"""Keep a prefix rooted and free of a trailing slash."""
|
|
390
418
|
prefix = "/" + value.strip("/")
|
|
391
419
|
return prefix.rstrip("/") or "/"
|
|
392
420
|
|
|
@@ -45,8 +45,10 @@ from dirigent_core.messages import (
|
|
|
45
45
|
NOT_A_MAPPING,
|
|
46
46
|
NOT_YAML,
|
|
47
47
|
PARAMS_SCHEMA_INVALID,
|
|
48
|
+
REFERENCE_ERROR_NOT_UPSTREAM,
|
|
48
49
|
REFERENCE_GRID_IN_CONFIG,
|
|
49
50
|
REFERENCE_MALFORMED_ARTIFACTS,
|
|
51
|
+
REFERENCE_MALFORMED_ERROR,
|
|
50
52
|
REFERENCE_MALFORMED_ITEM,
|
|
51
53
|
REFERENCE_MALFORMED_RUN,
|
|
52
54
|
REFERENCE_MALFORMED_STEP,
|
|
@@ -366,6 +368,7 @@ def validate_against_catalog(
|
|
|
366
368
|
blocks: Mapping[str, Any] | None = None,
|
|
367
369
|
format_checker: FormatChecker | None = None,
|
|
368
370
|
target: TriggerTarget | None = None,
|
|
371
|
+
source_kinds: Mapping[str, Any] | None = None,
|
|
369
372
|
) -> list[ValidationIssue]:
|
|
370
373
|
"""Check a document against what this instance actually has, reporting everything at once.
|
|
371
374
|
|
|
@@ -379,8 +382,13 @@ def validate_against_catalog(
|
|
|
379
382
|
against; unset, they are checked against the base formats alone, which is all a caller
|
|
380
383
|
holding no host can know. ``target`` is the pipeline a triggers document names, as the
|
|
381
384
|
instance holds it now; on the offline path there is none to read, and only the clocks and
|
|
382
|
-
the codes are checked.
|
|
385
|
+
the codes are checked. ``source_kinds`` is the source kinds the instance knows; unset, the
|
|
386
|
+
built-in ones.
|
|
383
387
|
"""
|
|
388
|
+
from dirigent_core.sources.kinds import built_in_kinds
|
|
389
|
+
from dirigent_core.sources.rules import source_issues
|
|
390
|
+
|
|
391
|
+
kinds = source_kinds if source_kinds is not None else built_in_kinds()
|
|
384
392
|
checker = format_checker if format_checker is not None else base_format_checker()
|
|
385
393
|
sensors = _sensors(catalog) if check_blocks else None
|
|
386
394
|
if isinstance(definition, TriggersDefinition):
|
|
@@ -406,6 +414,7 @@ def validate_against_catalog(
|
|
|
406
414
|
issues.extend(_carried_schema_issues(definition))
|
|
407
415
|
issues.extend(_reference_issues(definition))
|
|
408
416
|
issues.extend(_trigger_issues(definition, checker, check_params=not malformed_params, sensors=sensors))
|
|
417
|
+
issues.extend(source_issues(definition, kinds))
|
|
409
418
|
if check_blocks:
|
|
410
419
|
issues.extend(
|
|
411
420
|
_step_block_issues(
|
|
@@ -607,6 +616,10 @@ def _triggers_document_issues(
|
|
|
607
616
|
return [ValidationIssue.of(TARGET_PIPELINE_MISSING, location="pipeline", code=repr(definition.pipeline))]
|
|
608
617
|
if not target.active:
|
|
609
618
|
return [ValidationIssue.of(TARGET_PIPELINE_INACTIVE, location="pipeline", code=repr(definition.pipeline))]
|
|
619
|
+
if target.definition is not None and target.definition.sources:
|
|
620
|
+
from dirigent_core.sources.rules import triggers_issues
|
|
621
|
+
|
|
622
|
+
return triggers_issues(target.code, definition.triggers)
|
|
610
623
|
return _trigger_issues(definition, format_checker, against=target.definition, sensors=sensors)
|
|
611
624
|
|
|
612
625
|
|
|
@@ -672,6 +685,8 @@ def _step_block_issues(
|
|
|
672
685
|
issues: list[ValidationIssue] = []
|
|
673
686
|
for name in sorted(definition.steps):
|
|
674
687
|
step = definition.steps[name]
|
|
688
|
+
if step.block is None:
|
|
689
|
+
continue
|
|
675
690
|
entry = catalog.block(step.block)
|
|
676
691
|
if entry is None:
|
|
677
692
|
issues.append(
|
|
@@ -851,6 +866,12 @@ def _reference_issues(definition: PipelineDefinition) -> list[ValidationIssue]:
|
|
|
851
866
|
problem = _reference_problem(reference.strip(), definition, name, upstream, declared, family)
|
|
852
867
|
if problem is not None:
|
|
853
868
|
issues.append(_at(f"steps.{name}.config", problem))
|
|
869
|
+
if step.reply is not None:
|
|
870
|
+
written = cast("JsonValue", step.reply.model_dump(mode="json", exclude={"timeout"}))
|
|
871
|
+
for reference in sorted(set(references_in(written))):
|
|
872
|
+
problem = _reference_problem(reference.strip(), definition, name, upstream, declared, family)
|
|
873
|
+
if problem is not None:
|
|
874
|
+
issues.append(_at(f"steps.{name}.reply", problem))
|
|
854
875
|
issues.extend(_fan_out_literal_issues(name, step.for_each))
|
|
855
876
|
issues.extend(_adoption_issues(definition, name))
|
|
856
877
|
for reference in sorted(set(references_in(cast("JsonValue", step.for_each)))):
|
|
@@ -952,6 +973,14 @@ def _reference_problem(
|
|
|
952
973
|
if target not in upstream:
|
|
953
974
|
return Issue.of(REFERENCE_NOT_UPSTREAM, reference=reference, target=repr(target), step=repr(step))
|
|
954
975
|
return None
|
|
976
|
+
case ["steps", target, "error", *path]:
|
|
977
|
+
if target not in definition.steps:
|
|
978
|
+
return Issue.of(REFERENCE_UNKNOWN_STEP, reference=reference, step=repr(target))
|
|
979
|
+
if target not in upstream:
|
|
980
|
+
return Issue.of(REFERENCE_ERROR_NOT_UPSTREAM, reference=reference, target=repr(target), step=repr(step))
|
|
981
|
+
if path[:1] not in (["code"], ["message"], ["params"]) or (path[:1] != ["params"] and len(path) > 1):
|
|
982
|
+
return Issue.of(REFERENCE_MALFORMED_ERROR, reference=reference)
|
|
983
|
+
return None
|
|
955
984
|
case ["steps", target, "items"]:
|
|
956
985
|
return Issue.of(REFERENCE_GRID_IN_CONFIG, reference=reference, target=repr(target), bare_target=target)
|
|
957
986
|
case ["steps", target, "item", "output", *_]:
|
|
@@ -1032,7 +1061,9 @@ def summarize(definition: Document) -> DocumentSummary:
|
|
|
1032
1061
|
pipeline = definition.pipeline if isinstance(definition, TriggersDefinition) else None
|
|
1033
1062
|
steps = sorted(definition.steps) if isinstance(definition, PipelineDefinition) else []
|
|
1034
1063
|
blocks = (
|
|
1035
|
-
sorted({step.block for step in definition.steps.values()
|
|
1064
|
+
sorted({step.block for step in definition.steps.values() if step.block is not None})
|
|
1065
|
+
if isinstance(definition, PipelineDefinition)
|
|
1066
|
+
else []
|
|
1036
1067
|
)
|
|
1037
1068
|
return DocumentSummary(
|
|
1038
1069
|
kind=definition.kind,
|
|
@@ -36,6 +36,7 @@ from dirigent_core.engine.references import ReferenceScope, UnknownReference, re
|
|
|
36
36
|
from dirigent_core.engine.runs import (
|
|
37
37
|
Attribution,
|
|
38
38
|
EngineRuns,
|
|
39
|
+
Entry,
|
|
39
40
|
FanOutError,
|
|
40
41
|
Provenance,
|
|
41
42
|
RunCreationError,
|
|
@@ -62,6 +63,7 @@ __all__ = [
|
|
|
62
63
|
"KIND_PIPELINE",
|
|
63
64
|
"KIND_TRIGGERS",
|
|
64
65
|
"Attribution",
|
|
66
|
+
"Entry",
|
|
65
67
|
"BufferedLogger",
|
|
66
68
|
"ClaimedUnit",
|
|
67
69
|
"ConcurrencyPolicy",
|