dafab-client 2.2.3__tar.gz → 2.2.4__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.
- {dafab_client-2.2.3 → dafab_client-2.2.4}/PKG-INFO +1 -1
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/example_notebooks/dafab_dasi_workflows.ipynb +13 -10
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Preflight/validate_original_item_file.py +22 -57
- dafab_client-2.2.4/dafab_client/helpers/resources/schemas/Schema_Copernicus_with_dafab.json +780 -0
- dafab_client-2.2.4/dafab_client/helpers/resources/schemas/dasi-original-item.schema.json +691 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/PKG-INFO +1 -1
- {dafab_client-2.2.3 → dafab_client-2.2.4}/pyproject.toml +1 -1
- dafab_client-2.2.3/dafab_client/helpers/resources/schemas/Schema_Copernicus_with_dafab.json +0 -4781
- dafab_client-2.2.3/dafab_client/helpers/resources/schemas/dasi-original-item.schema.json +0 -4729
- {dafab_client-2.2.3 → dafab_client-2.2.4}/MANIFEST.in +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/__init__.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/__init__.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/__init__.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/accountclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/baseclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/client.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/credentialclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/didclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/downloadclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/pingclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/replicaclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/rseclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/scopeclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/uploadclient.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/cache.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/checksum.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/client.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/config.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/constants.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/constraints.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/didtype.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/exception.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/extra.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/filter.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/logging.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/pcache.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/plugins.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/types.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/utils.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/core/rse.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/dafab_lib.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/global_utils.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/overridden_rucio_client.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/__init__.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/protocols/__init__.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/protocols/gfal.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/protocols/protocol.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/protocols/webdav.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/rsemanager.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/translation.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/secrets/user_dafab/config +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/secrets/user_dafab/rucio_ca_bundle.pem +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/version.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/example_notebooks/dafab_operator_workflows.ipynb +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/example_notebooks/dafab_simple_user_workflows.ipynb +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/example_notebooks/dafab_skim_workflows.ipynb +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/System/check_available_accounts.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/System/check_storage.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/__init__.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/check_server_health.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/container_management/ensure_container.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/container_management/list_containers.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/dataset_management/ensure_dataset.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/dataset_management/list_datasets.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/dataset_management/scope_management.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/db_bootstrap/account_manager.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/demo_imports.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/did_management/operations.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Deletion/delete_file_of_scope_name.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Deletion/delete_replica_of_scope_name.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Insertion/upload_file_of_scope_name.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Retrieval/download_file_of_scope_name.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Workflows/manage_derived_assets.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/list_files.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_bbox.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_bbox_and_timerange.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_enhanced_filter.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_relationships.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_timerange.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/mapper.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Insertion/set_metadata.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/common.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/delete_metadata_for_did.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/insert_metadata_for_did.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/update_metadata_for_did.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/upsert_metadata_for_did.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Patch/patch_metadata_for_dids.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Preflight/validate_derived_item_file.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Preflight/validate_facet_value_catalog_file.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Retrieval/Complete/get_metadata_for_scope_name.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Retrieval/Partial/get_single_metadata_for_scope_name.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Workflows/sync_spatial_bbox_integrity.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Workflows/update_derived_item.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Workflows/update_original_item.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/document_api.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/resources/schemas/Schema_DaFab_Facet_Value_Catalog.json +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/resources/schemas/dafab-smart_agriculture-item.schema.json +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/resources/schemas/dafab-water_analysis-item.schema.json +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/runtime_paths.py +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/SOURCES.txt +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/dependency_links.txt +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/requires.txt +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/top_level.txt +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/readme.md +0 -0
- {dafab_client-2.2.3 → dafab_client-2.2.4}/setup.cfg +0 -0
{dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/example_notebooks/dafab_dasi_workflows.ipynb
RENAMED
|
@@ -73,13 +73,16 @@
|
|
|
73
73
|
"STAC_COLLECTION_ID = \"sentinel_2_l2a\"\n",
|
|
74
74
|
"\n",
|
|
75
75
|
"# Input JSON file for original-item validation.\n",
|
|
76
|
-
"ORIGINAL_ITEM_FILE = \"docs/notebooks/demo-data/uploads/items/sentinel-2-l2a/
|
|
76
|
+
"ORIGINAL_ITEM_FILE = \"../../docs/notebooks/demo-data/uploads/items/sentinel-2-l2a/S2C_MSIL2A_20260218T105111_N0512_R051_T30TYN_20260218T145711.json\"\n",
|
|
77
77
|
"\n",
|
|
78
|
-
"# JSON schema
|
|
79
|
-
"
|
|
78
|
+
"# JSON schema used before the item is normalized into DaFab STAC state.\n",
|
|
79
|
+
"ORIGINAL_ITEM_SOURCE_SCHEMA = \"../../dafab_client/helpers/resources/schemas/dasi-original-item.schema.json\"\n",
|
|
80
|
+
"\n",
|
|
81
|
+
"# JSON schema used after DaFab navigation fields are synchronized.\n",
|
|
82
|
+
"ORIGINAL_ITEM_SCHEMA = \"../../dafab_client/helpers/resources/schemas/Schema_Copernicus_with_dafab.json\"\n",
|
|
80
83
|
"\n",
|
|
81
84
|
"# Original Sentinel-2 item ID reused across DASI publication steps.\n",
|
|
82
|
-
"ORIGINAL_ITEM_ID = \"
|
|
85
|
+
"ORIGINAL_ITEM_ID = \"S2C_MSIL2A_20260218T105111_N0512_R051_T30TYN_20260218T145711\"\n"
|
|
83
86
|
],
|
|
84
87
|
"id": "51b980534fbae32f",
|
|
85
88
|
"outputs": [],
|
|
@@ -133,12 +136,12 @@
|
|
|
133
136
|
"metadata": {},
|
|
134
137
|
"source": [
|
|
135
138
|
"## 4. Validate Original Item Contract (Pre-Publish Mode)\n",
|
|
136
|
-
"- Load the local JSON payload and validate it against the
|
|
139
|
+
"- Load the local JSON payload and validate it against the DASI original-item schema before insertion.\n",
|
|
137
140
|
"- Check required STAC fields and object type (`Feature`, `id`, `collection`, `properties`, `geometry`, `bbox`, `links`, `assets`).\n",
|
|
138
|
-
"- Validate
|
|
141
|
+
"- Validate the source `self` link and href/id consistency.\n",
|
|
139
142
|
"- Validate coordinate-box structure and ordering.\n",
|
|
140
143
|
"- Validate temporal fields (`datetime`, optional `created`/`updated` ordering).\n",
|
|
141
|
-
"- Validate Sentinel
|
|
144
|
+
"- Validate Sentinel SAFE-style product ID semantics.\n",
|
|
142
145
|
"- Validate collection-name compatibility against the expected collection ID (hyphen/underscore normalization).\n"
|
|
143
146
|
],
|
|
144
147
|
"id": "c3d5740b3ef24780"
|
|
@@ -151,12 +154,12 @@
|
|
|
151
154
|
" scope=STAC_SCOPE,\n",
|
|
152
155
|
" source=\"local\",\n",
|
|
153
156
|
" item_path=ORIGINAL_ITEM_FILE,\n",
|
|
154
|
-
" schema_path=
|
|
157
|
+
" schema_path=ORIGINAL_ITEM_SOURCE_SCHEMA,\n",
|
|
155
158
|
" expected_item_id=ORIGINAL_ITEM_ID,\n",
|
|
156
159
|
" expected_collection_id=STAC_COLLECTION_ID,\n",
|
|
157
160
|
" check_catalog_state=False,\n",
|
|
158
161
|
")\n",
|
|
159
|
-
"print(dc.as_json(original_item_pre_publish_validation))"
|
|
162
|
+
"print(dc.as_json(original_item_pre_publish_validation))\n"
|
|
160
163
|
],
|
|
161
164
|
"id": "8f988f1e6d08439a",
|
|
162
165
|
"outputs": [],
|
|
@@ -180,7 +183,7 @@
|
|
|
180
183
|
" metadata_path=ORIGINAL_ITEM_FILE,\n",
|
|
181
184
|
" scope=STAC_SCOPE,\n",
|
|
182
185
|
" op=\"upsert\",\n",
|
|
183
|
-
")"
|
|
186
|
+
")\n"
|
|
184
187
|
],
|
|
185
188
|
"id": "6e822f1f67784d8f",
|
|
186
189
|
"outputs": [],
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import argparse
|
|
6
6
|
import copy
|
|
7
7
|
import json
|
|
8
|
+
import re
|
|
8
9
|
from datetime import datetime
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
from typing import Any, Literal, Optional
|
|
@@ -37,6 +38,9 @@ STATE_ERROR_PREFIXES = (
|
|
|
37
38
|
"collection_metadata:",
|
|
38
39
|
"catalog_state:",
|
|
39
40
|
)
|
|
41
|
+
SENTINEL_SAFE_ITEM_ID_RE = re.compile(
|
|
42
|
+
r"^S2[ABC]_MSIL(?:1C|2A)_\d{8}T\d{6}_N\d{4}_R\d{3}_T\d{2}[A-Z]{3}_\d{8}T\d{6}$"
|
|
43
|
+
)
|
|
40
44
|
|
|
41
45
|
|
|
42
46
|
def _resolve_validation_phase(
|
|
@@ -178,33 +182,15 @@ def _validate_bbox(bbox: Any) -> tuple[Optional[bool], Optional[str]]:
|
|
|
178
182
|
return True, None
|
|
179
183
|
|
|
180
184
|
|
|
181
|
-
def _validate_item_id_semantics(item_id: Any, sequence: Any) -> tuple[Optional[bool], list[str]]:
|
|
182
|
-
issues: list[str] = []
|
|
185
|
+
def _validate_item_id_semantics(item_id: Any, sequence: Any) -> tuple[Optional[bool], list[str]]: # noqa: ARG001
|
|
183
186
|
if not isinstance(item_id, str) or not item_id.strip():
|
|
184
187
|
return False, ["item id is missing or empty."]
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
platform_token = parts[0]
|
|
192
|
-
sequence_token = parts[-2]
|
|
193
|
-
level_token = parts[-1]
|
|
194
|
-
|
|
195
|
-
if not platform_token.startswith("S2"):
|
|
196
|
-
issues.append(f"item id platform token '{platform_token}' does not look like Sentinel-2.")
|
|
197
|
-
if not sequence_token.isdigit():
|
|
198
|
-
issues.append(f"item id sequence token '{sequence_token}' is not numeric.")
|
|
199
|
-
if level_token not in {"L1C", "L2A"}:
|
|
200
|
-
issues.append(f"item id level token '{level_token}' is not one of expected values (L1C/L2A).")
|
|
201
|
-
|
|
202
|
-
if sequence is not None and str(sequence) != sequence_token:
|
|
203
|
-
issues.append(
|
|
204
|
-
f"properties['s2:sequence'] ('{sequence}') does not match item id sequence token '{sequence_token}'."
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
return len(issues) == 0, issues
|
|
188
|
+
if SENTINEL_SAFE_ITEM_ID_RE.fullmatch(item_id):
|
|
189
|
+
return True, []
|
|
190
|
+
return False, [
|
|
191
|
+
"item id does not match expected Sentinel SAFE-style product id "
|
|
192
|
+
"'<platform>_MSIL<level>_<sensing_time>_N<baseline>_R<relative_orbit>_T<mgrs_tile>_<generation_time>'."
|
|
193
|
+
]
|
|
208
194
|
|
|
209
195
|
|
|
210
196
|
def _extract_document(payload: Any) -> dict[str, Any]:
|
|
@@ -428,8 +414,6 @@ def _evaluate_original_item_payload(
|
|
|
428
414
|
links = core_fields["links"]
|
|
429
415
|
hrefs_by_rel = _collect_hrefs_by_rel(links)
|
|
430
416
|
self_links = hrefs_by_rel.get("self", [])
|
|
431
|
-
canonical_links = hrefs_by_rel.get("canonical", [])
|
|
432
|
-
derived_from_links = hrefs_by_rel.get("derived_from", [])
|
|
433
417
|
root_links = hrefs_by_rel.get("root", [])
|
|
434
418
|
collection_links = hrefs_by_rel.get("collection", [])
|
|
435
419
|
|
|
@@ -438,40 +422,21 @@ def _evaluate_original_item_payload(
|
|
|
438
422
|
if len(self_links) != 1:
|
|
439
423
|
link_structure_valid = False
|
|
440
424
|
errors.append(f"links: expected exactly one 'self' link, found {len(self_links)}")
|
|
441
|
-
if len(canonical_links) != 1:
|
|
442
|
-
link_structure_valid = False
|
|
443
|
-
errors.append(f"links: expected exactly one 'canonical' link, found {len(canonical_links)}")
|
|
444
|
-
if len(derived_from_links) < 1:
|
|
445
|
-
link_structure_valid = False
|
|
446
|
-
errors.append("links: expected at least one 'derived_from' link.")
|
|
447
|
-
|
|
448
|
-
if root_links:
|
|
449
|
-
root_valid = all(link == root_href for link in root_links)
|
|
450
|
-
report["checks"]["root_link_valid"] = root_valid
|
|
451
|
-
if not root_valid:
|
|
452
|
-
errors.append(f"root_link: expected all root hrefs to be '{root_href}', found {root_links}")
|
|
453
425
|
|
|
454
426
|
link_id_consistency_valid = False
|
|
455
427
|
if isinstance(item_id, str):
|
|
456
|
-
for
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if len(self_links) == 1 and len(canonical_links) == 1:
|
|
428
|
+
for href in self_links:
|
|
429
|
+
parsed_self_id = _item_id_from_href(href)
|
|
430
|
+
if parsed_self_id is None:
|
|
431
|
+
errors.append(f"links:self: could not extract item id from href '{href}'")
|
|
432
|
+
continue
|
|
433
|
+
if parsed_self_id != item_id:
|
|
434
|
+
errors.append(
|
|
435
|
+
f"links:self: href-derived id '{parsed_self_id}' does not match payload id '{item_id}'"
|
|
436
|
+
)
|
|
437
|
+
if len(self_links) == 1:
|
|
467
438
|
parsed_self_id = _item_id_from_href(self_links[0])
|
|
468
|
-
|
|
469
|
-
link_id_consistency_valid = (
|
|
470
|
-
parsed_self_id is not None
|
|
471
|
-
and parsed_canonical_id is not None
|
|
472
|
-
and parsed_self_id == item_id
|
|
473
|
-
and parsed_canonical_id == item_id
|
|
474
|
-
)
|
|
439
|
+
link_id_consistency_valid = parsed_self_id is not None and parsed_self_id == item_id
|
|
475
440
|
report["checks"]["link_id_consistency_valid"] = link_id_consistency_valid
|
|
476
441
|
|
|
477
442
|
if validation_phase == "post_publish":
|