From 72e061279f46cc2ef38d366e922c20004e33028c Mon Sep 17 00:00:00 2001 From: Mendy Man Date: Thu, 16 Jan 2025 11:17:27 -0500 Subject: [PATCH] Add python api_resource template --- .../svix-lib-python/api_resource.py.jinja | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 templates/svix-lib-python/api_resource.py.jinja diff --git a/templates/svix-lib-python/api_resource.py.jinja b/templates/svix-lib-python/api_resource.py.jinja new file mode 100644 index 0000000..d164b15 --- /dev/null +++ b/templates/svix-lib-python/api_resource.py.jinja @@ -0,0 +1,124 @@ +{% set api_mod_name %}{{ resource.name | to_snake_case }}_api{% endset -%} +{% set resource_class_name = resource.name | to_upper_camel_case -%} +{% set resource_type_name = resource.name | to_upper_camel_case -%} + +import typing as t +from datetime import datetime +from dataclasses import dataclass,asdict + +from deprecated import deprecated +from .common import PostOptions, ApiBase, BaseOptions +from ..internal.openapi_client.client import AuthenticatedClient +from ..internal.openapi_client import models + +{% if resource.operations | length > 0 %} + +from ..internal.openapi_client.api.{{ resource.name | to_snake_case }} import ( + {% for op in resource.operations %} + {{ op.id | to_snake_case }}, + {%- endfor %} +) + {% for op in resource.operations -%} + {#- + FIXME: this is a hack. i want to be able to export the resource types from this file so i have to import them (not just use models.Resource) + But this will result in duplicate import statements. ruff check --fix will remove them, but ideally we find a better way to do this + -#} + {%- if op.request_body_schema_name is defined %} +from ..internal.openapi_client.models.{{ op.request_body_schema_name | to_snake_case }} import {{ op.request_body_schema_name | to_upper_camel_case }} + {%- endif -%} + {%- if op.response_body_schema_name is defined %} +from ..internal.openapi_client.models.{{ op.response_body_schema_name | to_snake_case }} import {{ op.response_body_schema_name | to_upper_camel_case }} + {%- endif -%} + {% endfor %} +{% endif %} + + +{# FIXME: need to understand how this template generates newlines and stop generating newlines #} +{% for op in resource.operations %} + {% if op.query_params | length > 0 or op.header_params | length > 0 %} +@dataclass +class {{ resource_type_name }}{{ op.name | to_upper_camel_case }}Options(BaseOptions): + {%- for p in op.query_params %} + {%- if p.description is defined %} + # {{ p.description }} + {%- endif %} + {%- if p.required %} + {{ p.name }}: {{ p.type.to_python() }} + {%- else %} + {{ p.name }}: t.Optional[{{ p.type.to_python() }}] = None + {%- endif %} + {%- endfor %} + {# FIXME: hardcoding idempotency-key for now since that is the only header param #} + {%- if op.header_params | length > 0 %} + idempotency_key: t.Optional[str] = None + {%- endif %} + {%- endif %} +{%- endfor %} + + +{%- 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 op in resource.operations %} + {# FIXME: find a better way to deal with python's reserved keywords!, for now i do this hack #} + {%- if op.name == "import" %} + {%- set op_name = "import_" %} + {%- else %} + {%- set op_name = op.name | to_snake_case %} + {%- endif %} + {% if op.deprecated %} + @deprecated + {%- endif %} + {%- if is_async -%} + {%- set func_def = "async def" %} + {%- else %} + {%- set func_def = "def" %} + {%- endif %} + {{ func_def }} {{ op_name }}(self + {#- path parameters are non optional strings #} + {% for p in op.path_params -%} + ,{{ p }}: str + {% endfor -%} + {# body parameter struct #} + {%- if op.request_body_schema_name is defined %} + ,{{ op.request_body_schema_name | to_snake_case }}: {{ op.request_body_schema_name }} + {%- endif %} + + {# add query_options type #} + {%- if op.query_params | length > 0 or op.header_params | length > 0 %} + ,options: {{ resource_type_name }}{{ op.name | to_upper_camel_case }}Options = {{ resource_type_name }}{{ op.name | to_upper_camel_case }}Options() + {%- endif %} + + ) -> + {%- if op.response_body_schema_name is defined -%} + {{ op.response_body_schema_name | to_upper_camel_case }}: + {% else -%} + None: + {%- endif %} + {% if op.description is defined -%} + {{ op.description | to_doc_comment(style="python") }} + {%- endif -%} + {%- set internal_func_name -%} + {{ op.id | to_snake_case }}{% if is_async -%}.request_asyncio{% else -%}.request_sync{% endif -%} + {%- endset -%} + {% set ret -%} + {%- if is_async -%}return await{% else %}return{% endif -%} + {%- endset %} + {{ ret }} {{ internal_func_name }}(client=self._client + {%- for p in op.path_params -%} +,{{ p }}={{ p }} + {%- endfor -%} + {% if op.request_body_schema_name is defined -%} +,json_body={{ op.request_body_schema_name | to_snake_case }} + {% endif -%} + {% if (op.query_params | length > 0 )or (op.header_params | length > 0) %} + {# FIXME: how do we know there is no duplicate options in the path params #} +,**options.to_dict() + {% endif -%} +) + {% endfor -%} + {% else %} + {# empty class with no functions, so we have a pass here #} + pass + {% endif %} +{% endfor %}