Compare commits

..

16 Commits

Author SHA1 Message Date
35bbe91eb4 bump tag
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-22 11:39:19 -07:00
b4ce8e9642 Update api spec (#215)
* YOYO NEW API SPEC!

* I have generated the latest API!

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-22 11:10:19 -07:00
479cf6a937 Update api spec (#214)
* YOYO NEW API SPEC!

* I have generated the latest API!

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-18 14:44:34 -07:00
acea57bcba Update api spec (#213)
* YOYO NEW API SPEC!

* I have generated the latest API!

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-17 09:49:04 -07:00
64e8aa2816 Update api spec (#212)
* YOYO NEW API SPEC!

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* I have generated the latest API!

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
2024-04-12 12:03:34 -07:00
38801b52a7 fix
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-11 11:23:34 -07:00
e73f39cfa9 reviewers
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-11 11:16:11 -07:00
7536ca8683 assignee
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-11 11:05:49 -07:00
493991edd1 updates
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-10 18:36:19 -07:00
29003eae80 Update api spec (#211)
YOYO NEW API SPEC!

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-10 18:00:02 -07:00
79b977a55a fix test
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-05 13:30:47 -07:00
df9083c3e2 bump version
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-05 13:13:34 -07:00
f3e7f4f229 fix new spec bs
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-05 13:12:19 -07:00
12864cdb44 Merge remote-tracking branch 'origin/update-spec'
* origin/update-spec:
  YOYO NEW API SPEC!
2024-04-05 12:58:43 -07:00
671a0a8391 Bump softprops/action-gh-release from 1 to 2 (#209)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-05 12:58:12 -07:00
553b1c1fcd YOYO NEW API SPEC! 2024-04-04 20:49:25 +00:00
26 changed files with 2112 additions and 802 deletions

View File

@ -28,4 +28,4 @@ jobs:
# TODO: generate a nice little doc for the release text like we do for the
# cli repo.
- name: Create a Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2

View File

@ -67,6 +67,9 @@ jobs:
gh pr create --title "Update lang spec docs for python" \
--body "Updating the generated docs for python" \
--head "$NEW_BRANCH" \
--reviewer jessfraz \
--reviewer irev-dev \
--reviewer franknoirot \
--base main || true
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}

View File

@ -154,10 +154,19 @@ class WebSocket:
self.ws = sync(
{% for arg in args %}
{% if arg.in_query %}
{% if arg.is_optional == False %}
{{arg.name}},
{% endif %}
{% endif %}
{% endfor %}
client=client,
{% for arg in args %}
{% if arg.in_query %}
{% if arg.is_optional %}
{{arg.name}}={{arg.name}},
{% endif %}
{% endif %}
{% endfor %}
)
def __enter__(self,

View File

@ -478,11 +478,16 @@ from kittycad.types import Response
for endpoint_ref in endpoint_refs:
if endpoint_ref == "Error":
continue
example_imports = example_imports + (
"""from kittycad.models import """
+ endpoint_ref.replace("List[", "").replace("]", "")
+ "\n"
)
# For some reason, PrivacySettings is showing up twice so we want to skip
# it here. Obviously this is a hack and we should fix the root cause.
# When this happens again with another struct there might be a more obvious
# solution, but alas, I am lazy.
if endpoint_ref != "PrivacySettings" and endpoint_ref != "List[str]":
example_imports = example_imports + (
"""from kittycad.models import """
+ endpoint_ref.replace("List[", "").replace("]", "")
+ "\n"
)
example_imports = (
example_imports + "from typing import Union, Any, Optional, List, Tuple\n"
)
@ -841,6 +846,20 @@ async def test_"""
"\t\t\tfor item in response.json()\n"
)
parse_response.write("\t\t]\n")
elif "type" in items:
if items["type"] == "string":
parse_response.write(
"\t\tresponse_"
+ response_code
+ " = [\n"
)
parse_response.write("\t\t\tstr(**item)\n")
parse_response.write(
"\t\t\tfor item in response.json()\n"
)
parse_response.write("\t\t]\n")
else:
raise Exception("Unknown array type", items)
else:
raise Exception("Unknown array type")
elif json["type"] == "string":
@ -1251,7 +1270,7 @@ def generateAnyOfType(path: str, name: str, schema: dict, data: dict):
f.write("from ." + camel_to_snake(ref_name) + " import " + ref_name + "\n")
all_options.append(ref_name)
if isNestedObjectOneOf(schema):
if isNestedObjectAnyOf(schema):
# We want to write each of the nested objects.
for any_of in schema["anyOf"]:
# Get the nested object.
@ -1307,6 +1326,47 @@ def generateAnyOfType(path: str, name: str, schema: dict, data: dict):
f.write(object_code)
f.write("\n")
all_options.append(object_name)
else:
# We want to write each of the nested objects.
for any_of in schema["anyOf"]:
# Get the nested object.
if "properties" in any_of:
for prop_name in any_of["properties"]:
nested_object = any_of["properties"][prop_name]
if nested_object == {}:
f.write("from typing import Any\n")
f.write(prop_name + " = Any\n")
f.write("\n")
all_options.append(prop_name)
elif "$ref" in nested_object:
ref = nested_object["$ref"]
ref_name = ref[ref.rfind("/") + 1 :]
f.write(
"from ."
+ camel_to_snake(ref_name)
+ " import "
+ ref_name
+ "\n"
)
f.write("\n")
if prop_name != ref_name:
f.write(prop_name + " = " + ref_name + "\n")
f.write("\n")
all_options.append(prop_name)
else:
object_code = generateObjectTypeCode(
prop_name, nested_object, "object", data, None, None
)
f.write(object_code)
f.write("\n")
all_options.append(prop_name)
elif "type" in any_of and any_of["type"] == "string":
enum_code = generateEnumTypeCode(
any_of["enum"][0], any_of, "string", []
)
f.write(enum_code)
f.write("\n")
all_options.append(any_of["enum"][0])
# Write the sum type.
description = getAnyOfDescription(schema)
@ -1677,8 +1737,9 @@ def getRefs(schema: dict) -> List[str]:
# do nothing
pass
else:
logging.error("unsupported type: ", schema)
raise Exception("unsupported type: ", schema)
# This is likely an empty object like above but with a description
# so we will just skip it.
pass
elif type_name == "array":
if "items" in schema:
schema_refs = getRefs(schema["items"])
@ -1722,8 +1783,13 @@ def getEndpointRefs(endpoint: dict, data: dict) -> List[str]:
if "$ref" in items:
ref = items["$ref"].replace("#/components/schemas/", "")
refs.append("List[" + ref + "]")
elif "type" in items:
if items["type"] == "string":
refs.append("List[str]")
else:
raise Exception("Unknown array type", items)
else:
raise Exception("Unknown array type")
raise Exception("Unknown array type", items)
elif json["type"] == "string":
refs.append("str")
elif (
@ -1928,6 +1994,40 @@ def getOneOfRefType(schema: dict) -> str:
raise Exception("Cannot get oneOf ref type for schema: ", schema)
def isNestedObjectAnyOf(schema: dict) -> bool:
if "anyOf" not in schema:
return False
is_nested_object = False
for any_of in schema["anyOf"]:
# Check if each are an object w 1 property in it.
if (
"type" in any_of
and any_of["type"] == "object"
and "properties" in any_of
and len(any_of["properties"]) == 1
):
for prop_name in any_of["properties"]:
nested_object = any_of["properties"][prop_name]
if "type" in nested_object and nested_object["type"] == "object":
is_nested_object = True
else:
is_nested_object = False
break
elif (
"type" in any_of
and any_of["type"] == "string"
and "enum" in any_of
and len(any_of["enum"]) == 1
):
is_nested_object = True
else:
is_nested_object = False
break
return is_nested_object
def isNestedObjectOneOf(schema: dict) -> bool:
if "oneOf" not in schema:
return False
@ -1936,7 +2036,8 @@ def isNestedObjectOneOf(schema: dict) -> bool:
for one_of in schema["oneOf"]:
# Check if each are an object w 1 property in it.
if (
one_of["type"] == "object"
"type" in one_of
and one_of["type"] == "object"
and "properties" in one_of
and len(one_of["properties"]) == 1
):
@ -1948,7 +2049,10 @@ def isNestedObjectOneOf(schema: dict) -> bool:
is_nested_object = False
break
elif (
one_of["type"] == "string" and "enum" in one_of and len(one_of["enum"]) == 1
"type" in one_of
and one_of["type"] == "string"
and "enum" in one_of
and len(one_of["enum"]) == 1
):
is_nested_object = True
else:

File diff suppressed because it is too large Load Diff

View File

@ -41,9 +41,9 @@ def _get_kwargs(
def _parse_response(*, response: httpx.Response) -> Optional[Union[CodeOutput, Error]]:
if response.status_code == 200:
response_200 = CodeOutput(**response.json())
return response_200
if response.status_code == 201:
response_201 = CodeOutput(**response.json())
return response_201
if response.status_code == 400:
response_4XX = Error(**response.json())
return response_4XX

View File

@ -0,0 +1,104 @@
from typing import Any, Dict, List, Optional, Union
import httpx
from ...client import Client
from ...models.error import Error
from ...types import Response
def _get_kwargs(
*,
client: Client,
) -> Dict[str, Any]:
url = "{}/debug/uploads".format(
client.base_url,
) # noqa: E501
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[List[str], Error]]:
if response.status_code == 201:
response_201 = [str(**item) for item in response.json()]
return response_201
if response.status_code == 400:
response_4XX = Error(**response.json())
return response_4XX
if response.status_code == 500:
response_5XX = Error(**response.json())
return response_5XX
return Error(**response.json())
def _build_response(
*, response: httpx.Response
) -> Response[Optional[Union[List[str], Error]]]:
return Response(
status_code=response.status_code,
content=response.content,
headers=response.headers,
parsed=_parse_response(response=response),
)
def sync_detailed(
*,
client: Client,
) -> Response[Optional[Union[List[str], Error]]]:
kwargs = _get_kwargs(
client=client,
)
response = httpx.post(
verify=client.verify_ssl,
**kwargs,
)
return _build_response(response=response)
def sync(
*,
client: Client,
) -> Optional[Union[List[str], Error]]:
"""Do NOT send files here that you don't want to be public.""" # noqa: E501
return sync_detailed(
client=client,
).parsed
async def asyncio_detailed(
*,
client: Client,
) -> Response[Optional[Union[List[str], Error]]]:
kwargs = _get_kwargs(
client=client,
)
async with httpx.AsyncClient(verify=client.verify_ssl) as _client:
response = await _client.post(**kwargs)
return _build_response(response=response)
async def asyncio(
*,
client: Client,
) -> Optional[Union[List[str], Error]]:
"""Do NOT send files here that you don't want to be public.""" # noqa: E501
return (
await asyncio_detailed(
client=client,
)
).parsed

View File

@ -4,10 +4,12 @@ import httpx
from ...client import Client
from ...models.error import Error
from ...models.event import Event
from ...types import Response
def _get_kwargs(
body: Event,
*,
client: Client,
) -> Dict[str, Any]:
@ -23,6 +25,7 @@ def _get_kwargs(
"headers": headers,
"cookies": cookies,
"timeout": client.get_timeout(),
"content": body.model_dump_json(),
}
@ -47,10 +50,12 @@ def _build_response(*, response: httpx.Response) -> Response[Optional[Error]]:
def sync_detailed(
body: Event,
*,
client: Client,
) -> Response[Optional[Error]]:
kwargs = _get_kwargs(
body=body,
client=client,
)
@ -63,21 +68,25 @@ def sync_detailed(
def sync(
body: Event,
*,
client: Client,
) -> Optional[Error]:
"""We collect anonymous telemetry data for improving our product.""" # noqa: E501
return sync_detailed(
body=body,
client=client,
).parsed
async def asyncio_detailed(
body: Event,
*,
client: Client,
) -> Response[Optional[Error]]:
kwargs = _get_kwargs(
body=body,
client=client,
)
@ -88,6 +97,7 @@ async def asyncio_detailed(
async def asyncio(
body: Event,
*,
client: Client,
) -> Optional[Error]:
@ -95,6 +105,7 @@ async def asyncio(
return (
await asyncio_detailed(
body=body,
client=client,
)
).parsed

View File

@ -1,23 +1,26 @@
import json
from typing import Any, Dict, Iterator
from typing import Any, Dict, Iterator, Optional
import bson
from websockets.client import WebSocketClientProtocol, connect as ws_connect_async
from websockets.sync.client import ClientConnection, connect as ws_connect
from ...client import Client
from ...models.post_effect_type import PostEffectType
from ...models.web_socket_request import WebSocketRequest
from ...models.web_socket_response import WebSocketResponse
def _get_kwargs(
fps: int,
post_effect: PostEffectType,
unlocked_framerate: bool,
video_res_height: int,
video_res_width: int,
webrtc: bool,
*,
client: Client,
pool: Optional[str] = None,
) -> Dict[str, Any]:
url = "{}/ws/modeling/commands".format(client.base_url) # noqa: E501
@ -28,6 +31,20 @@ def _get_kwargs(
else:
url = url + "?fps=" + str(fps)
if pool is not None:
if "?" in url:
url = url + "&pool=" + str(pool)
else:
url = url + "?pool=" + str(pool)
if post_effect is not None:
if "?" in url:
url = url + "&post_effect=" + str(post_effect)
else:
url = url + "?post_effect=" + str(post_effect)
if unlocked_framerate is not None:
if "?" in url:
@ -69,17 +86,21 @@ def _get_kwargs(
def sync(
fps: int,
post_effect: PostEffectType,
unlocked_framerate: bool,
video_res_height: int,
video_res_width: int,
webrtc: bool,
*,
client: Client,
pool: Optional[str] = None,
) -> ClientConnection:
"""Pass those commands to the engine via websocket, and pass responses back to the client. Basically, this is a websocket proxy between the frontend/client and the engine.""" # noqa: E501
kwargs = _get_kwargs(
fps=fps,
pool=pool,
post_effect=post_effect,
unlocked_framerate=unlocked_framerate,
video_res_height=video_res_height,
video_res_width=video_res_width,
@ -92,17 +113,21 @@ def sync(
async def asyncio(
fps: int,
post_effect: PostEffectType,
unlocked_framerate: bool,
video_res_height: int,
video_res_width: int,
webrtc: bool,
*,
client: Client,
pool: Optional[str] = None,
) -> WebSocketClientProtocol:
"""Pass those commands to the engine via websocket, and pass responses back to the client. Basically, this is a websocket proxy between the frontend/client and the engine.""" # noqa: E501
kwargs = _get_kwargs(
fps=fps,
pool=pool,
post_effect=post_effect,
unlocked_framerate=unlocked_framerate,
video_res_height=video_res_height,
video_res_width=video_res_width,
@ -126,19 +151,23 @@ class WebSocket:
def __init__(
self,
fps: int,
post_effect: PostEffectType,
unlocked_framerate: bool,
video_res_height: int,
video_res_width: int,
webrtc: bool,
client: Client,
pool: Optional[str] = None,
):
self.ws = sync(
fps,
post_effect,
unlocked_framerate,
video_res_height,
video_res_width,
webrtc,
client=client,
pool=pool,
)
def __enter__(

View File

@ -39,6 +39,7 @@ from .models import (
ModelingCmd,
ModelingCmdId,
Pong,
PostEffectType,
System,
TextToCad,
TextToCadCreateBody,
@ -356,6 +357,7 @@ def test_ws_simple():
with modeling_commands_ws.WebSocket(
client=client,
fps=30,
post_effect=PostEffectType.NOEFFECT,
unlocked_framerate=False,
video_res_height=360,
video_res_width=480,
@ -383,6 +385,7 @@ def test_ws_import():
with modeling_commands_ws.WebSocket(
client=client,
fps=30,
post_effect=PostEffectType.NOEFFECT,
unlocked_framerate=False,
video_res_height=360,
video_res_width=480,

View File

@ -51,6 +51,7 @@ from kittycad.api.hidden import (
post_auth_saml,
)
from kittycad.api.meta import (
create_debug_uploads,
create_event,
get_ipinfo,
get_metadata,
@ -187,7 +188,6 @@ from kittycad.models import (
PaymentIntent,
PaymentMethod,
Pong,
PrivacySettings,
SamlIdentityProvider,
ServiceAccount,
ServiceAccountResultsPage,
@ -222,11 +222,13 @@ from kittycad.models.billing_info import BillingInfo
from kittycad.models.code_language import CodeLanguage
from kittycad.models.created_at_sort_mode import CreatedAtSortMode
from kittycad.models.email_authentication_form import EmailAuthenticationForm
from kittycad.models.event import modeling_app_event
from kittycad.models.file_export_format import FileExportFormat
from kittycad.models.file_import_format import FileImportFormat
from kittycad.models.idp_metadata_source import base64_encoded_xml
from kittycad.models.kcl_code_completion_params import KclCodeCompletionParams
from kittycad.models.kcl_code_completion_request import KclCodeCompletionRequest
from kittycad.models.modeling_app_event_type import ModelingAppEventType
from kittycad.models.modeling_app_individual_subscription_tier import (
ModelingAppIndividualSubscriptionTier,
)
@ -235,6 +237,8 @@ from kittycad.models.modeling_app_organization_subscription_tier import (
)
from kittycad.models.org_details import OrgDetails
from kittycad.models.plan_interval import PlanInterval
from kittycad.models.post_effect_type import PostEffectType
from kittycad.models.privacy_settings import PrivacySettings
from kittycad.models.rtc_sdp_type import RtcSdpType
from kittycad.models.rtc_session_description import RtcSessionDescription
from kittycad.models.saml_identity_provider_create import SamlIdentityProviderCreate
@ -1272,6 +1276,49 @@ async def test_post_auth_saml_async():
)
@pytest.mark.skip
def test_create_debug_uploads():
# Create our client.
client = ClientFromEnv()
result: Optional[Union[List[str], Error]] = create_debug_uploads.sync(
client=client,
)
if isinstance(result, Error) or result is None:
print(result)
raise Exception("Error in response")
body: List[str] = result
print(body)
# OR if you need more info (e.g. status_code)
response: Response[Optional[Union[List[str], Error]]] = (
create_debug_uploads.sync_detailed(
client=client,
)
)
# OR run async
@pytest.mark.asyncio
@pytest.mark.skip
async def test_create_debug_uploads_async():
# Create our client.
client = ClientFromEnv()
result: Optional[Union[List[str], Error]] = await create_debug_uploads.asyncio(
client=client,
)
# OR run async with more info
response: Response[Optional[Union[List[str], Error]]] = (
await create_debug_uploads.asyncio_detailed(
client=client,
)
)
@pytest.mark.skip
def test_create_event():
# Create our client.
@ -1279,6 +1326,13 @@ def test_create_event():
result: Optional[Error] = create_event.sync(
client=client,
body=modeling_app_event(
created_at="<string>",
event_type=ModelingAppEventType.SUCCESSFUL_COMPILE_BEFORE_CLOSE,
project_name="<string>",
source_id="<uuid>",
user_id="<string>",
),
)
if isinstance(result, Error) or result is None:
@ -1291,6 +1345,13 @@ def test_create_event():
# OR if you need more info (e.g. status_code)
response: Response[Optional[Error]] = create_event.sync_detailed(
client=client,
body=modeling_app_event(
created_at="<string>",
event_type=ModelingAppEventType.SUCCESSFUL_COMPILE_BEFORE_CLOSE,
project_name="<string>",
source_id="<uuid>",
user_id="<string>",
),
)
@ -1303,11 +1364,25 @@ async def test_create_event_async():
result: Optional[Error] = await create_event.asyncio(
client=client,
body=modeling_app_event(
created_at="<string>",
event_type=ModelingAppEventType.SUCCESSFUL_COMPILE_BEFORE_CLOSE,
project_name="<string>",
source_id="<uuid>",
user_id="<string>",
),
)
# OR run async with more info
response: Response[Optional[Error]] = await create_event.asyncio_detailed(
client=client,
body=modeling_app_event(
created_at="<string>",
event_type=ModelingAppEventType.SUCCESSFUL_COMPILE_BEFORE_CLOSE,
project_name="<string>",
source_id="<uuid>",
user_id="<string>",
),
)
@ -6732,10 +6807,12 @@ def test_modeling_commands_ws():
with modeling_commands_ws.WebSocket(
client=client,
fps=10,
post_effect=PostEffectType.PHOSPHOR,
unlocked_framerate=False,
video_res_height=10,
video_res_width=10,
webrtc=False,
pool=None, # Optional[str]
) as websocket:
# Send a message.
@ -6766,10 +6843,12 @@ async def test_modeling_commands_ws_async():
websocket = await modeling_commands_ws.asyncio(
client=client,
fps=10,
post_effect=PostEffectType.PHOSPHOR,
unlocked_framerate=False,
video_res_height=10,
video_res_width=10,
webrtc=False,
pool=None, # Optional[str]
)
# Send a message.

View File

@ -31,6 +31,7 @@ from .async_api_call_type import AsyncApiCallType
from .auth_callback import AuthCallback
from .axis import Axis
from .axis_direction_pair import AxisDirectionPair
from .batch_response import BatchResponse
from .billing_info import BillingInfo
from .block_reason import BlockReason
from .cache_metadata import CacheMetadata
@ -56,6 +57,7 @@ from .curve_get_type import CurveGetType
from .curve_type import CurveType
from .customer import Customer
from .customer_balance import CustomerBalance
from .default_camera_focus_on import DefaultCameraFocusOn
from .default_camera_get_settings import DefaultCameraGetSettings
from .default_camera_zoom import DefaultCameraZoom
from .density import Density
@ -80,6 +82,7 @@ from .entity_type import EntityType
from .environment import Environment
from .error import Error
from .error_code import ErrorCode
from .event import Event
from .export import Export
from .export_file import ExportFile
from .extended_user import ExtendedUser
@ -102,6 +105,7 @@ from .file_system_metadata import FileSystemMetadata
from .file_volume import FileVolume
from .gateway import Gateway
from .get_entity_type import GetEntityType
from .get_num_objects import GetNumObjects
from .get_sketch_mode_plane import GetSketchModePlane
from .global_axis import GlobalAxis
from .gltf_presentation import GltfPresentation
@ -112,6 +116,7 @@ from .idp_metadata_source import IdpMetadataSource
from .image_format import ImageFormat
from .import_file import ImportFile
from .import_files import ImportFiles
from .imported_geometry import ImportedGeometry
from .input_format import InputFormat
from .invoice import Invoice
from .invoice_line_item import InvoiceLineItem
@ -130,6 +135,7 @@ from .mass import Mass
from .meta_cluster_info import MetaClusterInfo
from .metadata import Metadata
from .method import Method
from .modeling_app_event_type import ModelingAppEventType
from .modeling_app_individual_subscription_tier import (
ModelingAppIndividualSubscriptionTier,
)
@ -174,6 +180,7 @@ from .ply_storage import PlyStorage
from .point2d import Point2d
from .point3d import Point3d
from .pong import Pong
from .post_effect_type import PostEffectType
from .privacy_settings import PrivacySettings
from .raw_file import RawFile
from .rtc_ice_candidate_init import RtcIceCandidateInit

View File

@ -0,0 +1,24 @@
from typing import Union
from pydantic import BaseModel, ConfigDict, RootModel
class response(BaseModel):
"""Response to the modeling command."""
model_config = ConfigDict(protected_namespaces=())
class errors(BaseModel):
"""Errors that occurred during the modeling command."""
model_config = ConfigDict(protected_namespaces=())
BatchResponse = RootModel[
Union[
response,
errors,
]
]

View File

@ -0,0 +1,9 @@
from pydantic import BaseModel, ConfigDict
class DefaultCameraFocusOn(BaseModel):
"""The response from the `DefaultCameraFocusOn` command."""
model_config = ConfigDict(protected_namespaces=())

34
kittycad/models/event.py Normal file
View File

@ -0,0 +1,34 @@
import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field, RootModel
from typing_extensions import Annotated
from ..models.modeling_app_event_type import ModelingAppEventType
class modeling_app_event(BaseModel):
"""An event related to modeling app files"""
attachment_uri: Optional[str] = None
created_at: datetime.datetime
event_type: ModelingAppEventType
last_compiled_at: Optional[datetime.datetime] = None
project_description: Optional[str] = None
project_name: str
source_id: str
type: Literal["modeling_app_event"] = "modeling_app_event"
user_id: str
model_config = ConfigDict(protected_namespaces=())
Event = RootModel[Annotated[Union[modeling_app_event,], Field(discriminator="type")]]

View File

@ -0,0 +1,11 @@
from pydantic import BaseModel, ConfigDict
class GetNumObjects(BaseModel):
"""The response from the `GetNumObjects` command."""
num_objects: int
model_config = ConfigDict(protected_namespaces=())

View File

@ -7,6 +7,8 @@ from ..models.point3d import Point3d
class GetSketchModePlane(BaseModel):
"""The plane for sketch mode."""
origin: Point3d
x_axis: Point3d
y_axis: Point3d

View File

@ -0,0 +1,14 @@
from typing import List
from pydantic import BaseModel, ConfigDict
class ImportedGeometry(BaseModel):
"""Data from importing the files"""
id: str
value: List[str]
model_config = ConfigDict(protected_namespaces=())

View File

@ -0,0 +1,11 @@
from enum import Enum
class ModelingAppEventType(str, Enum):
"""Type for modeling-app events""" # noqa: E501
"""# This event is sent before the modeling app or project is closed. The attachment should contain the contents of the most recent successful compile. """ # noqa: E501
SUCCESSFUL_COMPILE_BEFORE_CLOSE = "successful_compile_before_close"
def __str__(self) -> str:
return str(self.value)

View File

@ -3,6 +3,7 @@ from typing import List, Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field, RootModel
from typing_extensions import Annotated
from ..models.angle import Angle
from ..models.annotation_options import AnnotationOptions
from ..models.annotation_type import AnnotationType
from ..models.camera_drag_interaction_type import CameraDragInteractionType
@ -63,7 +64,7 @@ class extend_path(BaseModel):
class extrude(BaseModel):
"""Command for extruding a solid."""
"""Command for extruding a solid 2d."""
cap: bool
@ -76,6 +77,42 @@ class extrude(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class revolve(BaseModel):
"""Command for revolving a solid 2d."""
angle: Angle
axis: Point3d
axis_is_2d: bool
origin: Point3d
target: ModelingCmdId
tolerance: LengthUnit
type: Literal["revolve"] = "revolve"
model_config = ConfigDict(protected_namespaces=())
class revolve_about_edge(BaseModel):
"""Command for revolving a solid 2d about a brep edge"""
angle: Angle
edge_id: str
target: ModelingCmdId
tolerance: LengthUnit
type: Literal["revolve_about_edge"] = "revolve_about_edge"
model_config = ConfigDict(protected_namespaces=())
class close_path(BaseModel):
"""Closes a path, converting it to a 2D solid."""
@ -221,8 +258,6 @@ class export(BaseModel):
format: OutputFormat
source_unit: UnitLength
type: Literal["export"] = "export"
model_config = ConfigDict(protected_namespaces=())
@ -285,7 +320,7 @@ class entity_get_distance(BaseModel):
class entity_linear_pattern(BaseModel):
"""Create a linear pattern using this entity (currently only valid for 3D solids)."""
"""Create a linear pattern using this entity."""
axis: Point3d
@ -301,7 +336,7 @@ class entity_linear_pattern(BaseModel):
class entity_circular_pattern(BaseModel):
"""Create a circular pattern using this entity (currently only valid for 3D solids)."""
"""Create a circular pattern using this entity."""
arc_degrees: float
@ -320,6 +355,24 @@ class entity_circular_pattern(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class entity_make_helix(BaseModel):
"""Create a helix using the input cylinder and other specified parameters."""
cylinder_id: str
is_clockwise: bool
length: LengthUnit
revolutions: float
start_angle: Angle
type: Literal["entity_make_helix"] = "entity_make_helix"
model_config = ConfigDict(protected_namespaces=())
class edit_mode_enter(BaseModel):
"""Enter edit mode"""
@ -362,6 +415,14 @@ class select_remove(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class scene_clear_all(BaseModel):
"""Removes all of the Objects in the scene"""
type: Literal["scene_clear_all"] = "scene_clear_all"
model_config = ConfigDict(protected_namespaces=())
class select_replace(BaseModel):
"""Replaces current selection with these entities (by UUID)."""
@ -420,6 +481,16 @@ class update_annotation(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class edge_lines_visible(BaseModel):
"""Changes visibility of scene-wide edge lines on brep solids"""
hidden: bool
type: Literal["edge_lines_visible"] = "edge_lines_visible"
model_config = ConfigDict(protected_namespaces=())
class object_visible(BaseModel):
"""Hide or show an object"""
@ -764,6 +835,36 @@ class enable_sketch_mode(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class set_background_color(BaseModel):
"""Set the background color of the scene."""
color: Color
type: Literal["set_background_color"] = "set_background_color"
model_config = ConfigDict(protected_namespaces=())
class set_current_tool_properties(BaseModel):
"""Set the properties of the tool lines for the scene."""
color: Optional[Color] = None
type: Literal["set_current_tool_properties"] = "set_current_tool_properties"
model_config = ConfigDict(protected_namespaces=())
class set_default_system_properties(BaseModel):
"""Set the default system properties used when a specific property isn't set."""
color: Optional[Color] = None
type: Literal["set_default_system_properties"] = "set_default_system_properties"
model_config = ConfigDict(protected_namespaces=())
class curve_get_type(BaseModel):
"""Get type of the given curve."""
@ -951,8 +1052,6 @@ class mass(BaseModel):
output_unit: UnitMass
source_unit: UnitLength
type: Literal["mass"] = "mass"
model_config = ConfigDict(protected_namespaces=())
@ -969,8 +1068,6 @@ class density(BaseModel):
output_unit: UnitDensity
source_unit: UnitLength
type: Literal["density"] = "density"
model_config = ConfigDict(protected_namespaces=())
@ -983,8 +1080,6 @@ class volume(BaseModel):
output_unit: UnitVolume
source_unit: UnitLength
type: Literal["volume"] = "volume"
model_config = ConfigDict(protected_namespaces=())
@ -997,8 +1092,6 @@ class center_of_mass(BaseModel):
output_unit: UnitLength
source_unit: UnitLength
type: Literal["center_of_mass"] = "center_of_mass"
model_config = ConfigDict(protected_namespaces=())
@ -1011,8 +1104,6 @@ class surface_area(BaseModel):
output_unit: UnitArea
source_unit: UnitLength
type: Literal["surface_area"] = "surface_area"
model_config = ConfigDict(protected_namespaces=())
@ -1066,6 +1157,18 @@ class default_camera_set_perspective(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class zoom_to_fit(BaseModel):
"""Fit the view to the specified object(s)."""
object_ids: Optional[List[str]] = None
padding: float
type: Literal["zoom_to_fit"] = "zoom_to_fit"
model_config = ConfigDict(protected_namespaces=())
class solid3d_get_extrusion_face_info(BaseModel):
"""Get a concise description of all of an extrusion's faces."""
@ -1102,6 +1205,14 @@ class select_get(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class get_num_objects(BaseModel):
"""Get the number of objects in the scene"""
type: Literal["get_num_objects"] = "get_num_objects"
model_config = ConfigDict(protected_namespaces=())
ModelingCmd = RootModel[
Annotated[
Union[
@ -1109,6 +1220,8 @@ ModelingCmd = RootModel[
move_path_pen,
extend_path,
extrude,
revolve,
revolve_about_edge,
close_path,
camera_drag_start,
camera_drag_move,
@ -1127,15 +1240,18 @@ ModelingCmd = RootModel[
entity_get_distance,
entity_linear_pattern,
entity_circular_pattern,
entity_make_helix,
edit_mode_enter,
select_with_point,
select_add,
select_remove,
scene_clear_all,
select_replace,
highlight_set_entity,
highlight_set_entities,
new_annotation,
update_annotation,
edge_lines_visible,
object_visible,
object_bring_to_front,
object_set_material_params_pbr,
@ -1163,6 +1279,9 @@ ModelingCmd = RootModel[
get_sketch_mode_plane,
curve_set_constraint,
enable_sketch_mode,
set_background_color,
set_current_tool_properties,
set_default_system_properties,
curve_get_type,
curve_get_control_points,
take_snapshot,
@ -1189,10 +1308,12 @@ ModelingCmd = RootModel[
set_selection_filter,
default_camera_set_orthographic,
default_camera_set_perspective,
zoom_to_fit,
solid3d_get_extrusion_face_info,
edit_mode_exit,
select_clear,
select_get,
get_num_objects,
],
Field(discriminator="type"),
]

View File

@ -9,6 +9,7 @@ from ..models.center_of_mass import CenterOfMass
from ..models.curve_get_control_points import CurveGetControlPoints
from ..models.curve_get_end_points import CurveGetEndPoints
from ..models.curve_get_type import CurveGetType
from ..models.default_camera_focus_on import DefaultCameraFocusOn
from ..models.default_camera_get_settings import DefaultCameraGetSettings
from ..models.default_camera_zoom import DefaultCameraZoom
from ..models.density import Density
@ -25,9 +26,11 @@ from ..models.face_get_gradient import FaceGetGradient
from ..models.face_get_position import FaceGetPosition
from ..models.face_is_planar import FaceIsPlanar
from ..models.get_entity_type import GetEntityType
from ..models.get_num_objects import GetNumObjects
from ..models.get_sketch_mode_plane import GetSketchModePlane
from ..models.highlight_set_entity import HighlightSetEntity
from ..models.import_files import ImportFiles
from ..models.imported_geometry import ImportedGeometry
from ..models.mass import Mass
from ..models.mouse_click import MouseClick
from ..models.path_get_curve_uuids_for_vertices import PathGetCurveUuidsForVertices
@ -166,6 +169,26 @@ class default_camera_zoom(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class get_num_objects(BaseModel):
"""The response to the 'GetNumObjects' endpoint"""
data: GetNumObjects
type: Literal["get_num_objects"] = "get_num_objects"
model_config = ConfigDict(protected_namespaces=())
class default_camera_focus_on(BaseModel):
"""The response to the 'DefaultCameraFocusOn' endpoint"""
data: DefaultCameraFocusOn
type: Literal["default_camera_focus_on"] = "default_camera_focus_on"
model_config = ConfigDict(protected_namespaces=())
class select_get(BaseModel):
"""The response to the 'SelectGet' endpoint"""
@ -378,6 +401,16 @@ class import_files(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class imported_geometry(BaseModel):
"""The response to the 'ImportedGeometry' endpoint"""
data: ImportedGeometry
type: Literal["imported_geometry"] = "imported_geometry"
model_config = ConfigDict(protected_namespaces=())
class mass(BaseModel):
"""The response to the 'Mass' endpoint"""
@ -503,6 +536,8 @@ OkModelingCmdResponse = RootModel[
camera_drag_end,
default_camera_get_settings,
default_camera_zoom,
get_num_objects,
default_camera_focus_on,
select_get,
solid3d_get_all_edge_faces,
solid3d_get_all_opposite_edges,
@ -524,6 +559,7 @@ OkModelingCmdResponse = RootModel[
face_get_gradient,
plane_intersect_and_project,
import_files,
imported_geometry,
mass,
volume,
density,

View File

@ -1,8 +1,9 @@
from typing import List, Literal, Union
from typing import Dict, List, Literal, Union
from pydantic import BaseModel, ConfigDict, Field, RootModel
from typing_extensions import Annotated
from ..models.batch_response import BatchResponse
from ..models.ice_server import IceServer
from ..models.ok_modeling_cmd_response import OkModelingCmdResponse
from ..models.raw_file import RawFile
@ -82,6 +83,24 @@ class modeling(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class ModelingBatchData(BaseModel):
""""""
responses: Dict[str, BatchResponse]
model_config = ConfigDict(protected_namespaces=())
class modeling_batch(BaseModel):
"""Response to a ModelingBatch."""
data: ModelingBatchData
type: Literal["modeling_batch"] = "modeling_batch"
model_config = ConfigDict(protected_namespaces=())
class ExportData(BaseModel):
""""""
@ -139,6 +158,7 @@ OkWebSocketResponseData = RootModel[
trickle_ice,
sdp_answer,
modeling,
modeling_batch,
export,
metrics_request,
pong,

View File

@ -0,0 +1,12 @@
from enum import Enum
class PostEffectType(str, Enum):
"""Post effect type""" # noqa: E501
PHOSPHOR = "phosphor"
SSAO = "ssao"
NOEFFECT = "noeffect"
def __str__(self) -> str:
return str(self.value)

View File

@ -1,4 +1,4 @@
from typing import List, Literal, Union
from typing import Dict, List, Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field, RootModel
from typing_extensions import Annotated
@ -46,8 +46,12 @@ class modeling_cmd_req(BaseModel):
class modeling_cmd_batch_req(BaseModel):
"""A sequence of modeling requests. If any request fails, following requests will not be tried."""
batch_id: ModelingCmdId
requests: List[ModelingCmdReq]
responses: Optional[bool] = None
type: Literal["modeling_cmd_batch_req"] = "modeling_cmd_batch_req"
model_config = ConfigDict(protected_namespaces=())
@ -71,6 +75,16 @@ class metrics_response(BaseModel):
model_config = ConfigDict(protected_namespaces=())
class headers(BaseModel):
"""Authentication header request."""
headers: Dict[str, str]
type: Literal["headers"] = "headers"
model_config = ConfigDict(protected_namespaces=())
WebSocketRequest = RootModel[
Annotated[
Union[
@ -80,6 +94,7 @@ WebSocketRequest = RootModel[
modeling_cmd_batch_req,
ping,
metrics_response,
headers,
],
Field(discriminator="type"),
]

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "kittycad"
version = "0.6.8"
version = "0.6.13"
description = "A client library for accessing KittyCAD"
authors = []
@ -11,6 +11,10 @@ packages = [
]
include = ["CHANGELOG.md", "kittycad/py.typed"]
[[tool.poetry.source]]
name = "pypi-public"
url = "https://pypi.org/simple/"
[tool.poetry.dependencies]
attrs = ">=20.1.0,<24.0.0"
httpx = ">=0.15.4,<0.26.0"

910
spec.json

File diff suppressed because it is too large Load Diff