From 9bf861021fae32907526d245f2199adee45900a1 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 21 Mar 2025 15:49:46 +0100 Subject: [PATCH 1/3] python: Update api_resource template to support sub-resources --- python/templates/api_resource.py.jinja | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/python/templates/api_resource.py.jinja b/python/templates/api_resource.py.jinja index f06a35f70..47d8e5952 100644 --- a/python/templates/api_resource.py.jinja +++ b/python/templates/api_resource.py.jinja @@ -11,6 +11,12 @@ from .. import models {% for c in referenced_components -%} from ..models import {{ c | to_upper_camel_case }} +{% endfor -%} +{% for _, sub in resource.subresources | items -%} +from .{{ sub.name | to_snake_case }} import ( + {{ sub.name | to_upper_camel_case }}, + {{ sub.name | to_upper_camel_case }}Async, +) {% endfor %} @@ -65,7 +71,15 @@ class {{ resource_type_name }}{{ op.name | to_upper_camel_case }}Options(BaseOpt {% for is_async in [true, false] %} class {{ resource.name | to_upper_camel_case }}{% if is_async %}Async{% endif %}(ApiBase): - {%- if resource.operations | length != 0 %} + {%- for name, sub in resource.subresources | items %} + {%- set sub_type_name %}{{ sub.name | to_upper_camel_case }}{% if is_async %}Async{% endif %}{% endset %} + @property + def {{ name | to_snake_case }}(self) -> {{ sub_type_name }}: + return {{ sub_type_name }}(self._client) + + {% endfor %} + + {%- if resource.operations | length > 0 %} {%- for op in resource.operations %} {% if op.response_body_schema_name is defined -%} {% set return_type = op.response_body_schema_name | to_upper_camel_case -%} @@ -142,8 +156,9 @@ class {{ resource.name | to_upper_camel_case }}{% if is_async %}Async{% endif %} {{ code | indent(4, true) }} {% endfor -%} - {% else %} - {# empty class with no functions, so we have a pass here #} + {% endif %} + {% if resource.subresources | length == 0 and resource.operations | length == 0 %} + {# empty class with no members, so we have a pass here -#} pass {% endif %} {% endfor %} From c4709aaf71beca8e13dec2f17ba207976b13672b Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 21 Mar 2025 16:24:34 +0100 Subject: [PATCH 2/3] python: Add OperationalWebhook resource --- codegen.toml | 2 +- python/svix/api/operational_webhook.py | 18 ++++++++++++++++++ python/svix/api/svix.py | 9 +++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 python/svix/api/operational_webhook.py diff --git a/codegen.toml b/codegen.toml index 0b1b8ef88..08a767fac 100644 --- a/codegen.toml +++ b/codegen.toml @@ -61,7 +61,7 @@ output_dir = "svix-cli/src/cmds/api" [python] template_dir = "python/templates" extra_shell_commands = [ - "rm python/svix/api/{environment,health,ingest,operational_webhook}.py", + "rm python/svix/api/{environment,health,ingest}.py", ] [[python.task]] template = "python/templates/api_resource.py.jinja" diff --git a/python/svix/api/operational_webhook.py b/python/svix/api/operational_webhook.py new file mode 100644 index 000000000..7ffe73bdf --- /dev/null +++ b/python/svix/api/operational_webhook.py @@ -0,0 +1,18 @@ +# This file is @generated +from .common import ApiBase +from .operational_webhook_endpoint import ( + OperationalWebhookEndpoint, + OperationalWebhookEndpointAsync, +) + + +class OperationalWebhookAsync(ApiBase): + @property + def endpoint(self) -> OperationalWebhookEndpointAsync: + return OperationalWebhookEndpointAsync(self._client) + + +class OperationalWebhook(ApiBase): + @property + def endpoint(self) -> OperationalWebhookEndpoint: + return OperationalWebhookEndpoint(self._client) diff --git a/python/svix/api/svix.py b/python/svix/api/svix.py index 06e8e66a8..3aa0b8e2f 100644 --- a/python/svix/api/svix.py +++ b/python/svix/api/svix.py @@ -8,6 +8,7 @@ from .integration import Integration, IntegrationAsync from .message import Message, MessageAsync from .message_attempt import MessageAttempt, MessageAttemptAsync +from .operational_webhook import OperationalWebhook, OperationalWebhookAsync from .operational_webhook_endpoint import ( OperationalWebhookEndpoint, OperationalWebhookEndpointAsync, @@ -107,6 +108,10 @@ def message_attempt(self) -> MessageAttemptAsync: def statistics(self) -> StatisticsAsync: return StatisticsAsync(self._client) + @property + def operational_webhook(self) -> OperationalWebhookAsync: + return OperationalWebhookAsync(self._client) + @property def operational_webhook_endpoint(self) -> OperationalWebhookEndpointAsync: return OperationalWebhookEndpointAsync(self._client) @@ -145,6 +150,10 @@ def message_attempt(self) -> MessageAttempt: def statistics(self) -> Statistics: return Statistics(self._client) + @property + def operational_webhook(self) -> OperationalWebhook: + return OperationalWebhook(self._client) + @property def operational_webhook_endpoint(self) -> OperationalWebhookEndpoint: return OperationalWebhookEndpoint(self._client) From aa5b8b185b0430706d6f128f4fa756234c4979eb Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 21 Mar 2025 16:27:03 +0100 Subject: [PATCH 3/3] python: Add Environment resource --- codegen.toml | 2 +- python/svix/api/environment.py | 94 ++++++++++++++++++++++++++++++++++ python/svix/api/svix.py | 9 ++++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 python/svix/api/environment.py diff --git a/codegen.toml b/codegen.toml index 08a767fac..481f008fe 100644 --- a/codegen.toml +++ b/codegen.toml @@ -61,7 +61,7 @@ output_dir = "svix-cli/src/cmds/api" [python] template_dir = "python/templates" extra_shell_commands = [ - "rm python/svix/api/{environment,health,ingest}.py", + "rm python/svix/api/{health,ingest}.py", ] [[python.task]] template = "python/templates/api_resource.py.jinja" diff --git a/python/svix/api/environment.py b/python/svix/api/environment.py new file mode 100644 index 000000000..69615daf9 --- /dev/null +++ b/python/svix/api/environment.py @@ -0,0 +1,94 @@ +# This file is @generated +import typing as t +from dataclasses import dataclass + +from ..models import EnvironmentIn, EnvironmentOut +from .common import ApiBase, BaseOptions, serialize_params + + +@dataclass +class EnvironmentExportOptions(BaseOptions): + idempotency_key: t.Optional[str] = None + + def _header_params(self) -> t.Dict[str, str]: + return serialize_params( + { + "idempotency-key": self.idempotency_key, + } + ) + + +@dataclass +class EnvironmentImportOptions(BaseOptions): + idempotency_key: t.Optional[str] = None + + def _header_params(self) -> t.Dict[str, str]: + return serialize_params( + { + "idempotency-key": self.idempotency_key, + } + ) + + +class EnvironmentAsync(ApiBase): + async def export( + self, options: EnvironmentExportOptions = EnvironmentExportOptions() + ) -> EnvironmentOut: + """Download a JSON file containing all org-settings and event types.""" + response = await self._request_asyncio( + method="post", + path="/api/v1/environment/export", + path_params={}, + query_params=options._query_params(), + header_params=options._header_params(), + ) + return EnvironmentOut.model_validate(response.json()) + + async def import_( + self, + environment_in: EnvironmentIn, + options: EnvironmentImportOptions = EnvironmentImportOptions(), + ) -> None: + """Import a configuration into the active organization. + + It doesn't delete anything, only adds / updates what was passed to it.""" + await self._request_asyncio( + method="post", + path="/api/v1/environment/import", + path_params={}, + query_params=options._query_params(), + header_params=options._header_params(), + json_body=environment_in.model_dump_json(exclude_unset=True, by_alias=True), + ) + + +class Environment(ApiBase): + def export( + self, options: EnvironmentExportOptions = EnvironmentExportOptions() + ) -> EnvironmentOut: + """Download a JSON file containing all org-settings and event types.""" + response = self._request_sync( + method="post", + path="/api/v1/environment/export", + path_params={}, + query_params=options._query_params(), + header_params=options._header_params(), + ) + return EnvironmentOut.model_validate(response.json()) + + def import_( + self, + environment_in: EnvironmentIn, + options: EnvironmentImportOptions = EnvironmentImportOptions(), + ) -> None: + """Import a configuration into the active organization. + + It doesn't delete anything, only adds / updates what was passed to it.""" + self._request_sync( + method="post", + path="/api/v1/environment/import", + path_params={}, + query_params=options._query_params(), + header_params=options._header_params(), + json_body=environment_in.model_dump_json(exclude_unset=True, by_alias=True), + ) diff --git a/python/svix/api/svix.py b/python/svix/api/svix.py index 3aa0b8e2f..4b848011b 100644 --- a/python/svix/api/svix.py +++ b/python/svix/api/svix.py @@ -4,6 +4,7 @@ from .application import Application, ApplicationAsync from .authentication import Authentication, AuthenticationAsync from .endpoint import Endpoint, EndpointAsync +from .environment import Environment, EnvironmentAsync from .event_type import EventType, EventTypeAsync from .integration import Integration, IntegrationAsync from .message import Message, MessageAsync @@ -88,6 +89,10 @@ def application(self) -> ApplicationAsync: def endpoint(self) -> EndpointAsync: return EndpointAsync(self._client) + @property + def environment(self) -> EnvironmentAsync: + return EnvironmentAsync(self._client) + @property def event_type(self) -> EventTypeAsync: return EventTypeAsync(self._client) @@ -130,6 +135,10 @@ def application(self) -> Application: def endpoint(self) -> Endpoint: return Endpoint(self._client) + @property + def environment(self) -> Environment: + return Environment(self._client) + @property def event_type(self) -> EventType: return EventType(self._client)