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.
Files changed (105) hide show
  1. {dafab_client-2.2.3 → dafab_client-2.2.4}/PKG-INFO +1 -1
  2. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/example_notebooks/dafab_dasi_workflows.ipynb +13 -10
  3. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Preflight/validate_original_item_file.py +22 -57
  4. dafab_client-2.2.4/dafab_client/helpers/resources/schemas/Schema_Copernicus_with_dafab.json +780 -0
  5. dafab_client-2.2.4/dafab_client/helpers/resources/schemas/dasi-original-item.schema.json +691 -0
  6. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/PKG-INFO +1 -1
  7. {dafab_client-2.2.3 → dafab_client-2.2.4}/pyproject.toml +1 -1
  8. dafab_client-2.2.3/dafab_client/helpers/resources/schemas/Schema_Copernicus_with_dafab.json +0 -4781
  9. dafab_client-2.2.3/dafab_client/helpers/resources/schemas/dasi-original-item.schema.json +0 -4729
  10. {dafab_client-2.2.3 → dafab_client-2.2.4}/MANIFEST.in +0 -0
  11. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/__init__.py +0 -0
  12. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/__init__.py +0 -0
  13. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/__init__.py +0 -0
  14. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/accountclient.py +0 -0
  15. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/baseclient.py +0 -0
  16. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/client.py +0 -0
  17. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/credentialclient.py +0 -0
  18. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/didclient.py +0 -0
  19. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/downloadclient.py +0 -0
  20. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/pingclient.py +0 -0
  21. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/replicaclient.py +0 -0
  22. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/rseclient.py +0 -0
  23. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/scopeclient.py +0 -0
  24. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/client/uploadclient.py +0 -0
  25. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/cache.py +0 -0
  26. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/checksum.py +0 -0
  27. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/client.py +0 -0
  28. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/config.py +0 -0
  29. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/constants.py +0 -0
  30. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/constraints.py +0 -0
  31. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/didtype.py +0 -0
  32. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/exception.py +0 -0
  33. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/extra.py +0 -0
  34. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/filter.py +0 -0
  35. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/logging.py +0 -0
  36. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/pcache.py +0 -0
  37. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/plugins.py +0 -0
  38. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/types.py +0 -0
  39. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/common/utils.py +0 -0
  40. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/core/rse.py +0 -0
  41. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/dafab_lib.py +0 -0
  42. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/global_utils.py +0 -0
  43. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/overridden_rucio_client.py +0 -0
  44. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/__init__.py +0 -0
  45. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/protocols/__init__.py +0 -0
  46. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/protocols/gfal.py +0 -0
  47. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/protocols/protocol.py +0 -0
  48. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/protocols/webdav.py +0 -0
  49. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/rsemanager.py +0 -0
  50. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/rse/translation.py +0 -0
  51. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/secrets/user_dafab/config +0 -0
  52. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/secrets/user_dafab/rucio_ca_bundle.pem +0 -0
  53. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/_rucio/version.py +0 -0
  54. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/example_notebooks/dafab_operator_workflows.ipynb +0 -0
  55. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/example_notebooks/dafab_simple_user_workflows.ipynb +0 -0
  56. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/example_notebooks/dafab_skim_workflows.ipynb +0 -0
  57. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/System/check_available_accounts.py +0 -0
  58. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/System/check_storage.py +0 -0
  59. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/__init__.py +0 -0
  60. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/check_server_health.py +0 -0
  61. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/container_management/ensure_container.py +0 -0
  62. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/container_management/list_containers.py +0 -0
  63. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/dataset_management/ensure_dataset.py +0 -0
  64. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/dataset_management/list_datasets.py +0 -0
  65. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/dataset_management/scope_management.py +0 -0
  66. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/db_bootstrap/account_manager.py +0 -0
  67. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/demo_imports.py +0 -0
  68. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/did_management/operations.py +0 -0
  69. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Deletion/delete_file_of_scope_name.py +0 -0
  70. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Deletion/delete_replica_of_scope_name.py +0 -0
  71. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Insertion/upload_file_of_scope_name.py +0 -0
  72. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Retrieval/download_file_of_scope_name.py +0 -0
  73. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/Workflows/manage_derived_assets.py +0 -0
  74. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/file_management/list_files.py +0 -0
  75. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_bbox.py +0 -0
  76. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_bbox_and_timerange.py +0 -0
  77. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_enhanced_filter.py +0 -0
  78. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_relationships.py +0 -0
  79. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/filter_by_timerange.py +0 -0
  80. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/filtering/mapper.py +0 -0
  81. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Insertion/set_metadata.py +0 -0
  82. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/common.py +0 -0
  83. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/delete_metadata_for_did.py +0 -0
  84. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/insert_metadata_for_did.py +0 -0
  85. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/update_metadata_for_did.py +0 -0
  86. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Operations/upsert_metadata_for_did.py +0 -0
  87. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Patch/patch_metadata_for_dids.py +0 -0
  88. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Preflight/validate_derived_item_file.py +0 -0
  89. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Preflight/validate_facet_value_catalog_file.py +0 -0
  90. {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
  91. {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
  92. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Workflows/sync_spatial_bbox_integrity.py +0 -0
  93. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Workflows/update_derived_item.py +0 -0
  94. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/Workflows/update_original_item.py +0 -0
  95. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/metadata_management/document_api.py +0 -0
  96. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/resources/schemas/Schema_DaFab_Facet_Value_Catalog.json +0 -0
  97. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/resources/schemas/dafab-smart_agriculture-item.schema.json +0 -0
  98. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/resources/schemas/dafab-water_analysis-item.schema.json +0 -0
  99. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client/helpers/runtime_paths.py +0 -0
  100. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/SOURCES.txt +0 -0
  101. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/dependency_links.txt +0 -0
  102. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/requires.txt +0 -0
  103. {dafab_client-2.2.3 → dafab_client-2.2.4}/dafab_client.egg-info/top_level.txt +0 -0
  104. {dafab_client-2.2.3 → dafab_client-2.2.4}/readme.md +0 -0
  105. {dafab_client-2.2.3 → dafab_client-2.2.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dafab_client
3
- Version: 2.2.3
3
+ Version: 2.2.4
4
4
  Summary: DaFab helper client built on top of Rucio client modules.
5
5
  Author: DaFab
6
6
  License-Expression: Apache-2.0
@@ -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/S2A_45RYJ_20221205_0_L2A_(bootstrap).json\"\n",
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 file used to validate the original-item payload.\n",
79
- "ORIGINAL_ITEM_SCHEMA = \"docs/notebooks/demo-data/uploads/items/Schema_Copernicus_with_dafab.json\"\n",
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 = \"S2A_45RYJ_20221205_0_L2A\""
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 Copernicus+DaFab schema before insertion.\n",
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 pre-publish link contract (`self`, `canonical`, `derived_from`) and href/id consistency.\n",
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 item-id semantics and sequence consistency.\n",
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=ORIGINAL_ITEM_SCHEMA,\n",
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
- parts = item_id.split("_")
187
- if len(parts) < 5:
188
- issues.append("item id does not match expected Sentinel token layout '<platform>_<tile>_<date>_<sequence>_<level>'.")
189
- return False, issues
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 rel_name, rel_links in {"self": self_links, "canonical": canonical_links}.items():
457
- for href in rel_links:
458
- parsed_id = _item_id_from_href(href)
459
- if parsed_id is None:
460
- errors.append(f"links:{rel_name}: could not extract item id from href '{href}'")
461
- continue
462
- if parsed_id != item_id:
463
- errors.append(
464
- f"links:{rel_name}: href-derived id '{parsed_id}' does not match payload id '{item_id}'"
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
- parsed_canonical_id = _item_id_from_href(canonical_links[0])
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":