From 299cfca4bfd9c4e83020810fe0c458be0cc1eacc Mon Sep 17 00:00:00 2001 From: Max Wang Date: Wed, 5 Nov 2025 10:00:23 -0800 Subject: [PATCH 1/2] add solution association in create table --- README.md | 3 ++- src/dataverse_sdk/client.py | 16 ++++++++++++++-- src/dataverse_sdk/odata.py | 33 +++++++++++++++++++++++++++++---- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 26c6c42..58222b9 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Auth: | `delete` | `delete(logical_name, id)` | `None` | Delete one record. | | `delete` | `delete(logical_name, list[id])` | `None` | Delete many (sequential). | | `query_sql` | `query_sql(sql)` | `list[dict]` | Constrained read-only SELECT via `?sql=`. | -| `create_table` | `create_table(tablename, schema)` | `dict` | Creates custom table + columns. Friendly name (e.g. `SampleItem`) becomes schema `new_SampleItem`; explicit schema name (contains `_`) used as-is. | +| `create_table` | `create_table(tablename, schema, solution_unique_name=None)` | `dict` | Creates custom table + columns. Friendly name (e.g. `SampleItem`) becomes schema `new_SampleItem`; explicit schema name (contains `_`) used as-is. Pass `solution_unique_name` to attach the table to a specific solution instead of the default solution. | | `create_column` | `create_column(tablename, columns)` | `list[str]` | Adds columns using a `{name: type}` mapping (same shape as `create_table` schema). Returns schema names for the created columns. | | `get_table_info` | `get_table_info(schema_name)` | `dict | None` | Basic table metadata by schema name (e.g. `new_SampleItem`). Friendly names not auto-converted. | | `list_tables` | `list_tables()` | `list[dict]` | Lists non-private tables. | @@ -310,6 +310,7 @@ info = client.create_table( "active": "bool", "status": Status, }, + solution_unique_name="my_solution_unique_name", # optional: associate table with this solution ) # Create or delete columns diff --git a/src/dataverse_sdk/client.py b/src/dataverse_sdk/client.py index 3e6c3a9..4e5be5a 100644 --- a/src/dataverse_sdk/client.py +++ b/src/dataverse_sdk/client.py @@ -373,7 +373,12 @@ def get_table_info(self, tablename: str) -> Optional[Dict[str, Any]]: """ return self._get_odata()._get_table_info(tablename) - def create_table(self, tablename: str, schema: Dict[str, Any]) -> Dict[str, Any]: + def create_table( + self, + tablename: str, + schema: Dict[str, Any], + solution_unique_name: Optional[str] = None, + ) -> Dict[str, Any]: """ Create a simple custom table with specified columns. @@ -397,6 +402,9 @@ class ItemStatus(IntEnum): } :type schema: dict[str, Any] + :param solution_unique_name: Optional solution unique name that should own the new table. + When omitted the table is created in the default solution. + :type solution_unique_name: str or None :return: Dictionary containing table metadata including ``entity_schema``, ``entity_set_name``, ``entity_logical_name``, ``metadata_id``, and ``columns_created``. @@ -425,7 +433,11 @@ class ItemStatus(IntEnum): print(f"Created table: {result['entity_logical_name']}") print(f"Columns: {result['columns_created']}") """ - return self._get_odata()._create_table(tablename, schema) + return self._get_odata()._create_table( + tablename, + schema, + solution_unique_name, + ) def delete_table(self, tablename: str) -> None: """ diff --git a/src/dataverse_sdk/odata.py b/src/dataverse_sdk/odata.py index c7fccc3..f00441f 100644 --- a/src/dataverse_sdk/odata.py +++ b/src/dataverse_sdk/odata.py @@ -632,7 +632,13 @@ def _get_entity_by_schema(self, schema_name: str) -> Optional[Dict[str, Any]]: items = r.json().get("value", []) return items[0] if items else None - def _create_entity(self, schema_name: str, display_name: str, attributes: List[Dict[str, Any]]) -> str: + def _create_entity( + self, + schema_name: str, + display_name: str, + attributes: List[Dict[str, Any]], + solution_unique_name: Optional[str] = None, + ) -> str: url = f"{self.api}/EntityDefinitions" payload = { "@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata", @@ -646,7 +652,10 @@ def _create_entity(self, schema_name: str, display_name: str, attributes: List[D "IsActivity": False, "Attributes": attributes, } - r = self._request("post", url, json=payload) + params = None + if solution_unique_name: + params = {"SolutionUniqueName": solution_unique_name} + r = self._request("post", url, json=payload, params=params) ent = self._wait_for_entity_ready(schema_name) if not ent or not ent.get("EntitySetName"): raise RuntimeError( @@ -1086,7 +1095,12 @@ def _delete_table(self, tablename: str) -> None: url = f"{self.api}/EntityDefinitions({metadata_id})" r = self._request("delete", url) - def _create_table(self, tablename: str, schema: Dict[str, Any]) -> Dict[str, Any]: + def _create_table( + self, + tablename: str, + schema: Dict[str, Any], + solution_unique_name: Optional[str] = None, + ) -> Dict[str, Any]: # Accept a friendly name and construct a default schema under 'new_'. # If a full SchemaName is passed (contains '_'), use as-is. entity_schema = self._normalize_entity_schema(tablename) @@ -1110,7 +1124,18 @@ def _create_table(self, tablename: str, schema: Dict[str, Any]) -> Dict[str, Any attributes.append(payload) created_cols.append(attr_schema) - metadata_id = self._create_entity(entity_schema, tablename, attributes) + if solution_unique_name is not None: + if not isinstance(solution_unique_name, str): + raise TypeError("solution_unique_name must be a string when provided") + if not solution_unique_name: + raise ValueError("solution_unique_name cannot be empty") + + metadata_id = self._create_entity( + entity_schema, + tablename, + attributes, + solution_unique_name, + ) ent2: Dict[str, Any] = self._wait_for_entity_ready(entity_schema) or {} logical_name = ent2.get("LogicalName") From 00e22287ed088f552e826f0f79944b797325cb33 Mon Sep 17 00:00:00 2001 From: zhaodongwang-msft Date: Wed, 5 Nov 2025 10:08:27 -0800 Subject: [PATCH 2/2] Update src/dataverse_sdk/odata.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/dataverse_sdk/odata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dataverse_sdk/odata.py b/src/dataverse_sdk/odata.py index f00441f..f01bcf7 100644 --- a/src/dataverse_sdk/odata.py +++ b/src/dataverse_sdk/odata.py @@ -655,7 +655,7 @@ def _create_entity( params = None if solution_unique_name: params = {"SolutionUniqueName": solution_unique_name} - r = self._request("post", url, json=payload, params=params) + self._request("post", url, json=payload, params=params) ent = self._wait_for_entity_ready(schema_name) if not ent or not ent.get("EntitySetName"): raise RuntimeError(