|
7 | 7 | sys.path.append(str(Path(__file__).resolve().parents[1] / "src")) |
8 | 8 |
|
9 | 9 | from dataverse_sdk import DataverseClient |
| 10 | +from dataverse_sdk.errors import MetadataError |
10 | 11 | from enum import IntEnum |
11 | 12 | from azure.identity import InteractiveBrowserCredential |
12 | 13 | import traceback |
@@ -64,7 +65,7 @@ def backoff_retry(op, *, delays=(0, 2, 5, 10, 20), retry_http_statuses=(400, 403 |
64 | 65 | break |
65 | 66 | if last_exc: |
66 | 67 | raise last_exc |
67 | | - |
| 68 | + |
68 | 69 | # Enum demonstrating local option set creation with multilingual labels (for French labels to work, enable French language in the environment first) |
69 | 70 | class Status(IntEnum): |
70 | 71 | Active = 1 |
@@ -141,7 +142,94 @@ class Status(IntEnum): |
141 | 142 | pass |
142 | 143 | # Fail fast: all operations must use the custom table |
143 | 144 | sys.exit(1) |
| 145 | +entity_schema = table_info.get("entity_schema") or "new_SampleItem" |
144 | 146 | logical = table_info.get("entity_logical_name") |
| 147 | +metadata_id = table_info.get("metadata_id") |
| 148 | +if not metadata_id: |
| 149 | + refreshed_info = client.get_table_info(entity_schema) or {} |
| 150 | + metadata_id = refreshed_info.get("metadata_id") |
| 151 | + if metadata_id: |
| 152 | + table_info["metadata_id"] = metadata_id |
| 153 | + |
| 154 | +# 6) Column metadata helpers: column create/delete |
| 155 | +print("Column metadata helpers (create/delete column):") |
| 156 | +scratch_column = f"scratch_{int(time.time())}" |
| 157 | +column_payload = {scratch_column: "string"} |
| 158 | +try: |
| 159 | + log_call(f"client.create_column('{entity_schema}', {repr(column_payload)})") |
| 160 | + column_create = client.create_columns(entity_schema, column_payload) |
| 161 | + if not isinstance(column_create, list) or not column_create: |
| 162 | + raise RuntimeError("create_column did not return schema list") |
| 163 | + created_details = column_create |
| 164 | + if not all(isinstance(item, str) for item in created_details): |
| 165 | + raise RuntimeError("create_column entries were not schema strings") |
| 166 | + attribute_schema = created_details[0] |
| 167 | + odata_client = client._get_odata() |
| 168 | + exists_after_create = None |
| 169 | + exists_after_delete = None |
| 170 | + attr_type_before = None |
| 171 | + attr_type_before_norm = None |
| 172 | + if metadata_id and attribute_schema: |
| 173 | + _ready_message = "Column metadata not yet available" |
| 174 | + def _metadata_after_create(): |
| 175 | + meta = odata_client._get_attribute_metadata( |
| 176 | + metadata_id, |
| 177 | + attribute_schema, |
| 178 | + extra_select="@odata.type,AttributeType", |
| 179 | + ) |
| 180 | + if not meta or not meta.get("MetadataId"): |
| 181 | + raise RuntimeError(_ready_message) |
| 182 | + return meta |
| 183 | + |
| 184 | + ready_meta = backoff_retry( |
| 185 | + _metadata_after_create, |
| 186 | + delays=(0, 1, 2, 4, 8), |
| 187 | + retry_http_statuses=(), |
| 188 | + retry_if=lambda exc: isinstance(exc, RuntimeError) and str(exc) == _ready_message, |
| 189 | + ) |
| 190 | + exists_after_create = bool(ready_meta) |
| 191 | + raw_type = ready_meta.get("@odata.type") or ready_meta.get("AttributeType") |
| 192 | + if isinstance(raw_type, str): |
| 193 | + attr_type_before = raw_type |
| 194 | + lowered = raw_type.lower() |
| 195 | + attr_type_before_norm = lowered.rsplit(".", 1)[-1] if "." in lowered else lowered |
| 196 | + log_call(f"client.delete_column('{entity_schema}', '{scratch_column}')") |
| 197 | + column_delete = client.delete_columns(entity_schema, scratch_column) |
| 198 | + if not isinstance(column_delete, list) or not column_delete: |
| 199 | + raise RuntimeError("delete_column did not return schema list") |
| 200 | + deleted_details = column_delete |
| 201 | + if not all(isinstance(item, str) for item in deleted_details): |
| 202 | + raise RuntimeError("delete_column entries were not schema strings") |
| 203 | + if attribute_schema not in deleted_details: |
| 204 | + raise RuntimeError("delete_columns response missing expected schema name") |
| 205 | + if metadata_id and attribute_schema: |
| 206 | + _delete_message = "Column metadata still present after delete" |
| 207 | + def _ensure_removed(): |
| 208 | + meta = odata_client._get_attribute_metadata(metadata_id, attribute_schema) |
| 209 | + if meta: |
| 210 | + raise RuntimeError(_delete_message) |
| 211 | + return True |
| 212 | + |
| 213 | + removed = backoff_retry( |
| 214 | + _ensure_removed, |
| 215 | + delays=(0, 1, 2, 4, 8), |
| 216 | + retry_http_statuses=(), |
| 217 | + retry_if=lambda exc: isinstance(exc, RuntimeError) and str(exc) == _delete_message, |
| 218 | + ) |
| 219 | + exists_after_delete = not removed |
| 220 | + print({ |
| 221 | + "created_column": scratch_column, |
| 222 | + "create_summary": created_details, |
| 223 | + "delete_summary": deleted_details, |
| 224 | + "attribute_type_before_delete": attr_type_before, |
| 225 | + "exists_after_create": exists_after_create, |
| 226 | + "exists_after_delete": exists_after_delete, |
| 227 | + }) |
| 228 | +except MetadataError as meta_err: |
| 229 | + print({"column_metadata_error": str(meta_err)}) |
| 230 | +except Exception as exc: |
| 231 | + print({"column_metadata_unexpected": str(exc)}) |
| 232 | +sys.exit(0) |
145 | 233 |
|
146 | 234 | # Derive attribute logical name prefix from the entity logical name (segment before first underscore) |
147 | 235 | attr_prefix = logical.split("_", 1)[0] if "_" in logical else logical |
@@ -527,9 +615,90 @@ def _del_one(rid: str) -> tuple[str, bool, str | None]: |
527 | 615 | except Exception as e: |
528 | 616 | print(f"Delete failed: {e}") |
529 | 617 |
|
| 618 | +pause("Next: column metadata helpers") |
| 619 | + |
| 620 | +# 6) Column metadata helpers: column create/delete |
| 621 | +print("Column metadata helpers (create/delete column):") |
| 622 | +scratch_column = f"scratch_{int(time.time())}" |
| 623 | +column_payload = {scratch_column: "string"} |
| 624 | +try: |
| 625 | + log_call(f"client.create_column('{entity_schema}', {repr(column_payload)})") |
| 626 | + column_create = client.create_columns(entity_schema, column_payload) |
| 627 | + if not isinstance(column_create, list) or not column_create: |
| 628 | + raise RuntimeError("create_column did not return schema list") |
| 629 | + created_details = column_create |
| 630 | + if not all(isinstance(item, str) for item in created_details): |
| 631 | + raise RuntimeError("create_column entries were not schema strings") |
| 632 | + attribute_schema = created_details[0] |
| 633 | + odata_client = client._get_odata() |
| 634 | + exists_after_create = None |
| 635 | + exists_after_delete = None |
| 636 | + attr_type_before = None |
| 637 | + attr_type_before_norm = None |
| 638 | + if metadata_id and attribute_schema: |
| 639 | + _ready_message = "Column metadata not yet available" |
| 640 | + def _metadata_after_create(): |
| 641 | + meta = odata_client._get_attribute_metadata( |
| 642 | + metadata_id, |
| 643 | + attribute_schema, |
| 644 | + extra_select="@odata.type,AttributeType", |
| 645 | + ) |
| 646 | + if not meta or not meta.get("MetadataId"): |
| 647 | + raise RuntimeError(_ready_message) |
| 648 | + return meta |
| 649 | + |
| 650 | + ready_meta = backoff_retry( |
| 651 | + _metadata_after_create, |
| 652 | + delays=(0, 1, 2, 4, 8), |
| 653 | + retry_http_statuses=(), |
| 654 | + retry_if=lambda exc: isinstance(exc, RuntimeError) and str(exc) == _ready_message, |
| 655 | + ) |
| 656 | + exists_after_create = bool(ready_meta) |
| 657 | + raw_type = ready_meta.get("@odata.type") or ready_meta.get("AttributeType") |
| 658 | + if isinstance(raw_type, str): |
| 659 | + attr_type_before = raw_type |
| 660 | + lowered = raw_type.lower() |
| 661 | + attr_type_before_norm = lowered.rsplit(".", 1)[-1] if "." in lowered else lowered |
| 662 | + log_call(f"client.delete_column('{entity_schema}', '{scratch_column}')") |
| 663 | + column_delete = client.delete_columns(entity_schema, scratch_column) |
| 664 | + if not isinstance(column_delete, list) or not column_delete: |
| 665 | + raise RuntimeError("delete_column did not return schema list") |
| 666 | + deleted_details = column_delete |
| 667 | + if not all(isinstance(item, str) for item in deleted_details): |
| 668 | + raise RuntimeError("delete_column entries were not schema strings") |
| 669 | + if attribute_schema not in deleted_details: |
| 670 | + raise RuntimeError("delete_column response missing expected schema name") |
| 671 | + if metadata_id and attribute_schema: |
| 672 | + _delete_message = "Column metadata still present after delete" |
| 673 | + def _ensure_removed(): |
| 674 | + meta = odata_client._get_attribute_metadata(metadata_id, attribute_schema) |
| 675 | + if meta: |
| 676 | + raise RuntimeError(_delete_message) |
| 677 | + return True |
| 678 | + |
| 679 | + removed = backoff_retry( |
| 680 | + _ensure_removed, |
| 681 | + delays=(0, 1, 2, 4, 8), |
| 682 | + retry_http_statuses=(), |
| 683 | + retry_if=lambda exc: isinstance(exc, RuntimeError) and str(exc) == _delete_message, |
| 684 | + ) |
| 685 | + exists_after_delete = not removed |
| 686 | + print({ |
| 687 | + "created_column": scratch_column, |
| 688 | + "create_summary": created_details, |
| 689 | + "delete_summary": deleted_details, |
| 690 | + "attribute_type_before_delete": attr_type_before, |
| 691 | + "exists_after_create": exists_after_create, |
| 692 | + "exists_after_delete": exists_after_delete, |
| 693 | + }) |
| 694 | +except MetadataError as meta_err: |
| 695 | + print({"column_metadata_error": str(meta_err)}) |
| 696 | +except Exception as exc: |
| 697 | + print({"column_metadata_unexpected": str(exc)}) |
| 698 | + |
530 | 699 | pause("Next: Cleanup table") |
531 | 700 |
|
532 | | -# 6) Cleanup: delete the custom table if it exists |
| 701 | +# 7) Cleanup: delete the custom table if it exists |
533 | 702 | print("Cleanup (Metadata):") |
534 | 703 | if delete_table_at_end: |
535 | 704 | try: |
|
0 commit comments