add initial generation
Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
4
Makefile
4
Makefile
@ -11,7 +11,9 @@ generate: docker-image
|
||||
--name python-generator \
|
||||
-v $(CURDIR):/usr/src \
|
||||
--workdir /usr/src \
|
||||
$(DOCKER_IMAGE_NAME) openapi-python-client generate --path ./spec.yml
|
||||
$(DOCKER_IMAGE_NAME) openapi-python-client update \
|
||||
--path ./spec.yml \
|
||||
--config /usr/src/config.yml
|
||||
|
||||
.PHONY: docker-image
|
||||
docker-image:
|
||||
|
15
README.md
15
README.md
@ -1,9 +1,24 @@
|
||||
# kittycad.py
|
||||
|
||||
The Python API client for KittyCAD.
|
||||
|
||||
This is generated from
|
||||
[openapi-generators/openapi-python-client](https://github.com/openapi-generators/openapi-python-client).
|
||||
|
||||
## Generating
|
||||
|
||||
You can trigger a build with the GitHub action to generate the client. This will
|
||||
automatically update the client to the latest version of the hosted API at
|
||||
[api.kittycad.io](https://api.kittycad.io).
|
||||
|
||||
Alternatively, if you wish to generate the client locally, make sure you have
|
||||
[Docker installed](https://docs.docker.com/get-docker/) and run:
|
||||
|
||||
```bash
|
||||
$ make generate
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Please do not change the code directly since it is generated. PRs that change
|
||||
the code directly will be automatically closed by a bot.
|
||||
|
@ -1,2 +1,2 @@
|
||||
project_name_override: kittycad.py
|
||||
package_name_override: kittycad.py
|
||||
project_name_override: kittycad
|
||||
package_name_override: kittycad
|
||||
|
23
kittycad/.gitignore
vendored
Normal file
23
kittycad/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
__pycache__/
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
.pytest_cache/
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# JetBrains
|
||||
.idea/
|
||||
|
||||
/coverage.xml
|
||||
/.coverage
|
87
kittycad/README.md
Normal file
87
kittycad/README.md
Normal file
@ -0,0 +1,87 @@
|
||||
# kittycad
|
||||
A client library for accessing KittyCAD
|
||||
|
||||
## Usage
|
||||
First, create a client:
|
||||
|
||||
```python
|
||||
from kittycad import Client
|
||||
|
||||
client = Client(base_url="https://api.example.com")
|
||||
```
|
||||
|
||||
If the endpoints you're going to hit require authentication, use `AuthenticatedClient` instead:
|
||||
|
||||
```python
|
||||
from kittycad import AuthenticatedClient
|
||||
|
||||
client = AuthenticatedClient(base_url="https://api.example.com", token="SuperSecretToken")
|
||||
```
|
||||
|
||||
Now call your endpoint and use your models:
|
||||
|
||||
```python
|
||||
from kittycad.models import MyDataModel
|
||||
from kittycad.api.my_tag import get_my_data_model
|
||||
from kittycad.types import Response
|
||||
|
||||
my_data: MyDataModel = get_my_data_model.sync(client=client)
|
||||
# or if you need more info (e.g. status_code)
|
||||
response: Response[MyDataModel] = get_my_data_model.sync_detailed(client=client)
|
||||
```
|
||||
|
||||
Or do the same thing with an async version:
|
||||
|
||||
```python
|
||||
from kittycad.models import MyDataModel
|
||||
from kittycad.api.my_tag import get_my_data_model
|
||||
from kittycad.types import Response
|
||||
|
||||
my_data: MyDataModel = await get_my_data_model.asyncio(client=client)
|
||||
response: Response[MyDataModel] = await get_my_data_model.asyncio_detailed(client=client)
|
||||
```
|
||||
|
||||
By default, when you're calling an HTTPS API it will attempt to verify that SSL is working correctly. Using certificate verification is highly recommended most of the time, but sometimes you may need to authenticate to a server (especially an internal server) using a custom certificate bundle.
|
||||
|
||||
```python
|
||||
client = AuthenticatedClient(
|
||||
base_url="https://internal_api.example.com",
|
||||
token="SuperSecretToken",
|
||||
verify_ssl="/path/to/certificate_bundle.pem",
|
||||
)
|
||||
```
|
||||
|
||||
You can also disable certificate validation altogether, but beware that **this is a security risk**.
|
||||
|
||||
```python
|
||||
client = AuthenticatedClient(
|
||||
base_url="https://internal_api.example.com",
|
||||
token="SuperSecretToken",
|
||||
verify_ssl=False
|
||||
)
|
||||
```
|
||||
|
||||
Things to know:
|
||||
1. Every path/method combo becomes a Python module with four functions:
|
||||
1. `sync`: Blocking request that returns parsed data (if successful) or `None`
|
||||
1. `sync_detailed`: Blocking request that always returns a `Request`, optionally with `parsed` set if the request was successful.
|
||||
1. `asyncio`: Like `sync` but the async instead of blocking
|
||||
1. `asyncio_detailed`: Like `sync_detailed` by async instead of blocking
|
||||
|
||||
1. All path/query params, and bodies become method arguments.
|
||||
1. If your endpoint had any tags on it, the first tag will be used as a module name for the function (my_tag above)
|
||||
1. Any endpoint which did not have a tag will be in `kittycad.api.default`
|
||||
|
||||
## Building / publishing this Client
|
||||
This project uses [Poetry](https://python-poetry.org/) to manage dependencies and packaging. Here are the basics:
|
||||
1. Update the metadata in pyproject.toml (e.g. authors, version)
|
||||
1. If you're using a private repository, configure it with Poetry
|
||||
1. `poetry config repositories.<your-repository-name> <url-to-your-repository>`
|
||||
1. `poetry config http-basic.<your-repository-name> <username> <password>`
|
||||
1. Publish the client with `poetry publish --build -r <your-repository-name>` or, if for public PyPI, just `poetry publish --build`
|
||||
|
||||
If you want to install this client into another project without publishing it (e.g. for development) then:
|
||||
1. If that project **is using Poetry**, you can simply do `poetry add <path-to-this-client>` from that project
|
||||
1. If that project is not using Poetry:
|
||||
1. Build a wheel with `poetry build -f wheel`
|
||||
1. Install that wheel from the other project `pip install <path-to-wheel>`
|
2
kittycad/kittycad/__init__.py
Normal file
2
kittycad/kittycad/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
""" A client library for accessing KittyCAD """
|
||||
from .client import AuthenticatedClient, Client
|
1
kittycad/kittycad/api/__init__.py
Normal file
1
kittycad/kittycad/api/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
""" Contains methods for accessing the API """
|
0
kittycad/kittycad/api/file/__init__.py
Normal file
0
kittycad/kittycad/api/file/__init__.py
Normal file
124
kittycad/kittycad/api/file/file_conversion_by_id.py
Normal file
124
kittycad/kittycad/api/file/file_conversion_by_id.py
Normal file
@ -0,0 +1,124 @@
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from ...client import AuthenticatedClient
|
||||
from ...models.file_conversion import FileConversion
|
||||
from ...types import Response
|
||||
|
||||
|
||||
def _get_kwargs(
|
||||
id: str,
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Dict[str, Any]:
|
||||
url = "{}/file/conversion/{id}".format(client.base_url, id=id)
|
||||
|
||||
headers: Dict[str, Any] = client.get_headers()
|
||||
cookies: Dict[str, Any] = client.get_cookies()
|
||||
|
||||
return {
|
||||
"url": url,
|
||||
"headers": headers,
|
||||
"cookies": cookies,
|
||||
"timeout": client.get_timeout(),
|
||||
}
|
||||
|
||||
|
||||
def _parse_response(*, response: httpx.Response) -> Optional[Union[Any, FileConversion]]:
|
||||
if response.status_code == 200:
|
||||
response_200 = FileConversion.from_dict(response.json())
|
||||
|
||||
return response_200
|
||||
if response.status_code == 400:
|
||||
response_400 = None
|
||||
|
||||
return response_400
|
||||
if response.status_code == 401:
|
||||
response_401 = None
|
||||
|
||||
return response_401
|
||||
if response.status_code == 403:
|
||||
response_403 = None
|
||||
|
||||
return response_403
|
||||
if response.status_code == 404:
|
||||
response_404 = None
|
||||
|
||||
return response_404
|
||||
if response.status_code == 406:
|
||||
response_406 = None
|
||||
|
||||
return response_406
|
||||
return None
|
||||
|
||||
|
||||
def _build_response(*, response: httpx.Response) -> Response[Union[Any, FileConversion]]:
|
||||
return Response(
|
||||
status_code=response.status_code,
|
||||
content=response.content,
|
||||
headers=response.headers,
|
||||
parsed=_parse_response(response=response),
|
||||
)
|
||||
|
||||
|
||||
def sync_detailed(
|
||||
id: str,
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Response[Union[Any, FileConversion]]:
|
||||
kwargs = _get_kwargs(
|
||||
id=id,
|
||||
client=client,
|
||||
)
|
||||
|
||||
response = httpx.get(
|
||||
verify=client.verify_ssl,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
|
||||
def sync(
|
||||
id: str,
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Optional[Union[Any, FileConversion]]:
|
||||
"""Get the status of a file conversion."""
|
||||
|
||||
return sync_detailed(
|
||||
id=id,
|
||||
client=client,
|
||||
).parsed
|
||||
|
||||
|
||||
async def asyncio_detailed(
|
||||
id: str,
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Response[Union[Any, FileConversion]]:
|
||||
kwargs = _get_kwargs(
|
||||
id=id,
|
||||
client=client,
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient(verify=client.verify_ssl) as _client:
|
||||
response = await _client.get(**kwargs)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
|
||||
async def asyncio(
|
||||
id: str,
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Optional[Union[Any, FileConversion]]:
|
||||
"""Get the status of a file conversion."""
|
||||
|
||||
return (
|
||||
await asyncio_detailed(
|
||||
id=id,
|
||||
client=client,
|
||||
)
|
||||
).parsed
|
0
kittycad/kittycad/api/meta/__init__.py
Normal file
0
kittycad/kittycad/api/meta/__init__.py
Normal file
107
kittycad/kittycad/api/meta/meta_debug_instance.py
Normal file
107
kittycad/kittycad/api/meta/meta_debug_instance.py
Normal file
@ -0,0 +1,107 @@
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from ...client import AuthenticatedClient
|
||||
from ...models.instance_metadata import InstanceMetadata
|
||||
from ...types import Response
|
||||
|
||||
|
||||
def _get_kwargs(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Dict[str, Any]:
|
||||
url = "{}/_meta/debug/instance".format(client.base_url)
|
||||
|
||||
headers: Dict[str, Any] = client.get_headers()
|
||||
cookies: Dict[str, Any] = client.get_cookies()
|
||||
|
||||
return {
|
||||
"url": url,
|
||||
"headers": headers,
|
||||
"cookies": cookies,
|
||||
"timeout": client.get_timeout(),
|
||||
}
|
||||
|
||||
|
||||
def _parse_response(*, response: httpx.Response) -> Optional[Union[Any, InstanceMetadata]]:
|
||||
if response.status_code == 200:
|
||||
response_200 = InstanceMetadata.from_dict(response.json())
|
||||
|
||||
return response_200
|
||||
if response.status_code == 400:
|
||||
response_400 = None
|
||||
|
||||
return response_400
|
||||
if response.status_code == 401:
|
||||
response_401 = None
|
||||
|
||||
return response_401
|
||||
if response.status_code == 403:
|
||||
response_403 = None
|
||||
|
||||
return response_403
|
||||
return None
|
||||
|
||||
|
||||
def _build_response(*, response: httpx.Response) -> Response[Union[Any, InstanceMetadata]]:
|
||||
return Response(
|
||||
status_code=response.status_code,
|
||||
content=response.content,
|
||||
headers=response.headers,
|
||||
parsed=_parse_response(response=response),
|
||||
)
|
||||
|
||||
|
||||
def sync_detailed(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Response[Union[Any, InstanceMetadata]]:
|
||||
kwargs = _get_kwargs(
|
||||
client=client,
|
||||
)
|
||||
|
||||
response = httpx.get(
|
||||
verify=client.verify_ssl,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
|
||||
def sync(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Optional[Union[Any, InstanceMetadata]]:
|
||||
"""Get information about this specific API server instance. This is primarily used for debugging."""
|
||||
|
||||
return sync_detailed(
|
||||
client=client,
|
||||
).parsed
|
||||
|
||||
|
||||
async def asyncio_detailed(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Response[Union[Any, InstanceMetadata]]:
|
||||
kwargs = _get_kwargs(
|
||||
client=client,
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient(verify=client.verify_ssl) as _client:
|
||||
response = await _client.get(**kwargs)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
|
||||
async def asyncio(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Optional[Union[Any, InstanceMetadata]]:
|
||||
"""Get information about this specific API server instance. This is primarily used for debugging."""
|
||||
|
||||
return (
|
||||
await asyncio_detailed(
|
||||
client=client,
|
||||
)
|
||||
).parsed
|
107
kittycad/kittycad/api/meta/meta_debug_session.py
Normal file
107
kittycad/kittycad/api/meta/meta_debug_session.py
Normal file
@ -0,0 +1,107 @@
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from ...client import AuthenticatedClient
|
||||
from ...models.auth_session import AuthSession
|
||||
from ...types import Response
|
||||
|
||||
|
||||
def _get_kwargs(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Dict[str, Any]:
|
||||
url = "{}/_meta/debug/session".format(client.base_url)
|
||||
|
||||
headers: Dict[str, Any] = client.get_headers()
|
||||
cookies: Dict[str, Any] = client.get_cookies()
|
||||
|
||||
return {
|
||||
"url": url,
|
||||
"headers": headers,
|
||||
"cookies": cookies,
|
||||
"timeout": client.get_timeout(),
|
||||
}
|
||||
|
||||
|
||||
def _parse_response(*, response: httpx.Response) -> Optional[Union[Any, AuthSession]]:
|
||||
if response.status_code == 200:
|
||||
response_200 = AuthSession.from_dict(response.json())
|
||||
|
||||
return response_200
|
||||
if response.status_code == 400:
|
||||
response_400 = None
|
||||
|
||||
return response_400
|
||||
if response.status_code == 401:
|
||||
response_401 = None
|
||||
|
||||
return response_401
|
||||
if response.status_code == 403:
|
||||
response_403 = None
|
||||
|
||||
return response_403
|
||||
return None
|
||||
|
||||
|
||||
def _build_response(*, response: httpx.Response) -> Response[Union[Any, AuthSession]]:
|
||||
return Response(
|
||||
status_code=response.status_code,
|
||||
content=response.content,
|
||||
headers=response.headers,
|
||||
parsed=_parse_response(response=response),
|
||||
)
|
||||
|
||||
|
||||
def sync_detailed(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Response[Union[Any, AuthSession]]:
|
||||
kwargs = _get_kwargs(
|
||||
client=client,
|
||||
)
|
||||
|
||||
response = httpx.get(
|
||||
verify=client.verify_ssl,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
|
||||
def sync(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Optional[Union[Any, AuthSession]]:
|
||||
"""Get information about your API request session. This is primarily used for debugging."""
|
||||
|
||||
return sync_detailed(
|
||||
client=client,
|
||||
).parsed
|
||||
|
||||
|
||||
async def asyncio_detailed(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Response[Union[Any, AuthSession]]:
|
||||
kwargs = _get_kwargs(
|
||||
client=client,
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient(verify=client.verify_ssl) as _client:
|
||||
response = await _client.get(**kwargs)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
|
||||
async def asyncio(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
) -> Optional[Union[Any, AuthSession]]:
|
||||
"""Get information about your API request session. This is primarily used for debugging."""
|
||||
|
||||
return (
|
||||
await asyncio_detailed(
|
||||
client=client,
|
||||
)
|
||||
).parsed
|
95
kittycad/kittycad/api/meta/ping.py
Normal file
95
kittycad/kittycad/api/meta/ping.py
Normal file
@ -0,0 +1,95 @@
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import httpx
|
||||
|
||||
from ...client import Client
|
||||
from ...models.ping_response_200 import PingResponse200
|
||||
from ...types import Response
|
||||
|
||||
|
||||
def _get_kwargs(
|
||||
*,
|
||||
client: Client,
|
||||
) -> Dict[str, Any]:
|
||||
url = "{}/ping".format(client.base_url)
|
||||
|
||||
headers: Dict[str, Any] = client.get_headers()
|
||||
cookies: Dict[str, Any] = client.get_cookies()
|
||||
|
||||
return {
|
||||
"url": url,
|
||||
"headers": headers,
|
||||
"cookies": cookies,
|
||||
"timeout": client.get_timeout(),
|
||||
}
|
||||
|
||||
|
||||
def _parse_response(*, response: httpx.Response) -> Optional[PingResponse200]:
|
||||
if response.status_code == 200:
|
||||
response_200 = PingResponse200.from_dict(response.json())
|
||||
|
||||
return response_200
|
||||
return None
|
||||
|
||||
|
||||
def _build_response(*, response: httpx.Response) -> Response[PingResponse200]:
|
||||
return Response(
|
||||
status_code=response.status_code,
|
||||
content=response.content,
|
||||
headers=response.headers,
|
||||
parsed=_parse_response(response=response),
|
||||
)
|
||||
|
||||
|
||||
def sync_detailed(
|
||||
*,
|
||||
client: Client,
|
||||
) -> Response[PingResponse200]:
|
||||
kwargs = _get_kwargs(
|
||||
client=client,
|
||||
)
|
||||
|
||||
response = httpx.get(
|
||||
verify=client.verify_ssl,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
|
||||
def sync(
|
||||
*,
|
||||
client: Client,
|
||||
) -> Optional[PingResponse200]:
|
||||
"""Simple ping to the server."""
|
||||
|
||||
return sync_detailed(
|
||||
client=client,
|
||||
).parsed
|
||||
|
||||
|
||||
async def asyncio_detailed(
|
||||
*,
|
||||
client: Client,
|
||||
) -> Response[PingResponse200]:
|
||||
kwargs = _get_kwargs(
|
||||
client=client,
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient(verify=client.verify_ssl) as _client:
|
||||
response = await _client.get(**kwargs)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
|
||||
async def asyncio(
|
||||
*,
|
||||
client: Client,
|
||||
) -> Optional[PingResponse200]:
|
||||
"""Simple ping to the server."""
|
||||
|
||||
return (
|
||||
await asyncio_detailed(
|
||||
client=client,
|
||||
)
|
||||
).parsed
|
48
kittycad/kittycad/client.py
Normal file
48
kittycad/kittycad/client.py
Normal file
@ -0,0 +1,48 @@
|
||||
import ssl
|
||||
from typing import Dict, Union
|
||||
|
||||
import attr
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class Client:
|
||||
"""A class for keeping track of data related to the API"""
|
||||
|
||||
base_url: str
|
||||
cookies: Dict[str, str] = attr.ib(factory=dict, kw_only=True)
|
||||
headers: Dict[str, str] = attr.ib(factory=dict, kw_only=True)
|
||||
timeout: float = attr.ib(5.0, kw_only=True)
|
||||
verify_ssl: Union[str, bool, ssl.SSLContext] = attr.ib(True, kw_only=True)
|
||||
|
||||
def get_headers(self) -> Dict[str, str]:
|
||||
"""Get headers to be used in all endpoints"""
|
||||
return {**self.headers}
|
||||
|
||||
def with_headers(self, headers: Dict[str, str]) -> "Client":
|
||||
"""Get a new client matching this one with additional headers"""
|
||||
return attr.evolve(self, headers={**self.headers, **headers})
|
||||
|
||||
def get_cookies(self) -> Dict[str, str]:
|
||||
return {**self.cookies}
|
||||
|
||||
def with_cookies(self, cookies: Dict[str, str]) -> "Client":
|
||||
"""Get a new client matching this one with additional cookies"""
|
||||
return attr.evolve(self, cookies={**self.cookies, **cookies})
|
||||
|
||||
def get_timeout(self) -> float:
|
||||
return self.timeout
|
||||
|
||||
def with_timeout(self, timeout: float) -> "Client":
|
||||
"""Get a new client matching this one with a new timeout (in seconds)"""
|
||||
return attr.evolve(self, timeout=timeout)
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class AuthenticatedClient(Client):
|
||||
"""A Client which has been authenticated for use on secured endpoints"""
|
||||
|
||||
token: str
|
||||
|
||||
def get_headers(self) -> Dict[str, str]:
|
||||
"""Get headers to be used in authenticated endpoints"""
|
||||
return {"Authorization": f"Bearer {self.token}", **self.headers}
|
11
kittycad/kittycad/models/__init__.py
Normal file
11
kittycad/kittycad/models/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
""" Contains all the data models used in inputs/outputs """
|
||||
|
||||
from .auth_session import AuthSession
|
||||
from .error_message import ErrorMessage
|
||||
from .file_conversion import FileConversion
|
||||
from .file_conversion_status import FileConversionStatus
|
||||
from .instance_metadata import InstanceMetadata
|
||||
from .instance_metadata_environment import InstanceMetadataEnvironment
|
||||
from .ping_response_200 import PingResponse200
|
||||
from .ping_response_200_message import PingResponse200Message
|
||||
from .valid_file_types import ValidFileTypes
|
98
kittycad/kittycad/models/auth_session.py
Normal file
98
kittycad/kittycad/models/auth_session.py
Normal file
@ -0,0 +1,98 @@
|
||||
import datetime
|
||||
from typing import Any, Dict, List, Type, TypeVar, Union
|
||||
|
||||
import attr
|
||||
from dateutil.parser import isoparse
|
||||
|
||||
from ..types import UNSET, Unset
|
||||
|
||||
T = TypeVar("T", bound="AuthSession")
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class AuthSession:
|
||||
""" """
|
||||
|
||||
id: Union[Unset, str] = UNSET
|
||||
token: Union[Unset, str] = UNSET
|
||||
user_id: Union[Unset, str] = UNSET
|
||||
ip_address: Union[Unset, str] = UNSET
|
||||
is_valid: Union[Unset, bool] = False
|
||||
created_at: Union[Unset, datetime.datetime] = UNSET
|
||||
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
id = self.id
|
||||
token = self.token
|
||||
user_id = self.user_id
|
||||
ip_address = self.ip_address
|
||||
is_valid = self.is_valid
|
||||
created_at: Union[Unset, str] = UNSET
|
||||
if not isinstance(self.created_at, Unset):
|
||||
created_at = self.created_at.isoformat()
|
||||
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update(self.additional_properties)
|
||||
field_dict.update({})
|
||||
if id is not UNSET:
|
||||
field_dict["id"] = id
|
||||
if token is not UNSET:
|
||||
field_dict["token"] = token
|
||||
if user_id is not UNSET:
|
||||
field_dict["user_id"] = user_id
|
||||
if ip_address is not UNSET:
|
||||
field_dict["ip_address"] = ip_address
|
||||
if is_valid is not UNSET:
|
||||
field_dict["is_valid"] = is_valid
|
||||
if created_at is not UNSET:
|
||||
field_dict["created_at"] = created_at
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
d = src_dict.copy()
|
||||
id = d.pop("id", UNSET)
|
||||
|
||||
token = d.pop("token", UNSET)
|
||||
|
||||
user_id = d.pop("user_id", UNSET)
|
||||
|
||||
ip_address = d.pop("ip_address", UNSET)
|
||||
|
||||
is_valid = d.pop("is_valid", UNSET)
|
||||
|
||||
_created_at = d.pop("created_at", UNSET)
|
||||
created_at: Union[Unset, datetime.datetime]
|
||||
if isinstance(_created_at, Unset):
|
||||
created_at = UNSET
|
||||
else:
|
||||
created_at = isoparse(_created_at)
|
||||
|
||||
auth_session = cls(
|
||||
id=id,
|
||||
token=token,
|
||||
user_id=user_id,
|
||||
ip_address=ip_address,
|
||||
is_valid=is_valid,
|
||||
created_at=created_at,
|
||||
)
|
||||
|
||||
auth_session.additional_properties = d
|
||||
return auth_session
|
||||
|
||||
@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
|
54
kittycad/kittycad/models/error_message.py
Normal file
54
kittycad/kittycad/models/error_message.py
Normal file
@ -0,0 +1,54 @@
|
||||
from typing import Any, Dict, List, Type, TypeVar, Union
|
||||
|
||||
import attr
|
||||
|
||||
from ..types import UNSET, Unset
|
||||
|
||||
T = TypeVar("T", bound="ErrorMessage")
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class ErrorMessage:
|
||||
""" """
|
||||
|
||||
message: Union[Unset, str] = UNSET
|
||||
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
message = self.message
|
||||
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update(self.additional_properties)
|
||||
field_dict.update({})
|
||||
if message is not UNSET:
|
||||
field_dict["message"] = message
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
d = src_dict.copy()
|
||||
message = d.pop("message", UNSET)
|
||||
|
||||
error_message = cls(
|
||||
message=message,
|
||||
)
|
||||
|
||||
error_message.additional_properties = d
|
||||
return error_message
|
||||
|
||||
@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
|
140
kittycad/kittycad/models/file_conversion.py
Normal file
140
kittycad/kittycad/models/file_conversion.py
Normal file
@ -0,0 +1,140 @@
|
||||
import datetime
|
||||
from typing import Any, Dict, List, Type, TypeVar, Union
|
||||
|
||||
import attr
|
||||
from dateutil.parser import isoparse
|
||||
|
||||
from ..models.file_conversion_status import FileConversionStatus
|
||||
from ..models.valid_file_types import ValidFileTypes
|
||||
from ..types import UNSET, Unset
|
||||
|
||||
T = TypeVar("T", bound="FileConversion")
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class FileConversion:
|
||||
""" """
|
||||
|
||||
id: Union[Unset, str] = UNSET
|
||||
created_at: Union[Unset, datetime.datetime] = UNSET
|
||||
completed_at: Union[Unset, datetime.datetime] = UNSET
|
||||
status: Union[Unset, FileConversionStatus] = UNSET
|
||||
src_format: Union[Unset, ValidFileTypes] = UNSET
|
||||
output_format: Union[Unset, ValidFileTypes] = UNSET
|
||||
output: Union[Unset, str] = UNSET
|
||||
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
id = self.id
|
||||
created_at: Union[Unset, str] = UNSET
|
||||
if not isinstance(self.created_at, Unset):
|
||||
created_at = self.created_at.isoformat()
|
||||
|
||||
completed_at: Union[Unset, str] = UNSET
|
||||
if not isinstance(self.completed_at, Unset):
|
||||
completed_at = self.completed_at.isoformat()
|
||||
|
||||
status: Union[Unset, str] = UNSET
|
||||
if not isinstance(self.status, Unset):
|
||||
status = self.status.value
|
||||
|
||||
src_format: Union[Unset, str] = UNSET
|
||||
if not isinstance(self.src_format, Unset):
|
||||
src_format = self.src_format.value
|
||||
|
||||
output_format: Union[Unset, str] = UNSET
|
||||
if not isinstance(self.output_format, Unset):
|
||||
output_format = self.output_format.value
|
||||
|
||||
output = self.output
|
||||
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update(self.additional_properties)
|
||||
field_dict.update({})
|
||||
if id is not UNSET:
|
||||
field_dict["id"] = id
|
||||
if created_at is not UNSET:
|
||||
field_dict["created_at"] = created_at
|
||||
if completed_at is not UNSET:
|
||||
field_dict["completed_at"] = completed_at
|
||||
if status is not UNSET:
|
||||
field_dict["status"] = status
|
||||
if src_format is not UNSET:
|
||||
field_dict["src_format"] = src_format
|
||||
if output_format is not UNSET:
|
||||
field_dict["output_format"] = output_format
|
||||
if output is not UNSET:
|
||||
field_dict["output"] = output
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
d = src_dict.copy()
|
||||
id = d.pop("id", UNSET)
|
||||
|
||||
_created_at = d.pop("created_at", UNSET)
|
||||
created_at: Union[Unset, datetime.datetime]
|
||||
if isinstance(_created_at, Unset):
|
||||
created_at = UNSET
|
||||
else:
|
||||
created_at = isoparse(_created_at)
|
||||
|
||||
_completed_at = d.pop("completed_at", UNSET)
|
||||
completed_at: Union[Unset, datetime.datetime]
|
||||
if isinstance(_completed_at, Unset):
|
||||
completed_at = UNSET
|
||||
else:
|
||||
completed_at = isoparse(_completed_at)
|
||||
|
||||
_status = d.pop("status", UNSET)
|
||||
status: Union[Unset, FileConversionStatus]
|
||||
if isinstance(_status, Unset):
|
||||
status = UNSET
|
||||
else:
|
||||
status = FileConversionStatus(_status)
|
||||
|
||||
_src_format = d.pop("src_format", UNSET)
|
||||
src_format: Union[Unset, ValidFileTypes]
|
||||
if isinstance(_src_format, Unset):
|
||||
src_format = UNSET
|
||||
else:
|
||||
src_format = ValidFileTypes(_src_format)
|
||||
|
||||
_output_format = d.pop("output_format", UNSET)
|
||||
output_format: Union[Unset, ValidFileTypes]
|
||||
if isinstance(_output_format, Unset):
|
||||
output_format = UNSET
|
||||
else:
|
||||
output_format = ValidFileTypes(_output_format)
|
||||
|
||||
output = d.pop("output", UNSET)
|
||||
|
||||
file_conversion = cls(
|
||||
id=id,
|
||||
created_at=created_at,
|
||||
completed_at=completed_at,
|
||||
status=status,
|
||||
src_format=src_format,
|
||||
output_format=output_format,
|
||||
output=output,
|
||||
)
|
||||
|
||||
file_conversion.additional_properties = d
|
||||
return file_conversion
|
||||
|
||||
@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
|
12
kittycad/kittycad/models/file_conversion_status.py
Normal file
12
kittycad/kittycad/models/file_conversion_status.py
Normal file
@ -0,0 +1,12 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class FileConversionStatus(str, Enum):
|
||||
QUEUED = "Queued"
|
||||
UPLOADED = "Uploaded"
|
||||
IN_PROGRESS = "In Progress"
|
||||
COMPLETED = "Completed"
|
||||
FAILED = "Failed"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
133
kittycad/kittycad/models/instance_metadata.py
Normal file
133
kittycad/kittycad/models/instance_metadata.py
Normal file
@ -0,0 +1,133 @@
|
||||
from typing import Any, Dict, List, Type, TypeVar, Union
|
||||
|
||||
import attr
|
||||
|
||||
from ..models.instance_metadata_environment import InstanceMetadataEnvironment
|
||||
from ..types import UNSET, Unset
|
||||
|
||||
T = TypeVar("T", bound="InstanceMetadata")
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class InstanceMetadata:
|
||||
""" """
|
||||
|
||||
id: Union[Unset, str] = UNSET
|
||||
git_hash: Union[Unset, str] = UNSET
|
||||
environment: Union[Unset, InstanceMetadataEnvironment] = UNSET
|
||||
name: Union[Unset, str] = UNSET
|
||||
description: Union[Unset, str] = UNSET
|
||||
ip_address: Union[Unset, str] = UNSET
|
||||
zone: Union[Unset, str] = UNSET
|
||||
image: Union[Unset, str] = UNSET
|
||||
hostname: Union[Unset, str] = UNSET
|
||||
cpu_platform: Union[Unset, str] = UNSET
|
||||
machine_type: Union[Unset, str] = UNSET
|
||||
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
id = self.id
|
||||
git_hash = self.git_hash
|
||||
environment: Union[Unset, str] = UNSET
|
||||
if not isinstance(self.environment, Unset):
|
||||
environment = self.environment.value
|
||||
|
||||
name = self.name
|
||||
description = self.description
|
||||
ip_address = self.ip_address
|
||||
zone = self.zone
|
||||
image = self.image
|
||||
hostname = self.hostname
|
||||
cpu_platform = self.cpu_platform
|
||||
machine_type = self.machine_type
|
||||
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update(self.additional_properties)
|
||||
field_dict.update({})
|
||||
if id is not UNSET:
|
||||
field_dict["id"] = id
|
||||
if git_hash is not UNSET:
|
||||
field_dict["git_hash"] = git_hash
|
||||
if environment is not UNSET:
|
||||
field_dict["environment"] = environment
|
||||
if name is not UNSET:
|
||||
field_dict["name"] = name
|
||||
if description is not UNSET:
|
||||
field_dict["description"] = description
|
||||
if ip_address is not UNSET:
|
||||
field_dict["ip_address"] = ip_address
|
||||
if zone is not UNSET:
|
||||
field_dict["zone"] = zone
|
||||
if image is not UNSET:
|
||||
field_dict["image"] = image
|
||||
if hostname is not UNSET:
|
||||
field_dict["hostname"] = hostname
|
||||
if cpu_platform is not UNSET:
|
||||
field_dict["cpu_platform"] = cpu_platform
|
||||
if machine_type is not UNSET:
|
||||
field_dict["machine_type"] = machine_type
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
d = src_dict.copy()
|
||||
id = d.pop("id", UNSET)
|
||||
|
||||
git_hash = d.pop("git_hash", UNSET)
|
||||
|
||||
_environment = d.pop("environment", UNSET)
|
||||
environment: Union[Unset, InstanceMetadataEnvironment]
|
||||
if isinstance(_environment, Unset):
|
||||
environment = UNSET
|
||||
else:
|
||||
environment = InstanceMetadataEnvironment(_environment)
|
||||
|
||||
name = d.pop("name", UNSET)
|
||||
|
||||
description = d.pop("description", UNSET)
|
||||
|
||||
ip_address = d.pop("ip_address", UNSET)
|
||||
|
||||
zone = d.pop("zone", UNSET)
|
||||
|
||||
image = d.pop("image", UNSET)
|
||||
|
||||
hostname = d.pop("hostname", UNSET)
|
||||
|
||||
cpu_platform = d.pop("cpu_platform", UNSET)
|
||||
|
||||
machine_type = d.pop("machine_type", UNSET)
|
||||
|
||||
instance_metadata = cls(
|
||||
id=id,
|
||||
git_hash=git_hash,
|
||||
environment=environment,
|
||||
name=name,
|
||||
description=description,
|
||||
ip_address=ip_address,
|
||||
zone=zone,
|
||||
image=image,
|
||||
hostname=hostname,
|
||||
cpu_platform=cpu_platform,
|
||||
machine_type=machine_type,
|
||||
)
|
||||
|
||||
instance_metadata.additional_properties = d
|
||||
return instance_metadata
|
||||
|
||||
@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
|
10
kittycad/kittycad/models/instance_metadata_environment.py
Normal file
10
kittycad/kittycad/models/instance_metadata_environment.py
Normal file
@ -0,0 +1,10 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class InstanceMetadataEnvironment(str, Enum):
|
||||
DEVELOPMENT = "DEVELOPMENT"
|
||||
PREVIEW = "PREVIEW"
|
||||
PRODUCTION = "PRODUCTION"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
62
kittycad/kittycad/models/ping_response_200.py
Normal file
62
kittycad/kittycad/models/ping_response_200.py
Normal file
@ -0,0 +1,62 @@
|
||||
from typing import Any, Dict, List, Type, TypeVar, Union
|
||||
|
||||
import attr
|
||||
|
||||
from ..models.ping_response_200_message import PingResponse200Message
|
||||
from ..types import UNSET, Unset
|
||||
|
||||
T = TypeVar("T", bound="PingResponse200")
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class PingResponse200:
|
||||
""" """
|
||||
|
||||
message: Union[Unset, PingResponse200Message] = UNSET
|
||||
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
message: Union[Unset, str] = UNSET
|
||||
if not isinstance(self.message, Unset):
|
||||
message = self.message.value
|
||||
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update(self.additional_properties)
|
||||
field_dict.update({})
|
||||
if message is not UNSET:
|
||||
field_dict["message"] = message
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
d = src_dict.copy()
|
||||
_message = d.pop("message", UNSET)
|
||||
message: Union[Unset, PingResponse200Message]
|
||||
if isinstance(_message, Unset):
|
||||
message = UNSET
|
||||
else:
|
||||
message = PingResponse200Message(_message)
|
||||
|
||||
ping_response_200 = cls(
|
||||
message=message,
|
||||
)
|
||||
|
||||
ping_response_200.additional_properties = d
|
||||
return ping_response_200
|
||||
|
||||
@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
|
8
kittycad/kittycad/models/ping_response_200_message.py
Normal file
8
kittycad/kittycad/models/ping_response_200_message.py
Normal file
@ -0,0 +1,8 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class PingResponse200Message(str, Enum):
|
||||
PONG = "pong"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
12
kittycad/kittycad/models/valid_file_types.py
Normal file
12
kittycad/kittycad/models/valid_file_types.py
Normal file
@ -0,0 +1,12 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ValidFileTypes(str, Enum):
|
||||
STEP = "step"
|
||||
OBJ = "obj"
|
||||
STL = "stl"
|
||||
DXF = "dxf"
|
||||
DWG = "dwg"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
1
kittycad/kittycad/py.typed
Normal file
1
kittycad/kittycad/py.typed
Normal file
@ -0,0 +1 @@
|
||||
# Marker file for PEP 561
|
43
kittycad/kittycad/types.py
Normal file
43
kittycad/kittycad/types.py
Normal file
@ -0,0 +1,43 @@
|
||||
""" Contains some shared types for properties """
|
||||
from typing import BinaryIO, Generic, MutableMapping, Optional, TextIO, Tuple, TypeVar, Union
|
||||
|
||||
import attr
|
||||
|
||||
|
||||
class Unset:
|
||||
def __bool__(self) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
UNSET: Unset = Unset()
|
||||
|
||||
FileJsonType = Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class File:
|
||||
"""Contains information for file uploads"""
|
||||
|
||||
payload: Union[BinaryIO, TextIO]
|
||||
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")
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class Response(Generic[T]):
|
||||
"""A response from an endpoint"""
|
||||
|
||||
status_code: int
|
||||
content: bytes
|
||||
headers: MutableMapping[str, str]
|
||||
parsed: Optional[T]
|
||||
|
||||
|
||||
__all__ = ["File", "Response", "FileJsonType"]
|
39
kittycad/pyproject.toml
Normal file
39
kittycad/pyproject.toml
Normal file
@ -0,0 +1,39 @@
|
||||
[tool.poetry]
|
||||
name = "kittycad"
|
||||
version = "0.1.0"
|
||||
description = "A client library for accessing KittyCAD"
|
||||
|
||||
authors = []
|
||||
|
||||
readme = "README.md"
|
||||
packages = [
|
||||
{include = "kittycad"},
|
||||
]
|
||||
include = ["CHANGELOG.md", "kittycad/py.typed"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.6"
|
||||
httpx = ">=0.15.4,<0.21.0"
|
||||
attrs = ">=20.1.0,<22.0.0"
|
||||
python-dateutil = "^2.8.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry>=1.0"]
|
||||
build-backend = "poetry.masonry.api"
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
target_version = ['py36', 'py37', 'py38']
|
||||
exclude = '''
|
||||
(
|
||||
/(
|
||||
| \.git
|
||||
| \.venv
|
||||
| \.mypy_cache
|
||||
)/
|
||||
)
|
||||
'''
|
||||
|
||||
[tool.isort]
|
||||
line_length = 120
|
||||
profile = "black"
|
Reference in New Issue
Block a user