diff --git a/robosystems_client/api/graph_operations/update_graph_metadata.py b/robosystems_client/api/graph_operations/update_graph_metadata.py new file mode 100644 index 0000000..9c6ab5e --- /dev/null +++ b/robosystems_client/api/graph_operations/update_graph_metadata.py @@ -0,0 +1,310 @@ +from http import HTTPStatus +from typing import Any +from urllib.parse import quote + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.error_response import ErrorResponse +from ...models.operation_envelope_graph_metadata_result import ( + OperationEnvelopeGraphMetadataResult, +) +from ...models.update_graph_metadata_op import UpdateGraphMetadataOp +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + graph_id: str, + *, + body: UpdateGraphMetadataOp, + idempotency_key: None | str | Unset = UNSET, +) -> dict[str, Any]: + headers: dict[str, Any] = {} + if not isinstance(idempotency_key, Unset): + headers["Idempotency-Key"] = idempotency_key + + _kwargs: dict[str, Any] = { + "method": "post", + "url": "/v1/graphs/{graph_id}/operations/update-graph-metadata".format( + graph_id=quote(str(graph_id), safe=""), + ), + } + + _kwargs["json"] = body.to_dict() + + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + + +def _parse_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> ErrorResponse | OperationEnvelopeGraphMetadataResult | None: + if response.status_code == 200: + response_200 = OperationEnvelopeGraphMetadataResult.from_dict(response.json()) + + return response_200 + + if response.status_code == 400: + response_400 = ErrorResponse.from_dict(response.json()) + + return response_400 + + if response.status_code == 401: + response_401 = ErrorResponse.from_dict(response.json()) + + return response_401 + + if response.status_code == 403: + response_403 = ErrorResponse.from_dict(response.json()) + + return response_403 + + if response.status_code == 404: + response_404 = ErrorResponse.from_dict(response.json()) + + return response_404 + + if response.status_code == 409: + response_409 = ErrorResponse.from_dict(response.json()) + + return response_409 + + if response.status_code == 422: + response_422 = ErrorResponse.from_dict(response.json()) + + return response_422 + + if response.status_code == 429: + response_429 = ErrorResponse.from_dict(response.json()) + + return response_429 + + if response.status_code == 500: + response_500 = ErrorResponse.from_dict(response.json()) + + return response_500 + + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[ErrorResponse | OperationEnvelopeGraphMetadataResult]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + graph_id: str, + *, + client: AuthenticatedClient, + body: UpdateGraphMetadataOp, + idempotency_key: None | str | Unset = UNSET, +) -> Response[ErrorResponse | OperationEnvelopeGraphMetadataResult]: + r"""Update Graph Metadata + + Edit the graph's platform-level label: display name, description, and tags. Partial — only supplied + (non-null) fields change; pass `\"\"` to clear the description and `[]` to clear the tags. Requires + admin on the graph. This is not the entity name that appears on financial statements — change that + through the roboledger `update-entity` operation. + + **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours + return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. + + Args: + graph_id (str): + idempotency_key (None | str | Unset): + body (UpdateGraphMetadataOp): Body for the update-graph-metadata operation. + + Partial update — only supplied (non-null) fields change, so a caller + editing just the display name need not resend the description and tags. + Because ``None`` means "leave alone", clearing a field uses its empty + value instead: pass ``""`` to clear the description and ``[]`` to clear + the tags. ``graph_name`` cannot be cleared; it is the graph's label + everywhere it is listed. + + This is the platform-level label for the graph, independent of the + entity name shown on financial statements — change that through + ``POST /extensions/roboledger/{graph_id}/operations/update-entity``. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[ErrorResponse | OperationEnvelopeGraphMetadataResult] + """ + + kwargs = _get_kwargs( + graph_id=graph_id, + body=body, + idempotency_key=idempotency_key, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + graph_id: str, + *, + client: AuthenticatedClient, + body: UpdateGraphMetadataOp, + idempotency_key: None | str | Unset = UNSET, +) -> ErrorResponse | OperationEnvelopeGraphMetadataResult | None: + r"""Update Graph Metadata + + Edit the graph's platform-level label: display name, description, and tags. Partial — only supplied + (non-null) fields change; pass `\"\"` to clear the description and `[]` to clear the tags. Requires + admin on the graph. This is not the entity name that appears on financial statements — change that + through the roboledger `update-entity` operation. + + **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours + return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. + + Args: + graph_id (str): + idempotency_key (None | str | Unset): + body (UpdateGraphMetadataOp): Body for the update-graph-metadata operation. + + Partial update — only supplied (non-null) fields change, so a caller + editing just the display name need not resend the description and tags. + Because ``None`` means "leave alone", clearing a field uses its empty + value instead: pass ``""`` to clear the description and ``[]`` to clear + the tags. ``graph_name`` cannot be cleared; it is the graph's label + everywhere it is listed. + + This is the platform-level label for the graph, independent of the + entity name shown on financial statements — change that through + ``POST /extensions/roboledger/{graph_id}/operations/update-entity``. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + ErrorResponse | OperationEnvelopeGraphMetadataResult + """ + + return sync_detailed( + graph_id=graph_id, + client=client, + body=body, + idempotency_key=idempotency_key, + ).parsed + + +async def asyncio_detailed( + graph_id: str, + *, + client: AuthenticatedClient, + body: UpdateGraphMetadataOp, + idempotency_key: None | str | Unset = UNSET, +) -> Response[ErrorResponse | OperationEnvelopeGraphMetadataResult]: + r"""Update Graph Metadata + + Edit the graph's platform-level label: display name, description, and tags. Partial — only supplied + (non-null) fields change; pass `\"\"` to clear the description and `[]` to clear the tags. Requires + admin on the graph. This is not the entity name that appears on financial statements — change that + through the roboledger `update-entity` operation. + + **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours + return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. + + Args: + graph_id (str): + idempotency_key (None | str | Unset): + body (UpdateGraphMetadataOp): Body for the update-graph-metadata operation. + + Partial update — only supplied (non-null) fields change, so a caller + editing just the display name need not resend the description and tags. + Because ``None`` means "leave alone", clearing a field uses its empty + value instead: pass ``""`` to clear the description and ``[]`` to clear + the tags. ``graph_name`` cannot be cleared; it is the graph's label + everywhere it is listed. + + This is the platform-level label for the graph, independent of the + entity name shown on financial statements — change that through + ``POST /extensions/roboledger/{graph_id}/operations/update-entity``. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[ErrorResponse | OperationEnvelopeGraphMetadataResult] + """ + + kwargs = _get_kwargs( + graph_id=graph_id, + body=body, + idempotency_key=idempotency_key, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + graph_id: str, + *, + client: AuthenticatedClient, + body: UpdateGraphMetadataOp, + idempotency_key: None | str | Unset = UNSET, +) -> ErrorResponse | OperationEnvelopeGraphMetadataResult | None: + r"""Update Graph Metadata + + Edit the graph's platform-level label: display name, description, and tags. Partial — only supplied + (non-null) fields change; pass `\"\"` to clear the description and `[]` to clear the tags. Requires + admin on the graph. This is not the entity name that appears on financial statements — change that + through the roboledger `update-entity` operation. + + **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours + return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. + + Args: + graph_id (str): + idempotency_key (None | str | Unset): + body (UpdateGraphMetadataOp): Body for the update-graph-metadata operation. + + Partial update — only supplied (non-null) fields change, so a caller + editing just the display name need not resend the description and tags. + Because ``None`` means "leave alone", clearing a field uses its empty + value instead: pass ``""`` to clear the description and ``[]`` to clear + the tags. ``graph_name`` cannot be cleared; it is the graph's label + everywhere it is listed. + + This is the platform-level label for the graph, independent of the + entity name shown on financial statements — change that through + ``POST /extensions/roboledger/{graph_id}/operations/update-entity``. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + ErrorResponse | OperationEnvelopeGraphMetadataResult + """ + + return ( + await asyncio_detailed( + graph_id=graph_id, + client=client, + body=body, + idempotency_key=idempotency_key, + ) + ).parsed diff --git a/robosystems_client/models/__init__.py b/robosystems_client/models/__init__.py index b8d88f5..5c7108b 100644 --- a/robosystems_client/models/__init__.py +++ b/robosystems_client/models/__init__.py @@ -257,6 +257,7 @@ from .graph_member_response import GraphMemberResponse from .graph_member_response_source import GraphMemberResponseSource from .graph_metadata import GraphMetadata +from .graph_metadata_result import GraphMetadataResult from .graph_metrics_response import GraphMetricsResponse from .graph_metrics_response_health_status import GraphMetricsResponseHealthStatus from .graph_metrics_response_node_counts import GraphMetricsResponseNodeCounts @@ -466,6 +467,12 @@ from .operation_envelope_fiscal_calendar_response_status import ( OperationEnvelopeFiscalCalendarResponseStatus, ) +from .operation_envelope_graph_metadata_result import ( + OperationEnvelopeGraphMetadataResult, +) +from .operation_envelope_graph_metadata_result_status import ( + OperationEnvelopeGraphMetadataResultStatus, +) from .operation_envelope_information_block_envelope import ( OperationEnvelopeInformationBlockEnvelope, ) @@ -803,6 +810,7 @@ UpdateForecastRequestScenarioKindType0, ) from .update_graph_member_role_request import UpdateGraphMemberRoleRequest +from .update_graph_metadata_op import UpdateGraphMetadataOp from .update_journal_entry_request import UpdateJournalEntryRequest from .update_journal_entry_request_type_type_0 import UpdateJournalEntryRequestTypeType0 from .update_legacy_arm import UpdateLegacyArm @@ -1056,6 +1064,7 @@ "GraphMemberResponse", "GraphMemberResponseSource", "GraphMetadata", + "GraphMetadataResult", "GraphMetricsResponse", "GraphMetricsResponseHealthStatus", "GraphMetricsResponseNodeCounts", @@ -1179,6 +1188,8 @@ "OperationEnvelopeFinancialStatementAnalysisResponseStatus", "OperationEnvelopeFiscalCalendarResponse", "OperationEnvelopeFiscalCalendarResponseStatus", + "OperationEnvelopeGraphMetadataResult", + "OperationEnvelopeGraphMetadataResultStatus", "OperationEnvelopeInformationBlockEnvelope", "OperationEnvelopeInformationBlockEnvelopeStatus", "OperationEnvelopeInitializeLedgerResponse", @@ -1406,6 +1417,7 @@ "UpdateForecastRequest", "UpdateForecastRequestScenarioKindType0", "UpdateGraphMemberRoleRequest", + "UpdateGraphMetadataOp", "UpdateJournalEntryRequest", "UpdateJournalEntryRequestTypeType0", "UpdateLegacyArm", diff --git a/robosystems_client/models/graph_info.py b/robosystems_client/models/graph_info.py index 2bdb83b..b525ec8 100644 --- a/robosystems_client/models/graph_info.py +++ b/robosystems_client/models/graph_info.py @@ -21,6 +21,8 @@ class GraphInfo: role (str): User's role/access level is_selected (bool): Whether this is the currently selected graph created_at (str): Creation timestamp + description (str | Unset): Free-form description ('' when unset) Default: ''. + tags (list[str] | Unset): Organizational tags for the graph is_repository (bool | Unset): Whether this is a shared repository (vs user graph) Default: False. repository_type (None | str | Unset): Repository type if isRepository=true schema_extensions (list[str] | Unset): List of schema extensions installed on this graph @@ -35,6 +37,8 @@ class GraphInfo: role: str is_selected: bool created_at: str + description: str | Unset = "" + tags: list[str] | Unset = UNSET is_repository: bool | Unset = False repository_type: None | str | Unset = UNSET schema_extensions: list[str] | Unset = UNSET @@ -55,6 +59,12 @@ def to_dict(self) -> dict[str, Any]: created_at = self.created_at + description = self.description + + tags: list[str] | Unset = UNSET + if not isinstance(self.tags, Unset): + tags = self.tags + is_repository = self.is_repository repository_type: None | str | Unset @@ -90,6 +100,10 @@ def to_dict(self) -> dict[str, Any]: "createdAt": created_at, } ) + if description is not UNSET: + field_dict["description"] = description + if tags is not UNSET: + field_dict["tags"] = tags if is_repository is not UNSET: field_dict["isRepository"] = is_repository if repository_type is not UNSET: @@ -120,6 +134,10 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: created_at = d.pop("createdAt") + description = d.pop("description", UNSET) + + tags = cast(list[str], d.pop("tags", UNSET)) + is_repository = d.pop("isRepository", UNSET) def _parse_repository_type(data: object) -> None | str | Unset: @@ -154,6 +172,8 @@ def _parse_parent_graph_id(data: object) -> None | str | Unset: role=role, is_selected=is_selected, created_at=created_at, + description=description, + tags=tags, is_repository=is_repository, repository_type=repository_type, schema_extensions=schema_extensions, diff --git a/robosystems_client/models/graph_metadata_result.py b/robosystems_client/models/graph_metadata_result.py new file mode 100644 index 0000000..334b172 --- /dev/null +++ b/robosystems_client/models/graph_metadata_result.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="GraphMetadataResult") + + +@_attrs_define +class GraphMetadataResult: + """Result payload for the update-graph-metadata operation. + + Attributes: + graph_id (str): Graph the metadata belongs to + graph_name (str): Display name after the update + description (str | Unset): Description after the update ('' when unset) Default: ''. + tags (list[str] | Unset): Tags after the update (empty when unset) + updated_fields (list[str] | Unset): Fields this call actually changed. Empty when the submitted values already + matched what was stored. + """ + + graph_id: str + graph_name: str + description: str | Unset = "" + tags: list[str] | Unset = UNSET + updated_fields: list[str] | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + graph_id = self.graph_id + + graph_name = self.graph_name + + description = self.description + + tags: list[str] | Unset = UNSET + if not isinstance(self.tags, Unset): + tags = self.tags + + updated_fields: list[str] | Unset = UNSET + if not isinstance(self.updated_fields, Unset): + updated_fields = self.updated_fields + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "graph_id": graph_id, + "graph_name": graph_name, + } + ) + if description is not UNSET: + field_dict["description"] = description + if tags is not UNSET: + field_dict["tags"] = tags + if updated_fields is not UNSET: + field_dict["updated_fields"] = updated_fields + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + graph_id = d.pop("graph_id") + + graph_name = d.pop("graph_name") + + description = d.pop("description", UNSET) + + tags = cast(list[str], d.pop("tags", UNSET)) + + updated_fields = cast(list[str], d.pop("updated_fields", UNSET)) + + graph_metadata_result = cls( + graph_id=graph_id, + graph_name=graph_name, + description=description, + tags=tags, + updated_fields=updated_fields, + ) + + graph_metadata_result.additional_properties = d + return graph_metadata_result + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/robosystems_client/models/operation_envelope_graph_metadata_result.py b/robosystems_client/models/operation_envelope_graph_metadata_result.py new file mode 100644 index 0000000..fcfd1df --- /dev/null +++ b/robosystems_client/models/operation_envelope_graph_metadata_result.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..models.operation_envelope_graph_metadata_result_status import ( + OperationEnvelopeGraphMetadataResultStatus, +) +from ..types import UNSET, Unset + +if TYPE_CHECKING: + from ..models.graph_metadata_result import GraphMetadataResult + + +T = TypeVar("T", bound="OperationEnvelopeGraphMetadataResult") + + +@_attrs_define +class OperationEnvelopeGraphMetadataResult: + """ + Attributes: + operation (str): Kebab-case operation name + operation_id (str): op_-prefixed ULID for audit and SSE correlation + status (OperationEnvelopeGraphMetadataResultStatus): Operation lifecycle state + at (str): ISO-8601 UTC timestamp + result (GraphMetadataResult | None | Unset): Command-specific result payload + created_by (None | str | Unset): User ID that initiated the operation (null for legacy callers) + idempotent_replay (bool | Unset): True when this envelope came from the idempotency cache — the underlying + command did not execute again. False on fresh executions. Default: False. + """ + + operation: str + operation_id: str + status: OperationEnvelopeGraphMetadataResultStatus + at: str + result: GraphMetadataResult | None | Unset = UNSET + created_by: None | str | Unset = UNSET + idempotent_replay: bool | Unset = False + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + from ..models.graph_metadata_result import GraphMetadataResult + + operation = self.operation + + operation_id = self.operation_id + + status = self.status.value + + at = self.at + + result: dict[str, Any] | None | Unset + if isinstance(self.result, Unset): + result = UNSET + elif isinstance(self.result, GraphMetadataResult): + result = self.result.to_dict() + else: + result = self.result + + created_by: None | str | Unset + if isinstance(self.created_by, Unset): + created_by = UNSET + else: + created_by = self.created_by + + idempotent_replay = self.idempotent_replay + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "operation": operation, + "operationId": operation_id, + "status": status, + "at": at, + } + ) + if result is not UNSET: + field_dict["result"] = result + if created_by is not UNSET: + field_dict["createdBy"] = created_by + if idempotent_replay is not UNSET: + field_dict["idempotentReplay"] = idempotent_replay + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.graph_metadata_result import GraphMetadataResult + + d = dict(src_dict) + operation = d.pop("operation") + + operation_id = d.pop("operationId") + + status = OperationEnvelopeGraphMetadataResultStatus(d.pop("status")) + + at = d.pop("at") + + def _parse_result(data: object) -> GraphMetadataResult | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + result_type_0 = GraphMetadataResult.from_dict(data) + + return result_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(GraphMetadataResult | None | Unset, data) + + result = _parse_result(d.pop("result", UNSET)) + + def _parse_created_by(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + created_by = _parse_created_by(d.pop("createdBy", UNSET)) + + idempotent_replay = d.pop("idempotentReplay", UNSET) + + operation_envelope_graph_metadata_result = cls( + operation=operation, + operation_id=operation_id, + status=status, + at=at, + result=result, + created_by=created_by, + idempotent_replay=idempotent_replay, + ) + + operation_envelope_graph_metadata_result.additional_properties = d + return operation_envelope_graph_metadata_result + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/robosystems_client/models/operation_envelope_graph_metadata_result_status.py b/robosystems_client/models/operation_envelope_graph_metadata_result_status.py new file mode 100644 index 0000000..1c2fcf1 --- /dev/null +++ b/robosystems_client/models/operation_envelope_graph_metadata_result_status.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class OperationEnvelopeGraphMetadataResultStatus(str, Enum): + COMPLETED = "completed" + FAILED = "failed" + PENDING = "pending" + + def __str__(self) -> str: + return str(self.value) diff --git a/robosystems_client/models/update_graph_metadata_op.py b/robosystems_client/models/update_graph_metadata_op.py new file mode 100644 index 0000000..ca0c49e --- /dev/null +++ b/robosystems_client/models/update_graph_metadata_op.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="UpdateGraphMetadataOp") + + +@_attrs_define +class UpdateGraphMetadataOp: + """Body for the update-graph-metadata operation. + + Partial update — only supplied (non-null) fields change, so a caller + editing just the display name need not resend the description and tags. + Because ``None`` means "leave alone", clearing a field uses its empty + value instead: pass ``""`` to clear the description and ``[]`` to clear + the tags. ``graph_name`` cannot be cleared; it is the graph's label + everywhere it is listed. + + This is the platform-level label for the graph, independent of the + entity name shown on financial statements — change that through + ``POST /extensions/roboledger/{graph_id}/operations/update-entity``. + + Attributes: + graph_name (None | str | Unset): New display name. Omit to leave unchanged; cannot be cleared. + description (None | str | Unset): New description. Omit to leave unchanged; pass '' to clear. + tags (list[str] | None | Unset): Replaces the full tag list (not a merge). Omit to leave unchanged; pass [] to + clear. Tags are trimmed, de-duplicated, and capped at 50 characters each. + """ + + graph_name: None | str | Unset = UNSET + description: None | str | Unset = UNSET + tags: list[str] | None | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + graph_name: None | str | Unset + if isinstance(self.graph_name, Unset): + graph_name = UNSET + else: + graph_name = self.graph_name + + description: None | str | Unset + if isinstance(self.description, Unset): + description = UNSET + else: + description = self.description + + tags: list[str] | None | Unset + if isinstance(self.tags, Unset): + tags = UNSET + elif isinstance(self.tags, list): + tags = self.tags + + else: + tags = self.tags + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if graph_name is not UNSET: + field_dict["graph_name"] = graph_name + if description is not UNSET: + field_dict["description"] = description + if tags is not UNSET: + field_dict["tags"] = tags + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + + def _parse_graph_name(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + graph_name = _parse_graph_name(d.pop("graph_name", UNSET)) + + def _parse_description(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + description = _parse_description(d.pop("description", UNSET)) + + def _parse_tags(data: object) -> list[str] | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, list): + raise TypeError() + tags_type_0 = cast(list[str], data) + + return tags_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(list[str] | None | Unset, data) + + tags = _parse_tags(d.pop("tags", UNSET)) + + update_graph_metadata_op = cls( + graph_name=graph_name, + description=description, + tags=tags, + ) + + update_graph_metadata_op.additional_properties = d + return update_graph_metadata_op + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties