|
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,14 @@ 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 |
145 | 153 |
|
146 | 154 | # Derive attribute logical name prefix from the entity logical name (segment before first underscore) |
147 | 155 | attr_prefix = logical.split("_", 1)[0] if "_" in logical else logical |
@@ -527,9 +535,88 @@ def _del_one(rid: str) -> tuple[str, bool, str | None]: |
527 | 535 | except Exception as e: |
528 | 536 | print(f"Delete failed: {e}") |
529 | 537 |
|
| 538 | +pause("Next: column metadata helpers") |
| 539 | + |
| 540 | +# 6) Column metadata helpers: column create/delete |
| 541 | +print("Column metadata helpers (create/delete column):") |
| 542 | +scratch_column = f"scratch_{int(time.time())}" |
| 543 | +column_payload = {scratch_column: "string"} |
| 544 | +try: |
| 545 | + log_call(f"client.create_column('{entity_schema}', {repr(column_payload)})") |
| 546 | + column_create = client.create_columns(entity_schema, column_payload) |
| 547 | + if not isinstance(column_create, list) or not column_create: |
| 548 | + raise RuntimeError("create_column did not return schema list") |
| 549 | + created_details = column_create |
| 550 | + if not all(isinstance(item, str) for item in created_details): |
| 551 | + raise RuntimeError("create_column entries were not schema strings") |
| 552 | + attribute_schema = created_details[0] |
| 553 | + odata_client = client._get_odata() |
| 554 | + exists_after_create = None |
| 555 | + exists_after_delete = None |
| 556 | + attr_type_before = None |
| 557 | + if metadata_id and attribute_schema: |
| 558 | + _ready_message = "Column metadata not yet available" |
| 559 | + def _metadata_after_create(): |
| 560 | + meta = odata_client._get_attribute_metadata( |
| 561 | + metadata_id, |
| 562 | + attribute_schema, |
| 563 | + extra_select="@odata.type,AttributeType", |
| 564 | + ) |
| 565 | + if not meta or not meta.get("MetadataId"): |
| 566 | + raise RuntimeError(_ready_message) |
| 567 | + return meta |
| 568 | + |
| 569 | + ready_meta = backoff_retry( |
| 570 | + _metadata_after_create, |
| 571 | + delays=(0, 1, 2, 4, 8), |
| 572 | + retry_http_statuses=(), |
| 573 | + retry_if=lambda exc: isinstance(exc, RuntimeError) and str(exc) == _ready_message, |
| 574 | + ) |
| 575 | + exists_after_create = bool(ready_meta) |
| 576 | + raw_type = ready_meta.get("@odata.type") or ready_meta.get("AttributeType") |
| 577 | + if isinstance(raw_type, str): |
| 578 | + attr_type_before = raw_type |
| 579 | + lowered = raw_type.lower() |
| 580 | + log_call(f"client.delete_column('{entity_schema}', '{scratch_column}')") |
| 581 | + column_delete = client.delete_columns(entity_schema, scratch_column) |
| 582 | + if not isinstance(column_delete, list) or not column_delete: |
| 583 | + raise RuntimeError("delete_column did not return schema list") |
| 584 | + deleted_details = column_delete |
| 585 | + if not all(isinstance(item, str) for item in deleted_details): |
| 586 | + raise RuntimeError("delete_column entries were not schema strings") |
| 587 | + if attribute_schema not in deleted_details: |
| 588 | + raise RuntimeError("delete_column response missing expected schema name") |
| 589 | + if metadata_id and attribute_schema: |
| 590 | + _delete_message = "Column metadata still present after delete" |
| 591 | + def _ensure_removed(): |
| 592 | + meta = odata_client._get_attribute_metadata(metadata_id, attribute_schema) |
| 593 | + if meta: |
| 594 | + raise RuntimeError(_delete_message) |
| 595 | + return True |
| 596 | + |
| 597 | + removed = backoff_retry( |
| 598 | + _ensure_removed, |
| 599 | + delays=(0, 1, 2, 4, 8), |
| 600 | + retry_http_statuses=(), |
| 601 | + retry_if=lambda exc: isinstance(exc, RuntimeError) and str(exc) == _delete_message, |
| 602 | + ) |
| 603 | + exists_after_delete = not removed |
| 604 | + print({ |
| 605 | + "created_column": scratch_column, |
| 606 | + "create_summary": created_details, |
| 607 | + "delete_summary": deleted_details, |
| 608 | + "attribute_type_before_delete": attr_type_before, |
| 609 | + "exists_after_create": exists_after_create, |
| 610 | + "exists_after_delete": exists_after_delete, |
| 611 | + }) |
| 612 | +except MetadataError as meta_err: |
| 613 | + print({"column_metadata_error": str(meta_err)}) |
| 614 | +except Exception as exc: |
| 615 | + print({"column_metadata_unexpected": str(exc)}) |
| 616 | + |
530 | 617 | pause("Next: Cleanup table") |
531 | 618 |
|
532 | | -# 6) Cleanup: delete the custom table if it exists |
| 619 | +# 7) Cleanup: delete the custom table if it exists |
533 | 620 | print("Cleanup (Metadata):") |
534 | 621 | if delete_table_at_end: |
535 | 622 | try: |
|
0 commit comments