diff --git a/openapi python client generation/README.md b/openapi python client generation/README.md new file mode 100644 index 0000000..def42d6 --- /dev/null +++ b/openapi python client generation/README.md @@ -0,0 +1,5 @@ +[Reference Link](https://github.com/ajaishankar/openapi-typescript-fetch) + +#### Steps to run the demo +- Follow Backend Readme +- Follow Frontend Readme diff --git a/openapi python client generation/backend/README.md b/openapi python client generation/backend/README.md new file mode 100644 index 0000000..0c8001a --- /dev/null +++ b/openapi python client generation/backend/README.md @@ -0,0 +1,39 @@ +[Reference Link](https://www.django-rest-framework.org/api-guide/schemas/) + +#### Steps + +- Create virtual env + + ```sh + python -m venv .venv + ``` + +- Activate virtual env + + ```sh + source .venv/bin/activate + ``` + +- Install required dependencies + + ```sh + pip install -r requirements.txt + ``` + +- Generate Open Api Schema File + + ```sh + python manage.py generateschema --file 'openapi-schema.yml' + ``` + +- Run Backend Server + + ```sh + python manage.py runserver + ``` + += Generate OpenAPI Client + + ```sh + openapi-python-client generate --path openapi-schema.yml --meta none --fail-on-warning --output-path ../frontend/client + ``` \ No newline at end of file diff --git a/openapi python client generation/backend/core/__init__.py b/openapi python client generation/backend/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openapi python client generation/backend/core/admin.py b/openapi python client generation/backend/core/admin.py new file mode 100644 index 0000000..201d949 --- /dev/null +++ b/openapi python client generation/backend/core/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from .models import Student + +# Register your models here. + + +@admin.register(Student) +class StudentAdmin(admin.ModelAdmin): + list_display = ['id', 'name', 'roll', 'city'] diff --git a/openapi python client generation/backend/core/apps.py b/openapi python client generation/backend/core/apps.py new file mode 100644 index 0000000..8115ae6 --- /dev/null +++ b/openapi python client generation/backend/core/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'core' diff --git a/openapi python client generation/backend/core/migrations/0001_initial.py b/openapi python client generation/backend/core/migrations/0001_initial.py new file mode 100644 index 0000000..2916ad6 --- /dev/null +++ b/openapi python client generation/backend/core/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.5 on 2022-06-15 18:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Student', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ('roll', models.IntegerField()), + ('city', models.CharField(max_length=50)), + ], + ), + ] diff --git a/openapi python client generation/backend/core/migrations/__init__.py b/openapi python client generation/backend/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openapi python client generation/backend/core/models.py b/openapi python client generation/backend/core/models.py new file mode 100644 index 0000000..1bcb589 --- /dev/null +++ b/openapi python client generation/backend/core/models.py @@ -0,0 +1,9 @@ +from django.db import models + +# Create your models here. + + +class Student(models.Model): + name = models.CharField(max_length=50) + roll = models.IntegerField() + city = models.CharField(max_length=50) diff --git a/openapi python client generation/backend/core/serializers.py b/openapi python client generation/backend/core/serializers.py new file mode 100644 index 0000000..1c7491e --- /dev/null +++ b/openapi python client generation/backend/core/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers +from .models import Student + + +class StudentSerializer(serializers.ModelSerializer): + class Meta: + model = Student + fields = ['id', 'name', 'roll', 'city'] diff --git a/openapi python client generation/backend/core/tests.py b/openapi python client generation/backend/core/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/openapi python client generation/backend/core/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/openapi python client generation/backend/core/views.py b/openapi python client generation/backend/core/views.py new file mode 100644 index 0000000..3b0a1f2 --- /dev/null +++ b/openapi python client generation/backend/core/views.py @@ -0,0 +1,10 @@ +from .models import Student +from .serializers import StudentSerializer +from rest_framework import viewsets + +# Create your views here. + + +class StudentModelViewSet(viewsets.ModelViewSet): + queryset = Student.objects.all() + serializer_class = StudentSerializer diff --git a/openapi python client generation/backend/db.sqlite3 b/openapi python client generation/backend/db.sqlite3 new file mode 100644 index 0000000..e8be118 Binary files /dev/null and b/openapi python client generation/backend/db.sqlite3 differ diff --git a/openapi python client generation/backend/demo/__init__.py b/openapi python client generation/backend/demo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openapi python client generation/backend/demo/asgi.py b/openapi python client generation/backend/demo/asgi.py new file mode 100644 index 0000000..88d282d --- /dev/null +++ b/openapi python client generation/backend/demo/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for demo project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo.settings') + +application = get_asgi_application() diff --git a/openapi python client generation/backend/demo/settings.py b/openapi python client generation/backend/demo/settings.py new file mode 100644 index 0000000..1a3142e --- /dev/null +++ b/openapi python client generation/backend/demo/settings.py @@ -0,0 +1,129 @@ +""" +Django settings for demo project. + +Generated by 'django-admin startproject' using Django 4.0.5. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure---$ooz2ucyo3k_$vv*-r3_^3g145e-f4w=q8ckh6^=kb0fivr=' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'corsheaders', + 'core', +] + +MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'demo.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'demo.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +CORS_ORIGIN_ALLOW_ALL = True \ No newline at end of file diff --git a/openapi python client generation/backend/demo/urls.py b/openapi python client generation/backend/demo/urls.py new file mode 100644 index 0000000..1bc0f75 --- /dev/null +++ b/openapi python client generation/backend/demo/urls.py @@ -0,0 +1,38 @@ +"""demo URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include +from core import views +from rest_framework.routers import DefaultRouter +from rest_framework.schemas import get_schema_view +# Creating Router Object +router = DefaultRouter() + +# Register StudentModelViewSet With Router +router.register('studentapi', views.StudentModelViewSet, basename='student') + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include(router.urls)), + # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs. + # * `title` and `description` parameters are passed to `SchemaGenerator`. + # * Provide view name for use with `reverse()`. + path('openapi/', get_schema_view( + title="Your Project", + description="API for all things …", + version="1.0.0" + ), name='openapi-schema'), +] diff --git a/openapi python client generation/backend/demo/wsgi.py b/openapi python client generation/backend/demo/wsgi.py new file mode 100644 index 0000000..bcc17f3 --- /dev/null +++ b/openapi python client generation/backend/demo/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for demo project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo.settings') + +application = get_wsgi_application() diff --git a/openapi python client generation/backend/manage.py b/openapi python client generation/backend/manage.py new file mode 100644 index 0000000..d4cdb72 --- /dev/null +++ b/openapi python client generation/backend/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/openapi python client generation/backend/openapi-schema.yml b/openapi python client generation/backend/openapi-schema.yml new file mode 100644 index 0000000..8135c43 --- /dev/null +++ b/openapi python client generation/backend/openapi-schema.yml @@ -0,0 +1,160 @@ +openapi: 3.0.2 +info: + title: '' + version: '' +paths: + /studentapi/: + get: + operationId: listStudents + description: '' + parameters: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Student' + description: '' + tags: + - studentapi + post: + operationId: createStudent + description: '' + parameters: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Student' + multipart/form-data: + schema: + $ref: '#/components/schemas/Student' + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + description: '' + tags: + - studentapi + /studentapi/{id}/: + get: + operationId: retrieveStudent + description: '' + parameters: + - name: id + in: path + required: true + description: A unique integer value identifying this student. + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + description: '' + tags: + - studentapi + put: + operationId: updateStudent + description: '' + parameters: + - name: id + in: path + required: true + description: A unique integer value identifying this student. + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Student' + multipart/form-data: + schema: + $ref: '#/components/schemas/Student' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + description: '' + tags: + - studentapi + patch: + operationId: partialUpdateStudent + description: '' + parameters: + - name: id + in: path + required: true + description: A unique integer value identifying this student. + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Student' + multipart/form-data: + schema: + $ref: '#/components/schemas/Student' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + description: '' + tags: + - studentapi + delete: + operationId: destroyStudent + description: '' + parameters: + - name: id + in: path + required: true + description: A unique integer value identifying this student. + schema: + type: string + responses: + '204': + description: '' + tags: + - studentapi +components: + schemas: + Student: + type: object + properties: + id: + type: integer + readOnly: true + name: + type: string + maxLength: 50 + roll: + type: integer + city: + type: string + maxLength: 50 + required: + - name + - roll + - city diff --git a/openapi python client generation/backend/requirements.txt b/openapi python client generation/backend/requirements.txt new file mode 100644 index 0000000..0843c67 --- /dev/null +++ b/openapi python client generation/backend/requirements.txt @@ -0,0 +1,6 @@ +Django==4.0.5 +djangorestframework==3.13.1 +PyYAML==6.0.2 +uritemplate==4.1.1 +django-cors-headers==3.13.0 +openapi-python-client==0.13.2 \ No newline at end of file diff --git a/openapi python client generation/frontend/client/__init__.py b/openapi python client generation/frontend/client/__init__.py new file mode 100644 index 0000000..15a3454 --- /dev/null +++ b/openapi python client generation/frontend/client/__init__.py @@ -0,0 +1,8 @@ +"""A client library for accessing""" + +from .client import AuthenticatedClient, Client + +__all__ = ( + "AuthenticatedClient", + "Client", +) diff --git a/openapi python client generation/frontend/client/api/__init__.py b/openapi python client generation/frontend/client/api/__init__.py new file mode 100644 index 0000000..81f9fa2 --- /dev/null +++ b/openapi python client generation/frontend/client/api/__init__.py @@ -0,0 +1 @@ +"""Contains methods for accessing the API""" diff --git a/openapi python client generation/frontend/client/api/studentapi/__init__.py b/openapi python client generation/frontend/client/api/studentapi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openapi python client generation/frontend/client/api/studentapi/create_student.py b/openapi python client generation/frontend/client/api/studentapi/create_student.py new file mode 100644 index 0000000..39178a8 --- /dev/null +++ b/openapi python client generation/frontend/client/api/studentapi/create_student.py @@ -0,0 +1,194 @@ +from http import HTTPStatus +from typing import Any, Dict, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.student import Student +from ...types import Response + + +def _get_kwargs( + *, + body: Union[ + Student, + Student, + Student, + ], +) -> Dict[str, Any]: + headers: Dict[str, Any] = {} + + _kwargs: Dict[str, Any] = { + "method": "post", + "url": "/studentapi/", + } + + if isinstance(body, Student): + _json_body = body.to_dict() + + _kwargs["json"] = _json_body + headers["Content-Type"] = "application/json" + if isinstance(body, Student): + _data_body = body.to_dict() + + _kwargs["data"] = _data_body + headers["Content-Type"] = "application/x-www-form-urlencoded" + if isinstance(body, Student): + _files_body = body.to_multipart() + + _kwargs["files"] = _files_body + headers["Content-Type"] = "multipart/form-data" + + _kwargs["headers"] = headers + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Student]: + if response.status_code == 201: + response_201 = Student.from_dict(response.json()) + + return response_201 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[Student]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Response[Student]: + """ + Args: + body (Student): + body (Student): + body (Student): + + 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[Student] + """ + + kwargs = _get_kwargs( + body=body, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Optional[Student]: + """ + Args: + body (Student): + body (Student): + body (Student): + + 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: + Student + """ + + return sync_detailed( + client=client, + body=body, + ).parsed + + +async def asyncio_detailed( + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Response[Student]: + """ + Args: + body (Student): + body (Student): + body (Student): + + 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[Student] + """ + + kwargs = _get_kwargs( + body=body, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Optional[Student]: + """ + Args: + body (Student): + body (Student): + body (Student): + + 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: + Student + """ + + return ( + await asyncio_detailed( + client=client, + body=body, + ) + ).parsed diff --git a/openapi python client generation/frontend/client/api/studentapi/destroy_student.py b/openapi python client generation/frontend/client/api/studentapi/destroy_student.py new file mode 100644 index 0000000..00fd623 --- /dev/null +++ b/openapi python client generation/frontend/client/api/studentapi/destroy_student.py @@ -0,0 +1,97 @@ +from http import HTTPStatus +from typing import Any, Dict, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...types import Response + + +def _get_kwargs( + id: str, +) -> Dict[str, Any]: + _kwargs: Dict[str, Any] = { + "method": "delete", + "url": "/studentapi/{id}/".format( + id=id, + ), + } + + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Any]: + if response.status_code == 204: + return None + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[Any]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + id: str, + *, + client: Union[AuthenticatedClient, Client], +) -> Response[Any]: + """ + Args: + id (str): + + 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[Any] + """ + + kwargs = _get_kwargs( + id=id, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +async def asyncio_detailed( + id: str, + *, + client: Union[AuthenticatedClient, Client], +) -> Response[Any]: + """ + Args: + id (str): + + 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[Any] + """ + + kwargs = _get_kwargs( + id=id, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) diff --git a/openapi python client generation/frontend/client/api/studentapi/list_students.py b/openapi python client generation/frontend/client/api/studentapi/list_students.py new file mode 100644 index 0000000..9f25b24 --- /dev/null +++ b/openapi python client generation/frontend/client/api/studentapi/list_students.py @@ -0,0 +1,127 @@ +from http import HTTPStatus +from typing import Any, Dict, List, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.student import Student +from ...types import Response + + +def _get_kwargs() -> Dict[str, Any]: + _kwargs: Dict[str, Any] = { + "method": "get", + "url": "/studentapi/", + } + + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[List["Student"]]: + if response.status_code == 200: + response_200 = [] + _response_200 = response.json() + for response_200_item_data in _response_200: + response_200_item = Student.from_dict(response_200_item_data) + + response_200.append(response_200_item) + + return response_200 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[List["Student"]]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + *, + client: Union[AuthenticatedClient, Client], +) -> Response[List["Student"]]: + """ + 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[List['Student']] + """ + + kwargs = _get_kwargs() + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[List["Student"]]: + """ + 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: + List['Student'] + """ + + return sync_detailed( + client=client, + ).parsed + + +async def asyncio_detailed( + *, + client: Union[AuthenticatedClient, Client], +) -> Response[List["Student"]]: + """ + 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[List['Student']] + """ + + kwargs = _get_kwargs() + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[List["Student"]]: + """ + 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: + List['Student'] + """ + + return ( + await asyncio_detailed( + client=client, + ) + ).parsed diff --git a/openapi python client generation/frontend/client/api/studentapi/partial_update_student.py b/openapi python client generation/frontend/client/api/studentapi/partial_update_student.py new file mode 100644 index 0000000..162d6ee --- /dev/null +++ b/openapi python client generation/frontend/client/api/studentapi/partial_update_student.py @@ -0,0 +1,209 @@ +from http import HTTPStatus +from typing import Any, Dict, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.student import Student +from ...types import Response + + +def _get_kwargs( + id: str, + *, + body: Union[ + Student, + Student, + Student, + ], +) -> Dict[str, Any]: + headers: Dict[str, Any] = {} + + _kwargs: Dict[str, Any] = { + "method": "patch", + "url": "/studentapi/{id}/".format( + id=id, + ), + } + + if isinstance(body, Student): + _json_body = body.to_dict() + + _kwargs["json"] = _json_body + headers["Content-Type"] = "application/json" + if isinstance(body, Student): + _data_body = body.to_dict() + + _kwargs["data"] = _data_body + headers["Content-Type"] = "application/x-www-form-urlencoded" + if isinstance(body, Student): + _files_body = body.to_multipart() + + _kwargs["files"] = _files_body + headers["Content-Type"] = "multipart/form-data" + + _kwargs["headers"] = headers + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Student]: + if response.status_code == 200: + response_200 = Student.from_dict(response.json()) + + return response_200 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[Student]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + id: str, + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Response[Student]: + """ + Args: + id (str): + body (Student): + body (Student): + body (Student): + + 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[Student] + """ + + kwargs = _get_kwargs( + id=id, + body=body, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + id: str, + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Optional[Student]: + """ + Args: + id (str): + body (Student): + body (Student): + body (Student): + + 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: + Student + """ + + return sync_detailed( + id=id, + client=client, + body=body, + ).parsed + + +async def asyncio_detailed( + id: str, + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Response[Student]: + """ + Args: + id (str): + body (Student): + body (Student): + body (Student): + + 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[Student] + """ + + kwargs = _get_kwargs( + id=id, + body=body, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + id: str, + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Optional[Student]: + """ + Args: + id (str): + body (Student): + body (Student): + body (Student): + + 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: + Student + """ + + return ( + await asyncio_detailed( + id=id, + client=client, + body=body, + ) + ).parsed diff --git a/openapi python client generation/frontend/client/api/studentapi/retrieve_student.py b/openapi python client generation/frontend/client/api/studentapi/retrieve_student.py new file mode 100644 index 0000000..3d38fc3 --- /dev/null +++ b/openapi python client generation/frontend/client/api/studentapi/retrieve_student.py @@ -0,0 +1,148 @@ +from http import HTTPStatus +from typing import Any, Dict, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.student import Student +from ...types import Response + + +def _get_kwargs( + id: str, +) -> Dict[str, Any]: + _kwargs: Dict[str, Any] = { + "method": "get", + "url": "/studentapi/{id}/".format( + id=id, + ), + } + + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Student]: + if response.status_code == 200: + response_200 = Student.from_dict(response.json()) + + return response_200 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[Student]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + id: str, + *, + client: Union[AuthenticatedClient, Client], +) -> Response[Student]: + """ + Args: + id (str): + + 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[Student] + """ + + kwargs = _get_kwargs( + id=id, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + id: str, + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[Student]: + """ + Args: + id (str): + + 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: + Student + """ + + return sync_detailed( + id=id, + client=client, + ).parsed + + +async def asyncio_detailed( + id: str, + *, + client: Union[AuthenticatedClient, Client], +) -> Response[Student]: + """ + Args: + id (str): + + 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[Student] + """ + + kwargs = _get_kwargs( + id=id, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + id: str, + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[Student]: + """ + Args: + id (str): + + 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: + Student + """ + + return ( + await asyncio_detailed( + id=id, + client=client, + ) + ).parsed diff --git a/openapi python client generation/frontend/client/api/studentapi/update_student.py b/openapi python client generation/frontend/client/api/studentapi/update_student.py new file mode 100644 index 0000000..61dee79 --- /dev/null +++ b/openapi python client generation/frontend/client/api/studentapi/update_student.py @@ -0,0 +1,209 @@ +from http import HTTPStatus +from typing import Any, Dict, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.student import Student +from ...types import Response + + +def _get_kwargs( + id: str, + *, + body: Union[ + Student, + Student, + Student, + ], +) -> Dict[str, Any]: + headers: Dict[str, Any] = {} + + _kwargs: Dict[str, Any] = { + "method": "put", + "url": "/studentapi/{id}/".format( + id=id, + ), + } + + if isinstance(body, Student): + _json_body = body.to_dict() + + _kwargs["json"] = _json_body + headers["Content-Type"] = "application/json" + if isinstance(body, Student): + _data_body = body.to_dict() + + _kwargs["data"] = _data_body + headers["Content-Type"] = "application/x-www-form-urlencoded" + if isinstance(body, Student): + _files_body = body.to_multipart() + + _kwargs["files"] = _files_body + headers["Content-Type"] = "multipart/form-data" + + _kwargs["headers"] = headers + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Student]: + if response.status_code == 200: + response_200 = Student.from_dict(response.json()) + + return response_200 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[Student]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + id: str, + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Response[Student]: + """ + Args: + id (str): + body (Student): + body (Student): + body (Student): + + 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[Student] + """ + + kwargs = _get_kwargs( + id=id, + body=body, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + id: str, + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Optional[Student]: + """ + Args: + id (str): + body (Student): + body (Student): + body (Student): + + 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: + Student + """ + + return sync_detailed( + id=id, + client=client, + body=body, + ).parsed + + +async def asyncio_detailed( + id: str, + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Response[Student]: + """ + Args: + id (str): + body (Student): + body (Student): + body (Student): + + 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[Student] + """ + + kwargs = _get_kwargs( + id=id, + body=body, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + id: str, + *, + client: Union[AuthenticatedClient, Client], + body: Union[ + Student, + Student, + Student, + ], +) -> Optional[Student]: + """ + Args: + id (str): + body (Student): + body (Student): + body (Student): + + 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: + Student + """ + + return ( + await asyncio_detailed( + id=id, + client=client, + body=body, + ) + ).parsed diff --git a/openapi python client generation/frontend/client/client.py b/openapi python client generation/frontend/client/client.py new file mode 100644 index 0000000..eef9b71 --- /dev/null +++ b/openapi python client generation/frontend/client/client.py @@ -0,0 +1,286 @@ +import ssl +from typing import Any, Dict, Optional, Union + +import httpx +from attrs import define, evolve, field + + +@define +class Client: + """A class for keeping track of data related to the API + + The following are accepted as keyword arguments and will be used to construct httpx Clients internally: + + ``base_url``: The base URL for the API, all requests are made to a relative path to this URL + + ``cookies``: A dictionary of cookies to be sent with every request + + ``headers``: A dictionary of headers to be sent with every request + + ``timeout``: The maximum amount of a time a request can take. API functions will raise + httpx.TimeoutException if this is exceeded. + + ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production, + but can be set to False for testing purposes. + + ``follow_redirects``: Whether or not to follow redirects. Default value is False. + + ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor. + + + Attributes: + raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a + status code that was not documented in the source OpenAPI document. Can also be provided as a keyword + argument to the constructor. + """ + + raise_on_unexpected_status: bool = field(default=False, kw_only=True) + _base_url: str = field(alias="base_url") + _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _timeout: Optional[httpx.Timeout] = field( + default=None, kw_only=True, alias="timeout" + ) + _verify_ssl: Union[str, bool, ssl.SSLContext] = field( + default=True, kw_only=True, alias="verify_ssl" + ) + _follow_redirects: bool = field( + default=False, kw_only=True, alias="follow_redirects" + ) + _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _client: Optional[httpx.Client] = field(default=None, init=False) + _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + + def with_headers(self, headers: Dict[str, str]) -> "Client": + """Get a new client matching this one with additional headers""" + if self._client is not None: + self._client.headers.update(headers) + if self._async_client is not None: + self._async_client.headers.update(headers) + return evolve(self, headers={**self._headers, **headers}) + + def with_cookies(self, cookies: Dict[str, str]) -> "Client": + """Get a new client matching this one with additional cookies""" + if self._client is not None: + self._client.cookies.update(cookies) + if self._async_client is not None: + self._async_client.cookies.update(cookies) + return evolve(self, cookies={**self._cookies, **cookies}) + + def with_timeout(self, timeout: httpx.Timeout) -> "Client": + """Get a new client matching this one with a new timeout (in seconds)""" + if self._client is not None: + self._client.timeout = timeout + if self._async_client is not None: + self._async_client.timeout = timeout + return evolve(self, timeout=timeout) + + def set_httpx_client(self, client: httpx.Client) -> "Client": + """Manually set the underlying httpx.Client + + **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. + """ + self._client = client + return self + + def get_httpx_client(self) -> httpx.Client: + """Get the underlying httpx.Client, constructing a new one if not previously set""" + if self._client is None: + self._client = httpx.Client( + base_url=self._base_url, + cookies=self._cookies, + headers=self._headers, + timeout=self._timeout, + verify=self._verify_ssl, + follow_redirects=self._follow_redirects, + **self._httpx_args, + ) + return self._client + + def __enter__(self) -> "Client": + """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" + self.get_httpx_client().__enter__() + return self + + def __exit__(self, *args: Any, **kwargs: Any) -> None: + """Exit a context manager for internal httpx.Client (see httpx docs)""" + self.get_httpx_client().__exit__(*args, **kwargs) + + def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client": + """Manually the underlying httpx.AsyncClient + + **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. + """ + self._async_client = async_client + return self + + def get_async_httpx_client(self) -> httpx.AsyncClient: + """Get the underlying httpx.AsyncClient, constructing a new one if not previously set""" + if self._async_client is None: + self._async_client = httpx.AsyncClient( + base_url=self._base_url, + cookies=self._cookies, + headers=self._headers, + timeout=self._timeout, + verify=self._verify_ssl, + follow_redirects=self._follow_redirects, + **self._httpx_args, + ) + return self._async_client + + async def __aenter__(self) -> "Client": + """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" + await self.get_async_httpx_client().__aenter__() + return self + + async def __aexit__(self, *args: Any, **kwargs: Any) -> None: + """Exit a context manager for underlying httpx.AsyncClient (see httpx docs)""" + await self.get_async_httpx_client().__aexit__(*args, **kwargs) + + +@define +class AuthenticatedClient: + """A Client which has been authenticated for use on secured endpoints + + The following are accepted as keyword arguments and will be used to construct httpx Clients internally: + + ``base_url``: The base URL for the API, all requests are made to a relative path to this URL + + ``cookies``: A dictionary of cookies to be sent with every request + + ``headers``: A dictionary of headers to be sent with every request + + ``timeout``: The maximum amount of a time a request can take. API functions will raise + httpx.TimeoutException if this is exceeded. + + ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production, + but can be set to False for testing purposes. + + ``follow_redirects``: Whether or not to follow redirects. Default value is False. + + ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor. + + + Attributes: + raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a + status code that was not documented in the source OpenAPI document. Can also be provided as a keyword + argument to the constructor. + token: The token to use for authentication + prefix: The prefix to use for the Authorization header + auth_header_name: The name of the Authorization header + """ + + raise_on_unexpected_status: bool = field(default=False, kw_only=True) + _base_url: str = field(alias="base_url") + _cookies: Dict[str, str] = field(factory=dict, kw_only=True, alias="cookies") + _headers: Dict[str, str] = field(factory=dict, kw_only=True, alias="headers") + _timeout: Optional[httpx.Timeout] = field( + default=None, kw_only=True, alias="timeout" + ) + _verify_ssl: Union[str, bool, ssl.SSLContext] = field( + default=True, kw_only=True, alias="verify_ssl" + ) + _follow_redirects: bool = field( + default=False, kw_only=True, alias="follow_redirects" + ) + _httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args") + _client: Optional[httpx.Client] = field(default=None, init=False) + _async_client: Optional[httpx.AsyncClient] = field(default=None, init=False) + + token: str + prefix: str = "Bearer" + auth_header_name: str = "Authorization" + + def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient": + """Get a new client matching this one with additional headers""" + if self._client is not None: + self._client.headers.update(headers) + if self._async_client is not None: + self._async_client.headers.update(headers) + return evolve(self, headers={**self._headers, **headers}) + + def with_cookies(self, cookies: Dict[str, str]) -> "AuthenticatedClient": + """Get a new client matching this one with additional cookies""" + if self._client is not None: + self._client.cookies.update(cookies) + if self._async_client is not None: + self._async_client.cookies.update(cookies) + return evolve(self, cookies={**self._cookies, **cookies}) + + def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient": + """Get a new client matching this one with a new timeout (in seconds)""" + if self._client is not None: + self._client.timeout = timeout + if self._async_client is not None: + self._async_client.timeout = timeout + return evolve(self, timeout=timeout) + + def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient": + """Manually set the underlying httpx.Client + + **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. + """ + self._client = client + return self + + def get_httpx_client(self) -> httpx.Client: + """Get the underlying httpx.Client, constructing a new one if not previously set""" + if self._client is None: + self._headers[self.auth_header_name] = ( + f"{self.prefix} {self.token}" if self.prefix else self.token + ) + self._client = httpx.Client( + base_url=self._base_url, + cookies=self._cookies, + headers=self._headers, + timeout=self._timeout, + verify=self._verify_ssl, + follow_redirects=self._follow_redirects, + **self._httpx_args, + ) + return self._client + + def __enter__(self) -> "AuthenticatedClient": + """Enter a context manager for self.client—you cannot enter twice (see httpx docs)""" + self.get_httpx_client().__enter__() + return self + + def __exit__(self, *args: Any, **kwargs: Any) -> None: + """Exit a context manager for internal httpx.Client (see httpx docs)""" + self.get_httpx_client().__exit__(*args, **kwargs) + + def set_async_httpx_client( + self, async_client: httpx.AsyncClient + ) -> "AuthenticatedClient": + """Manually the underlying httpx.AsyncClient + + **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. + """ + self._async_client = async_client + return self + + def get_async_httpx_client(self) -> httpx.AsyncClient: + """Get the underlying httpx.AsyncClient, constructing a new one if not previously set""" + if self._async_client is None: + self._headers[self.auth_header_name] = ( + f"{self.prefix} {self.token}" if self.prefix else self.token + ) + self._async_client = httpx.AsyncClient( + base_url=self._base_url, + cookies=self._cookies, + headers=self._headers, + timeout=self._timeout, + verify=self._verify_ssl, + follow_redirects=self._follow_redirects, + **self._httpx_args, + ) + return self._async_client + + async def __aenter__(self) -> "AuthenticatedClient": + """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)""" + await self.get_async_httpx_client().__aenter__() + return self + + async def __aexit__(self, *args: Any, **kwargs: Any) -> None: + """Exit a context manager for underlying httpx.AsyncClient (see httpx docs)""" + await self.get_async_httpx_client().__aexit__(*args, **kwargs) diff --git a/openapi python client generation/frontend/client/errors.py b/openapi python client generation/frontend/client/errors.py new file mode 100644 index 0000000..5f92e76 --- /dev/null +++ b/openapi python client generation/frontend/client/errors.py @@ -0,0 +1,16 @@ +"""Contains shared errors types that can be raised from API functions""" + + +class UnexpectedStatus(Exception): + """Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True""" + + def __init__(self, status_code: int, content: bytes): + self.status_code = status_code + self.content = content + + super().__init__( + f"Unexpected status code: {status_code}\n\nResponse content:\n{content.decode(errors='ignore')}" + ) + + +__all__ = ["UnexpectedStatus"] diff --git a/openapi python client generation/frontend/client/models/__init__.py b/openapi python client generation/frontend/client/models/__init__.py new file mode 100644 index 0000000..730f554 --- /dev/null +++ b/openapi python client generation/frontend/client/models/__init__.py @@ -0,0 +1,5 @@ +"""Contains all the data models used in inputs/outputs""" + +from .student import Student + +__all__ = ("Student",) diff --git a/openapi python client generation/frontend/client/models/student.py b/openapi python client generation/frontend/client/models/student.py new file mode 100644 index 0000000..134081f --- /dev/null +++ b/openapi python client generation/frontend/client/models/student.py @@ -0,0 +1,114 @@ +from typing import Any, Dict, List, Type, TypeVar, Union + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="Student") + + +@_attrs_define +class Student: + """ + Attributes: + name (str): + roll (int): + city (str): + id (Union[Unset, int]): + """ + + name: str + roll: int + city: str + id: Union[Unset, int] = UNSET + additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + name = self.name + + roll = self.roll + + city = self.city + + id = self.id + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "name": name, + "roll": roll, + "city": city, + } + ) + if id is not UNSET: + field_dict["id"] = id + + return field_dict + + def to_multipart(self) -> Dict[str, Any]: + name = (None, str(self.name).encode(), "text/plain") + + roll = (None, str(self.roll).encode(), "text/plain") + + city = (None, str(self.city).encode(), "text/plain") + + id = ( + self.id + if isinstance(self.id, Unset) + else (None, str(self.id).encode(), "text/plain") + ) + + field_dict: Dict[str, Any] = {} + for prop_name, prop in self.additional_properties.items(): + field_dict[prop_name] = (None, str(prop).encode(), "text/plain") + + field_dict.update( + { + "name": name, + "roll": roll, + "city": city, + } + ) + if id is not UNSET: + field_dict["id"] = id + + return field_dict + + @classmethod + def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + d = src_dict.copy() + name = d.pop("name") + + roll = d.pop("roll") + + city = d.pop("city") + + id = d.pop("id", UNSET) + + student = cls( + name=name, + roll=roll, + city=city, + id=id, + ) + + student.additional_properties = d + return student + + @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/openapi python client generation/frontend/client/types.py b/openapi python client generation/frontend/client/types.py new file mode 100644 index 0000000..05f5a22 --- /dev/null +++ b/openapi python client generation/frontend/client/types.py @@ -0,0 +1,53 @@ +"""Contains some shared types for properties""" + +from http import HTTPStatus +from typing import ( + BinaryIO, + Generic, + Literal, + MutableMapping, + Optional, + Tuple, + TypeVar, +) + +from attrs import define + + +class Unset: + def __bool__(self) -> Literal[False]: + return False + + +UNSET: Unset = Unset() + +FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]] + + +@define +class File: + """Contains information for file uploads""" + + payload: BinaryIO + file_name: Optional[str] = None + mime_type: Optional[str] = None + + def to_tuple(self) -> FileJsonType: + """Return a tuple representation that httpx will accept for multipart/form-data""" + return self.file_name, self.payload, self.mime_type + + +T = TypeVar("T") + + +@define +class Response(Generic[T]): + """A response from an endpoint""" + + status_code: HTTPStatus + content: bytes + headers: MutableMapping[str, str] + parsed: Optional[T] + + +__all__ = ["File", "Response", "FileJsonType", "Unset", "UNSET"] diff --git a/openapi python client generation/frontend/demo.py b/openapi python client generation/frontend/demo.py new file mode 100644 index 0000000..e69de29