nl2data-semantic-catalog-postgres 0.1.0__py3-none-any.whl
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.
- nl2data_semantic_catalog_postgres/__init__.py +18 -0
- nl2data_semantic_catalog_postgres/client.py +139 -0
- nl2data_semantic_catalog_postgres/config.py +136 -0
- nl2data_semantic_catalog_postgres/envelope.py +327 -0
- nl2data_semantic_catalog_postgres/errors.py +188 -0
- nl2data_semantic_catalog_postgres/fake_postgres/__init__.py +53 -0
- nl2data_semantic_catalog_postgres/fake_postgres/driver.py +209 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_audit.py +243 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_drafts.py +84 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_maintenance.py +263 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_publications.py +249 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_schema.py +23 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_snapshots.py +197 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_versions.py +330 -0
- nl2data_semantic_catalog_postgres/fake_postgres/keys.py +99 -0
- nl2data_semantic_catalog_postgres/fake_postgres/pool.py +152 -0
- nl2data_semantic_catalog_postgres/fake_postgres/registry.py +144 -0
- nl2data_semantic_catalog_postgres/maintenance.py +235 -0
- nl2data_semantic_catalog_postgres/py.typed +0 -0
- nl2data_semantic_catalog_postgres/repositories/__init__.py +26 -0
- nl2data_semantic_catalog_postgres/repositories/activation.py +849 -0
- nl2data_semantic_catalog_postgres/repositories/audit_evidence.py +221 -0
- nl2data_semantic_catalog_postgres/repositories/drafts.py +156 -0
- nl2data_semantic_catalog_postgres/repositories/evidence.py +415 -0
- nl2data_semantic_catalog_postgres/repositories/publications.py +486 -0
- nl2data_semantic_catalog_postgres/repositories/snapshots.py +372 -0
- nl2data_semantic_catalog_postgres/schema.py +337 -0
- nl2data_semantic_catalog_postgres/sql.py +520 -0
- nl2data_semantic_catalog_postgres/store.py +716 -0
- nl2data_semantic_catalog_postgres/unit_of_work.py +589 -0
- nl2data_semantic_catalog_postgres-0.1.0.dist-info/METADATA +76 -0
- nl2data_semantic_catalog_postgres-0.1.0.dist-info/RECORD +34 -0
- nl2data_semantic_catalog_postgres-0.1.0.dist-info/WHEEL +5 -0
- nl2data_semantic_catalog_postgres-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
"""Bundle publication repository over the shared catalog unit of work.
|
|
2
|
+
|
|
3
|
+
The repository performs the atomic publication writes for one Bundle -
|
|
4
|
+
publication row, accepted manifest, verification evidence, publish audit,
|
|
5
|
+
published version, and supersession bookkeeping - inside a transaction
|
|
6
|
+
owned by the store facade. It never opens or commits a transaction for
|
|
7
|
+
these writes and never receives mutable ``AssemblyDraft`` objects: the
|
|
8
|
+
publication-time draft facts cross the boundary only as the immutable
|
|
9
|
+
:class:`PublicationDraftBinding` contract.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from nl2data_core.assembly.audit_evidence import publication_audit_entry
|
|
17
|
+
from nl2data_core.bundles.catalog import (
|
|
18
|
+
BundleCatalogOutcome,
|
|
19
|
+
_expected_snapshot_fingerprint,
|
|
20
|
+
_failure,
|
|
21
|
+
_failure_from_activation_check,
|
|
22
|
+
_failure_from_validation,
|
|
23
|
+
_success,
|
|
24
|
+
)
|
|
25
|
+
from nl2data_core.bundles.models import BUNDLE_SCHEMA_VERSION, SemanticModelBundle
|
|
26
|
+
from nl2data_core.bundles.publication import (
|
|
27
|
+
PublishedVersionState,
|
|
28
|
+
PublishIdempotencyStatus,
|
|
29
|
+
)
|
|
30
|
+
from nl2data_core.bundles.validation import validate_bundle
|
|
31
|
+
from nl2data_core.canonical import strict_sha256_fingerprint
|
|
32
|
+
from nl2data_core.control_plane.publication.contracts import (
|
|
33
|
+
PublicationDraftBinding,
|
|
34
|
+
PublicationRecordSet,
|
|
35
|
+
verification_evidence_reference,
|
|
36
|
+
)
|
|
37
|
+
from nl2data_core.metadata.policy import ProductionActivationContext
|
|
38
|
+
|
|
39
|
+
from ..envelope import ENVELOPE_SCHEMA_VERSION, ArtifactKind
|
|
40
|
+
from ..errors import SemanticCatalogError
|
|
41
|
+
from ..unit_of_work import CatalogUnitOfWork, _namespace
|
|
42
|
+
from .audit_evidence import AuditEvidenceRepository
|
|
43
|
+
from .evidence import EvidenceRepository
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class PublicationRepository:
|
|
47
|
+
"""Atomic Bundle publication and immutable publication reads."""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
uow: CatalogUnitOfWork,
|
|
52
|
+
evidence: EvidenceRepository,
|
|
53
|
+
audit: AuditEvidenceRepository,
|
|
54
|
+
) -> None:
|
|
55
|
+
self._uow = uow
|
|
56
|
+
self._evidence = evidence
|
|
57
|
+
self._audit = audit
|
|
58
|
+
|
|
59
|
+
def publish(
|
|
60
|
+
self,
|
|
61
|
+
conn: Any,
|
|
62
|
+
bundle: SemanticModelBundle,
|
|
63
|
+
*,
|
|
64
|
+
namespace: str,
|
|
65
|
+
now: Any,
|
|
66
|
+
records: PublicationRecordSet,
|
|
67
|
+
production: ProductionActivationContext | None = None,
|
|
68
|
+
publication_binding: PublicationDraftBinding | None = None,
|
|
69
|
+
idempotency_key: str | None = None,
|
|
70
|
+
) -> BundleCatalogOutcome:
|
|
71
|
+
"""Publish one Bundle and its validated lifecycle records atomically.
|
|
72
|
+
|
|
73
|
+
The connection is provided by the transaction owner; every write in
|
|
74
|
+
this method rolls back together when any statement fails. The
|
|
75
|
+
record set arrives already validated by the centralized integrity
|
|
76
|
+
validator (``build_publication_records`` at the facade boundary);
|
|
77
|
+
repositories never accept per-record compatibility arguments.
|
|
78
|
+
"""
|
|
79
|
+
result = validate_bundle(
|
|
80
|
+
bundle,
|
|
81
|
+
supported_schema_versions=(BUNDLE_SCHEMA_VERSION,),
|
|
82
|
+
expected_snapshot_fingerprint=_expected_snapshot_fingerprint(production),
|
|
83
|
+
)
|
|
84
|
+
if not result.valid:
|
|
85
|
+
return _failure_from_validation(result)
|
|
86
|
+
if production is not None:
|
|
87
|
+
check = production.check()
|
|
88
|
+
if not check.allowed:
|
|
89
|
+
return _failure_from_activation_check(check)
|
|
90
|
+
accepted_assertion_manifest = records.accepted_assertion_manifest
|
|
91
|
+
audit = records.audit
|
|
92
|
+
verification_evidence = records.verification_evidence
|
|
93
|
+
frozen_release_binding = records.frozen_release_binding
|
|
94
|
+
evidence_reference = (
|
|
95
|
+
verification_evidence_reference(verification_evidence.fingerprint)
|
|
96
|
+
if verification_evidence is not None
|
|
97
|
+
else None
|
|
98
|
+
)
|
|
99
|
+
bundle_payload = bundle.file_payload()
|
|
100
|
+
envelope = self._uow.encode(
|
|
101
|
+
ArtifactKind.BUNDLE,
|
|
102
|
+
bundle_payload,
|
|
103
|
+
strict_sha256_fingerprint(bundle_payload),
|
|
104
|
+
)
|
|
105
|
+
manifest_envelope = None
|
|
106
|
+
if accepted_assertion_manifest is not None:
|
|
107
|
+
manifest_payload = accepted_assertion_manifest.canonical_payload()
|
|
108
|
+
manifest_envelope = self._uow.encode(
|
|
109
|
+
ArtifactKind.ACCEPTED_ASSERTION_MANIFEST,
|
|
110
|
+
manifest_payload,
|
|
111
|
+
strict_sha256_fingerprint(manifest_payload),
|
|
112
|
+
)
|
|
113
|
+
audit_envelope = None
|
|
114
|
+
if audit is not None:
|
|
115
|
+
audit_payload = audit.safe_payload()
|
|
116
|
+
audit_envelope = self._uow.encode(
|
|
117
|
+
ArtifactKind.PUBLISH_AUDIT,
|
|
118
|
+
audit_payload,
|
|
119
|
+
strict_sha256_fingerprint(audit_payload),
|
|
120
|
+
)
|
|
121
|
+
evidence_envelope = None
|
|
122
|
+
if verification_evidence is not None and frozen_release_binding is not None:
|
|
123
|
+
evidence_payload = self._uow.verification_evidence_payload(
|
|
124
|
+
verification_evidence,
|
|
125
|
+
frozen_release_binding,
|
|
126
|
+
)
|
|
127
|
+
evidence_envelope = self._uow.encode(
|
|
128
|
+
ArtifactKind.VERIFICATION_SUITE_EVIDENCE,
|
|
129
|
+
evidence_payload,
|
|
130
|
+
strict_sha256_fingerprint(evidence_payload),
|
|
131
|
+
)
|
|
132
|
+
if publication_binding is not None:
|
|
133
|
+
persisted = self._uow.execute(
|
|
134
|
+
conn,
|
|
135
|
+
"lock_assembly_draft",
|
|
136
|
+
(namespace, publication_binding.draft_id),
|
|
137
|
+
).fetchone()
|
|
138
|
+
if persisted is None:
|
|
139
|
+
return _failure(
|
|
140
|
+
"conflict",
|
|
141
|
+
"draft_not_found",
|
|
142
|
+
"assembly draft is not persisted in this tenant scope",
|
|
143
|
+
)
|
|
144
|
+
if int(persisted["draft_revision"]) != publication_binding.draft_revision:
|
|
145
|
+
return _failure(
|
|
146
|
+
"conflict",
|
|
147
|
+
"draft_revision_conflict",
|
|
148
|
+
"assembly draft revision changed before publication",
|
|
149
|
+
)
|
|
150
|
+
persisted_envelope = self._uow.decode(
|
|
151
|
+
persisted["envelope"],
|
|
152
|
+
ArtifactKind.ASSEMBLY_DRAFT,
|
|
153
|
+
row_schema_version=persisted["schema_version"],
|
|
154
|
+
)
|
|
155
|
+
persisted_draft = self._uow.draft_from_envelope(persisted_envelope)
|
|
156
|
+
if strict_sha256_fingerprint(persisted_draft.file_payload()) != (
|
|
157
|
+
publication_binding.draft_payload_fingerprint
|
|
158
|
+
):
|
|
159
|
+
return _failure(
|
|
160
|
+
"conflict",
|
|
161
|
+
"draft_changed",
|
|
162
|
+
"assembly draft content changed before publication",
|
|
163
|
+
)
|
|
164
|
+
if verification_evidence is not None and (
|
|
165
|
+
verification_evidence.draft_id != publication_binding.draft_id
|
|
166
|
+
or verification_evidence.draft_revision
|
|
167
|
+
!= publication_binding.draft_revision
|
|
168
|
+
or verification_evidence.plan_fingerprint
|
|
169
|
+
!= publication_binding.approved_plan_fingerprint
|
|
170
|
+
or verification_evidence.tenant_scope_fingerprint
|
|
171
|
+
!= publication_binding.tenant_scope_fingerprint
|
|
172
|
+
or verification_evidence.source_scope_fingerprint
|
|
173
|
+
!= publication_binding.source_scope_fingerprint
|
|
174
|
+
):
|
|
175
|
+
return _failure(
|
|
176
|
+
"rejected",
|
|
177
|
+
"verification_evidence_mismatch",
|
|
178
|
+
"verification evidence does not match the locked draft scope",
|
|
179
|
+
)
|
|
180
|
+
self._uow.execute(
|
|
181
|
+
conn,
|
|
182
|
+
"lock_publication_series",
|
|
183
|
+
(namespace, bundle.bundle_id),
|
|
184
|
+
)
|
|
185
|
+
if idempotency_key is not None:
|
|
186
|
+
idempotent = self._uow.execute(
|
|
187
|
+
conn,
|
|
188
|
+
"read_publish_by_idempotency_key",
|
|
189
|
+
(namespace, idempotency_key),
|
|
190
|
+
).fetchone()
|
|
191
|
+
if idempotent is not None and (
|
|
192
|
+
idempotent["bundle_id"] != bundle.bundle_id
|
|
193
|
+
or idempotent["bundle_fingerprint"] != bundle.fingerprint
|
|
194
|
+
):
|
|
195
|
+
return _failure(
|
|
196
|
+
"conflict",
|
|
197
|
+
"idempotency_key_reused",
|
|
198
|
+
"idempotency key is already bound to other semantic content",
|
|
199
|
+
)
|
|
200
|
+
existing = self._uow.execute(
|
|
201
|
+
conn,
|
|
202
|
+
"read_publication_by_fingerprint",
|
|
203
|
+
(namespace, bundle.bundle_id, bundle.fingerprint),
|
|
204
|
+
).fetchone()
|
|
205
|
+
if existing is not None:
|
|
206
|
+
existing_envelope = self._uow.decode(
|
|
207
|
+
existing["envelope"],
|
|
208
|
+
ArtifactKind.BUNDLE,
|
|
209
|
+
row_schema_version=existing["schema_version"],
|
|
210
|
+
)
|
|
211
|
+
existing_bundle = self._uow.bundle_from_envelope(existing_envelope)
|
|
212
|
+
version_record = self._uow.execute(
|
|
213
|
+
conn,
|
|
214
|
+
"read_published_version",
|
|
215
|
+
(namespace, bundle.bundle_id, bundle.fingerprint),
|
|
216
|
+
).fetchone()
|
|
217
|
+
if version_record is None:
|
|
218
|
+
# Reuse without a durable version record would report success
|
|
219
|
+
# for a publication that no longer exists.
|
|
220
|
+
return _failure(
|
|
221
|
+
"conflict",
|
|
222
|
+
"publication_version_missing",
|
|
223
|
+
"published version record is missing for this publication",
|
|
224
|
+
)
|
|
225
|
+
try:
|
|
226
|
+
# Reuse re-validates the persisted records through the
|
|
227
|
+
# centralized integrity rule set, so a tampered or
|
|
228
|
+
# truncated record is never returned as reused.
|
|
229
|
+
persisted = self._evidence.validated_publication_records(
|
|
230
|
+
conn,
|
|
231
|
+
namespace,
|
|
232
|
+
bundle.bundle_id,
|
|
233
|
+
bundle.fingerprint,
|
|
234
|
+
audit_id=version_record["audit_id"],
|
|
235
|
+
)
|
|
236
|
+
except SemanticCatalogError as error:
|
|
237
|
+
if (error.details or {}).get("cause_type") == "MissingPublishAudit":
|
|
238
|
+
return _failure(
|
|
239
|
+
"conflict",
|
|
240
|
+
"verification_audit_missing",
|
|
241
|
+
"persisted publish audit referenced by the version "
|
|
242
|
+
"record is missing",
|
|
243
|
+
)
|
|
244
|
+
return _failure(
|
|
245
|
+
"conflict",
|
|
246
|
+
"verification_evidence_mismatch",
|
|
247
|
+
"persisted verification evidence no longer validates",
|
|
248
|
+
)
|
|
249
|
+
existing_audit = persisted.audit
|
|
250
|
+
if (
|
|
251
|
+
verification_evidence is not None
|
|
252
|
+
and persisted.verification_evidence is None
|
|
253
|
+
):
|
|
254
|
+
# An evidence-free record must never silently absorb a
|
|
255
|
+
# verified publication request.
|
|
256
|
+
return _failure(
|
|
257
|
+
"conflict",
|
|
258
|
+
"publication_state_conflict",
|
|
259
|
+
"existing publication has no verification evidence to reuse",
|
|
260
|
+
)
|
|
261
|
+
return _success(
|
|
262
|
+
"reused",
|
|
263
|
+
existing_bundle,
|
|
264
|
+
audit_reference=(
|
|
265
|
+
existing_audit.audit_id if existing_audit is not None else None
|
|
266
|
+
),
|
|
267
|
+
verification_evidence_reference=(
|
|
268
|
+
existing_audit.verification.evidence_reference
|
|
269
|
+
if existing_audit is not None
|
|
270
|
+
else None
|
|
271
|
+
),
|
|
272
|
+
superseded_fingerprint=version_record["predecessor_fingerprint"],
|
|
273
|
+
idempotency_status=PublishIdempotencyStatus.REUSED,
|
|
274
|
+
)
|
|
275
|
+
version_match = self._uow.execute(
|
|
276
|
+
conn,
|
|
277
|
+
"read_publication_fingerprint",
|
|
278
|
+
(namespace, bundle.bundle_id, bundle.model_version),
|
|
279
|
+
).fetchone()
|
|
280
|
+
if version_match is not None:
|
|
281
|
+
return _failure(
|
|
282
|
+
"conflict",
|
|
283
|
+
"version_exists",
|
|
284
|
+
f"bundle '{bundle.bundle_id}' version "
|
|
285
|
+
f"'{bundle.model_version}' is already published",
|
|
286
|
+
)
|
|
287
|
+
predecessor = self._uow.execute(
|
|
288
|
+
conn,
|
|
289
|
+
"read_latest_version",
|
|
290
|
+
(namespace, bundle.bundle_id),
|
|
291
|
+
).fetchone()
|
|
292
|
+
predecessor_fingerprint = (
|
|
293
|
+
predecessor["bundle_fingerprint"] if predecessor is not None else None
|
|
294
|
+
)
|
|
295
|
+
self._uow.execute(
|
|
296
|
+
conn,
|
|
297
|
+
"insert_publication",
|
|
298
|
+
(
|
|
299
|
+
namespace,
|
|
300
|
+
bundle.bundle_id,
|
|
301
|
+
bundle.model_version,
|
|
302
|
+
bundle.fingerprint,
|
|
303
|
+
ENVELOPE_SCHEMA_VERSION,
|
|
304
|
+
envelope,
|
|
305
|
+
now,
|
|
306
|
+
),
|
|
307
|
+
)
|
|
308
|
+
if manifest_envelope is not None:
|
|
309
|
+
self._uow.execute(
|
|
310
|
+
conn,
|
|
311
|
+
"insert_accepted_manifest",
|
|
312
|
+
(
|
|
313
|
+
namespace,
|
|
314
|
+
bundle.bundle_id,
|
|
315
|
+
bundle.fingerprint,
|
|
316
|
+
ENVELOPE_SCHEMA_VERSION,
|
|
317
|
+
manifest_envelope,
|
|
318
|
+
now,
|
|
319
|
+
),
|
|
320
|
+
)
|
|
321
|
+
if evidence_envelope is not None and verification_evidence is not None:
|
|
322
|
+
self._uow.execute(
|
|
323
|
+
conn,
|
|
324
|
+
"insert_verification_evidence",
|
|
325
|
+
(
|
|
326
|
+
namespace,
|
|
327
|
+
bundle.bundle_id,
|
|
328
|
+
bundle.fingerprint,
|
|
329
|
+
verification_evidence.fingerprint,
|
|
330
|
+
ENVELOPE_SCHEMA_VERSION,
|
|
331
|
+
evidence_envelope,
|
|
332
|
+
now,
|
|
333
|
+
),
|
|
334
|
+
)
|
|
335
|
+
if audit_envelope is not None and audit is not None:
|
|
336
|
+
self._uow.execute(
|
|
337
|
+
conn,
|
|
338
|
+
"insert_publish_audit",
|
|
339
|
+
(
|
|
340
|
+
namespace,
|
|
341
|
+
bundle.bundle_id,
|
|
342
|
+
bundle.fingerprint,
|
|
343
|
+
audit.audit_id,
|
|
344
|
+
idempotency_key,
|
|
345
|
+
ENVELOPE_SCHEMA_VERSION,
|
|
346
|
+
audit_envelope,
|
|
347
|
+
now,
|
|
348
|
+
),
|
|
349
|
+
)
|
|
350
|
+
if records.audit_evidence is not None:
|
|
351
|
+
# The release-readiness binding and its publication trail entry
|
|
352
|
+
# commit atomically with the publish audit they cross-link to.
|
|
353
|
+
self._evidence.insert_publication_audit_evidence(
|
|
354
|
+
conn,
|
|
355
|
+
namespace,
|
|
356
|
+
bundle.bundle_id,
|
|
357
|
+
bundle.fingerprint,
|
|
358
|
+
records.audit_evidence,
|
|
359
|
+
now=now,
|
|
360
|
+
)
|
|
361
|
+
predecessor_entry = (
|
|
362
|
+
self._audit.find_publication_entry(
|
|
363
|
+
conn, namespace, predecessor_fingerprint
|
|
364
|
+
)
|
|
365
|
+
if predecessor_fingerprint is not None
|
|
366
|
+
else None
|
|
367
|
+
)
|
|
368
|
+
self._audit.insert_audit_entries(
|
|
369
|
+
conn,
|
|
370
|
+
namespace,
|
|
371
|
+
[
|
|
372
|
+
publication_audit_entry(
|
|
373
|
+
records.audit_evidence,
|
|
374
|
+
predecessor_event_ids=(
|
|
375
|
+
()
|
|
376
|
+
if predecessor_entry is None
|
|
377
|
+
else (predecessor_entry.event_id,)
|
|
378
|
+
),
|
|
379
|
+
occurred_at=now,
|
|
380
|
+
)
|
|
381
|
+
],
|
|
382
|
+
)
|
|
383
|
+
self._uow.execute(
|
|
384
|
+
conn,
|
|
385
|
+
"insert_published_version",
|
|
386
|
+
(
|
|
387
|
+
namespace,
|
|
388
|
+
bundle.bundle_id,
|
|
389
|
+
bundle.fingerprint,
|
|
390
|
+
bundle.model_version,
|
|
391
|
+
PublishedVersionState.AVAILABLE.value,
|
|
392
|
+
predecessor_fingerprint,
|
|
393
|
+
None,
|
|
394
|
+
audit.audit_id if audit is not None else None,
|
|
395
|
+
now,
|
|
396
|
+
),
|
|
397
|
+
)
|
|
398
|
+
if predecessor_fingerprint is not None:
|
|
399
|
+
self._uow.execute(
|
|
400
|
+
conn,
|
|
401
|
+
"update_version_successor",
|
|
402
|
+
(
|
|
403
|
+
bundle.fingerprint,
|
|
404
|
+
namespace,
|
|
405
|
+
bundle.bundle_id,
|
|
406
|
+
predecessor_fingerprint,
|
|
407
|
+
),
|
|
408
|
+
)
|
|
409
|
+
self._uow.execute(
|
|
410
|
+
conn,
|
|
411
|
+
"insert_supersession_edge",
|
|
412
|
+
(
|
|
413
|
+
namespace,
|
|
414
|
+
bundle.bundle_id,
|
|
415
|
+
predecessor_fingerprint,
|
|
416
|
+
bundle.fingerprint,
|
|
417
|
+
now,
|
|
418
|
+
),
|
|
419
|
+
)
|
|
420
|
+
self._uow.insert_event(
|
|
421
|
+
conn,
|
|
422
|
+
"bundle_published",
|
|
423
|
+
bundle.bundle_id,
|
|
424
|
+
namespace=namespace,
|
|
425
|
+
occurred_at=now,
|
|
426
|
+
)
|
|
427
|
+
return _success(
|
|
428
|
+
"published",
|
|
429
|
+
bundle,
|
|
430
|
+
audit_reference=audit.audit_id if audit is not None else None,
|
|
431
|
+
verification_evidence_reference=evidence_reference,
|
|
432
|
+
superseded_fingerprint=predecessor_fingerprint,
|
|
433
|
+
idempotency_status=PublishIdempotencyStatus.CREATED,
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
def get(
|
|
437
|
+
self,
|
|
438
|
+
bundle_id: str,
|
|
439
|
+
version: str,
|
|
440
|
+
/,
|
|
441
|
+
*,
|
|
442
|
+
tenant_scope_fingerprint: str | None = None,
|
|
443
|
+
) -> SemanticModelBundle | None:
|
|
444
|
+
"""Load a published Bundle by id and version."""
|
|
445
|
+
namespace = _namespace(tenant_scope_fingerprint)
|
|
446
|
+
with self._uow.transaction() as conn:
|
|
447
|
+
row = self._uow.execute(
|
|
448
|
+
conn,
|
|
449
|
+
"read_publication",
|
|
450
|
+
(namespace, bundle_id, version),
|
|
451
|
+
).fetchone()
|
|
452
|
+
if row is None:
|
|
453
|
+
return None
|
|
454
|
+
envelope = self._uow.decode(
|
|
455
|
+
row["envelope"],
|
|
456
|
+
ArtifactKind.BUNDLE,
|
|
457
|
+
row_schema_version=row["schema_version"],
|
|
458
|
+
)
|
|
459
|
+
return self._uow.bundle_from_envelope(envelope)
|
|
460
|
+
|
|
461
|
+
def get_by_fingerprint(
|
|
462
|
+
self,
|
|
463
|
+
bundle_id: str,
|
|
464
|
+
fingerprint: str,
|
|
465
|
+
*,
|
|
466
|
+
tenant_scope_fingerprint: str | None = None,
|
|
467
|
+
) -> SemanticModelBundle | None:
|
|
468
|
+
"""Load an immutable Bundle by semantic fingerprint."""
|
|
469
|
+
namespace = _namespace(tenant_scope_fingerprint)
|
|
470
|
+
with self._uow.transaction() as conn:
|
|
471
|
+
row = self._uow.execute(
|
|
472
|
+
conn,
|
|
473
|
+
"read_publication_by_fingerprint",
|
|
474
|
+
(namespace, bundle_id, fingerprint),
|
|
475
|
+
).fetchone()
|
|
476
|
+
if row is None:
|
|
477
|
+
return None
|
|
478
|
+
envelope = self._uow.decode(
|
|
479
|
+
row["envelope"],
|
|
480
|
+
ArtifactKind.BUNDLE,
|
|
481
|
+
row_schema_version=row["schema_version"],
|
|
482
|
+
)
|
|
483
|
+
return self._uow.bundle_from_envelope(envelope)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
__all__ = ["PublicationRepository"]
|