Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d71b616e2b | |||
| 35750f12c5 | |||
| 82b6dbbe78 | |||
| 883be4b27b | |||
| 8dcc67fb31 | |||
| ed84820995 | |||
| 5c3dcb7bc0 | |||
| a0e10aae99 | |||
| bbea2a0d76 | |||
| a05ac3479b | |||
| 0bd972945e | |||
| f200544065 | |||
| 68ce9823ae | |||
| a920352407 | |||
| bd38dfacc7 | |||
| 281483f0fc | |||
| 932838de8f | |||
| a0b39e4c64 | |||
| 759cd0ef58 | |||
| 46c18bbe6b | |||
| 82d39e66fe | |||
| 10f7499182 | |||
| 170d2f0450 | |||
| 040a4e2553 | |||
| e978e4f632 | |||
| eae60160a1 | |||
| c78a780e85 | |||
| 1b45f50697 | |||
| be8fae3b1c | |||
| ab41d3cbe0 | |||
| f843bb0c89 | |||
| b7933e0088 | |||
| 7e09d4f4ce | |||
| bb62109332 | |||
| 3642731f37 | |||
| 3bd849c815 | |||
| 2acf4c41c7 | |||
| 6b6ff80bf2 | |||
| 0f1f00db00 | |||
| 280927b720 | |||
| 6096cd25f6 | |||
| cc004c8e6b | |||
| a10b2594d3 | |||
| 976a52bdc8 | |||
| 09ca501a74 | |||
| 225d4f02d4 | |||
| 537a504121 | |||
| 6c03dc82c8 | |||
| 780126528d |
@@ -2,7 +2,7 @@ repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
hooks:
|
||||
- id: ruff
|
||||
rev: v0.1.6
|
||||
rev: v0.8.2
|
||||
|
||||
- repo: https://github.com/commitizen-tools/commitizen
|
||||
hooks:
|
||||
@@ -13,12 +13,12 @@ repos:
|
||||
rev: v3.13.0
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.12.0
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.11.0
|
||||
rev: 24.10.0
|
||||
hooks:
|
||||
- id: black
|
||||
# It is recommended to specify the latest version of Python
|
||||
|
||||
@@ -2,46 +2,16 @@
|
||||
<img src="https://user-images.githubusercontent.com/2679513/131189167-18ea5fe1-c578-47f6-9785-3748178e4312.png" width="150px"/><br/>
|
||||
Speckle | specklepy 🐍
|
||||
</h1>
|
||||
|
||||
<p align="center"><a href="https://twitter.com/SpeckleSystems"><img src="https://img.shields.io/twitter/follow/SpeckleSystems?style=social" alt="Twitter Follow"></a> <a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&style=flat-square&logo=discourse&logoColor=white" alt="Community forum users"></a> <a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a> <a href="https://speckle.guide/dev/"><img src="https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white" alt="docs"></a></p>
|
||||
|
||||
> Speckle is the first AEC data hub that connects with your favorite AEC tools. Speckle exists to overcome the challenges of working in a fragmented industry where communication, creative workflows, and the exchange of data are often hindered by siloed software and processes. It is here to make the industry better.
|
||||
|
||||
<h3 align="center">
|
||||
The Python SDK
|
||||
</h3>
|
||||
<p align="center"><b>Speckle</b> is the data infrastructure for the AEC industry.</p><br/>
|
||||
|
||||
<p align="center"><a href="https://twitter.com/SpeckleSystems"><img src="https://img.shields.io/twitter/follow/SpeckleSystems?style=social" alt="Twitter Follow"></a> <a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&style=flat-square&logo=discourse&logoColor=white" alt="Community forum users"></a> <a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a> <a href="https://speckle.guide/dev/"><img src="https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white" alt="docs"></a></p>
|
||||
<p align="center"><a href="https://github.com/specklesystems/specklepy/"><img src="https://circleci.com/gh/specklesystems/specklepy.svg?style=svg&circle-token=76eabd350ea243575cbb258b746ed3f471f7ac29" alt="Speckle-Next"></a><a href="https://codecov.io/gh/specklesystems/specklepy">
|
||||
<img src="https://codecov.io/gh/specklesystems/specklepy/branch/main/graph/badge.svg?token=8KQFL5N0YF"/>
|
||||
</a> </p>
|
||||
|
||||
# About Speckle
|
||||
|
||||
What is Speckle? Check our 
|
||||
|
||||
### Features
|
||||
|
||||
- **Object-based:** say goodbye to files! Speckle is the first object based platform for the AEC industry
|
||||
- **Version control:** Speckle is the Git & Hub for geometry and BIM data
|
||||
- **Collaboration:** share your designs collaborate with others
|
||||
- **3D Viewer:** see your CAD and BIM models online, share and embed them anywhere
|
||||
- **Interoperability:** get your CAD and BIM models into other software without exporting or importing
|
||||
- **Real time:** get real time updates and notifications and changes
|
||||
- **GraphQL API:** get what you need anywhere you want it
|
||||
- **Webhooks:** the base for a automation and next-gen pipelines
|
||||
- **Built for developers:** we are building Speckle with developers in mind and got tools for every stack
|
||||
- **Built for the AEC industry:** Speckle connectors are plugins for the most common software used in the industry such as Revit, Rhino, Grasshopper, AutoCAD, Civil 3D, Excel, Unreal Engine, Unity, QGIS, Blender and more!
|
||||
|
||||
### Try Speckle now!
|
||||
|
||||
Give Speckle a try in no time by:
|
||||
|
||||
- [](https://app.speckle.systems) ⇒ creating an account at our public server
|
||||
- [](https://marketplace.digitalocean.com/apps/speckle-server?refcode=947a2b5d7dc1) ⇒ deploying an instance in 1 click
|
||||
|
||||
### Resources
|
||||
|
||||
- [](https://speckle.community) for help, feature requests or just to hang with other speckle enthusiasts, check out our community forum!
|
||||
- [](https://speckle.systems) our tutorials portal is full of resources to get you started using Speckle
|
||||
- [](https://speckle.guide/dev/) reference on almost any end-user and developer functionality
|
||||
|
||||
<p align="center"><a href="https://codecov.io/gh/specklesystems/specklepy"><img src="https://codecov.io/gh/specklesystems/specklepy/branch/main/graph/badge.svg?token=8KQFL5N0YF" alt="Codecov"></a></p>
|
||||
|
||||
# Repo structure
|
||||
|
||||
|
||||
Generated
+806
-706
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -15,7 +15,7 @@ packages = [
|
||||
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.8.0, <4.0"
|
||||
python = ">=3.9.0, <4.0"
|
||||
pydantic = "^2.5"
|
||||
appdirs = "^1.4.4"
|
||||
gql = { extras = ["requests", "websockets"], version = "^3.3.0" }
|
||||
@@ -26,18 +26,19 @@ attrs = "^23.1.0"
|
||||
httpx = "^0.25.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "23.11.0"
|
||||
isort = "^5.7.0"
|
||||
black = "24.10.0"
|
||||
isort = "^5.13.2"
|
||||
pytest = "^7.1.3"
|
||||
pytest-asyncio = "^0.23.0"
|
||||
pytest-ordering = "^0.6"
|
||||
pytest-cov = "^3.0.0"
|
||||
devtools = "^0.8.0"
|
||||
pylint = "^2.14.4"
|
||||
pylint = "^3.3.2"
|
||||
pydantic-settings = "^2.3.0"
|
||||
mypy = "^0.982"
|
||||
pre-commit = "^2.20.0"
|
||||
commitizen = "^2.38.0"
|
||||
ruff = "^0.4.4"
|
||||
commitizen = "^3.13.0"
|
||||
ruff = "^0.8.2"
|
||||
types-deprecated = "^1.2.9"
|
||||
types-ujson = "^5.6.0.0"
|
||||
types-requests = "^2.28.11.5"
|
||||
@@ -59,8 +60,7 @@ exclude = '''
|
||||
'''
|
||||
include = '\.pyi?$'
|
||||
line-length = 88
|
||||
target-version = ["py37", "py38", "py39", "py310", "py311"]
|
||||
|
||||
target-version = ["py39", "py310", "py311", "py312", "py313"]
|
||||
|
||||
[tool.commitizen]
|
||||
name = "cz_conventional_commits"
|
||||
|
||||
@@ -206,6 +206,7 @@ class AutomationContext:
|
||||
query = gql(
|
||||
"""
|
||||
mutation AutomateFunctionRunStatusReport(
|
||||
$projectId: String!
|
||||
$functionRunId: String!
|
||||
$status: AutomateRunStatus!
|
||||
$statusMessage: String
|
||||
@@ -213,6 +214,7 @@ class AutomationContext:
|
||||
$contextView: String
|
||||
){
|
||||
automateFunctionRunStatusReport(input: {
|
||||
projectId: $projectId
|
||||
functionRunId: $functionRunId
|
||||
status: $status
|
||||
statusMessage: $statusMessage
|
||||
@@ -236,6 +238,7 @@ class AutomationContext:
|
||||
object_results = None
|
||||
|
||||
params = {
|
||||
"projectId": self.automation_run_data.project_id,
|
||||
"functionRunId": self.automation_run_data.function_run_id,
|
||||
"status": self.run_status.value,
|
||||
"statusMessage": self._automation_result.status_message,
|
||||
@@ -355,6 +358,24 @@ class AutomationContext:
|
||||
visual_overrides,
|
||||
)
|
||||
|
||||
def attach_success_to_objects(
|
||||
self,
|
||||
category: str,
|
||||
object_ids: Union[str, List[str]],
|
||||
message: Optional[str] = None,
|
||||
metadata: Optional[Dict[str, Any]] = None,
|
||||
visual_overrides: Optional[Dict[str, Any]] = None,
|
||||
) -> None:
|
||||
"""Add a new success case to the run results."""
|
||||
self.attach_result_to_objects(
|
||||
ObjectResultLevel.SUCCESS,
|
||||
category,
|
||||
object_ids,
|
||||
message,
|
||||
metadata,
|
||||
visual_overrides,
|
||||
)
|
||||
|
||||
def attach_info_to_objects(
|
||||
self,
|
||||
category: str,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Some useful helpers for working with automation data."""
|
||||
|
||||
import secrets
|
||||
import string
|
||||
|
||||
|
||||
@@ -61,15 +61,13 @@ def _parse_input_data(
|
||||
def execute_automate_function(
|
||||
automate_function: AutomateFunction[T],
|
||||
input_schema: type[T],
|
||||
) -> None:
|
||||
...
|
||||
) -> None: ...
|
||||
|
||||
|
||||
@overload
|
||||
def execute_automate_function(
|
||||
automate_function: AutomateFunctionWithoutInputs,
|
||||
) -> None:
|
||||
...
|
||||
) -> None: ...
|
||||
|
||||
|
||||
class AutomateGenerateJsonSchema(GenerateJsonSchema):
|
||||
@@ -146,16 +144,14 @@ def run_function(
|
||||
automation_context: AutomationContext,
|
||||
automate_function: AutomateFunction[T],
|
||||
inputs: T,
|
||||
) -> AutomationContext:
|
||||
...
|
||||
) -> AutomationContext: ...
|
||||
|
||||
|
||||
@overload
|
||||
def run_function(
|
||||
automation_context: AutomationContext,
|
||||
automate_function: AutomateFunctionWithoutInputs,
|
||||
) -> AutomationContext:
|
||||
...
|
||||
) -> AutomationContext: ...
|
||||
|
||||
|
||||
def run_function(
|
||||
|
||||
@@ -69,6 +69,7 @@ class AutomationStatus(str, Enum):
|
||||
class ObjectResultLevel(str, Enum):
|
||||
"""Possible status message levels for object reports."""
|
||||
|
||||
SUCCESS = "SUCCESS"
|
||||
INFO = "INFO"
|
||||
WARNING = "WARNING"
|
||||
ERROR = "ERROR"
|
||||
|
||||
+54
-16
@@ -2,12 +2,17 @@ from deprecated import deprecated
|
||||
|
||||
from specklepy.api.credentials import Account
|
||||
from specklepy.api.resources import (
|
||||
active_user,
|
||||
ActiveUserResource,
|
||||
ModelResource,
|
||||
OtherUserResource,
|
||||
ProjectInviteResource,
|
||||
ProjectResource,
|
||||
ServerResource,
|
||||
SubscriptionResource,
|
||||
VersionResource,
|
||||
branch,
|
||||
commit,
|
||||
object,
|
||||
other_user,
|
||||
server,
|
||||
stream,
|
||||
subscriptions,
|
||||
user,
|
||||
@@ -64,32 +69,65 @@ class SpeckleClient(CoreSpeckleClient):
|
||||
self.account = Account()
|
||||
|
||||
def _init_resources(self) -> None:
|
||||
self.server = server.Resource(
|
||||
self.server = ServerResource(
|
||||
account=self.account, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
|
||||
server_version = None
|
||||
try:
|
||||
server_version = self.server.version()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.other_user = OtherUserResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.active_user = ActiveUserResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.project = ProjectResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.project_invite = ProjectInviteResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.model = ModelResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.version = VersionResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.subscription = SubscriptionResource(
|
||||
account=self.account,
|
||||
basepath=self.ws_url,
|
||||
client=self.wsclient,
|
||||
# todo: why doesn't this take a server version
|
||||
)
|
||||
# Deprecated Resources
|
||||
self.user = user.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.other_user = other_user.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.active_user = active_user.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.stream = stream.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
|
||||
@@ -1,9 +1,41 @@
|
||||
import pkgutil
|
||||
import sys
|
||||
from importlib import import_module
|
||||
from specklepy.api.resources.current.active_user_resource import ActiveUserResource
|
||||
from specklepy.api.resources.current.model_resource import ModelResource
|
||||
from specklepy.api.resources.current.other_user_resource import OtherUserResource
|
||||
from specklepy.api.resources.current.project_invite_resource import (
|
||||
ProjectInviteResource,
|
||||
)
|
||||
from specklepy.api.resources.current.project_resource import ProjectResource
|
||||
from specklepy.api.resources.current.server_resource import ServerResource
|
||||
from specklepy.api.resources.current.subscription_resource import SubscriptionResource
|
||||
from specklepy.api.resources.current.version_resource import VersionResource
|
||||
from specklepy.api.resources.deprecated import (
|
||||
active_user,
|
||||
branch,
|
||||
commit,
|
||||
object,
|
||||
other_user,
|
||||
server,
|
||||
stream,
|
||||
subscriptions,
|
||||
user,
|
||||
)
|
||||
|
||||
for _, name, _ in pkgutil.iter_modules(__path__):
|
||||
imported_module = import_module("." + name, package=__name__)
|
||||
|
||||
if hasattr(imported_module, "Resource"):
|
||||
setattr(sys.modules[__name__], name, imported_module)
|
||||
__all__ = [
|
||||
"ActiveUserResource",
|
||||
"ModelResource",
|
||||
"OtherUserResource",
|
||||
"ProjectInviteResource",
|
||||
"ProjectResource",
|
||||
"ServerResource",
|
||||
"SubscriptionResource",
|
||||
"VersionResource",
|
||||
"active_user",
|
||||
"branch",
|
||||
"commit",
|
||||
"object",
|
||||
"other_user",
|
||||
"server",
|
||||
"stream",
|
||||
"subscriptions",
|
||||
"user",
|
||||
]
|
||||
|
||||
+63
-24
@@ -1,12 +1,24 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, overload
|
||||
|
||||
from specklepy.api.models import PendingStreamCollaborator, User
|
||||
from specklepy.core.api.resources.active_user import Resource as CoreResource
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.core.api.inputs.user_inputs import UserProjectsFilter, UserUpdateInput
|
||||
from specklepy.core.api.models import (
|
||||
PendingStreamCollaborator,
|
||||
Project,
|
||||
ResourceCollection,
|
||||
User,
|
||||
)
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resources import ActiveUserResource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
|
||||
|
||||
class Resource(CoreResource):
|
||||
class ActiveUserResource(CoreResource):
|
||||
"""API Access class for users. This class provides methods to get and update
|
||||
the user profile, fetch user activity, and manage pending stream invitations."""
|
||||
|
||||
@@ -19,37 +31,62 @@ class Resource(CoreResource):
|
||||
)
|
||||
self.schema = User
|
||||
|
||||
def get(self) -> User:
|
||||
"""Gets the profile of the current authenticated user's profile
|
||||
(as extracted from the authorization header).
|
||||
|
||||
Returns:
|
||||
User -- the retrieved user
|
||||
"""
|
||||
metrics.track(metrics.SDK, custom_props={"name": "User Active Get"})
|
||||
def get(self) -> Optional[User]:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Active User Get"})
|
||||
return super().get()
|
||||
|
||||
@deprecated("Use UserUpdateInput overload", version=FE1_DEPRECATION_VERSION)
|
||||
@overload
|
||||
def update(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
company: Optional[str] = None,
|
||||
bio: Optional[str] = None,
|
||||
avatar: Optional[str] = None,
|
||||
) -> User: ...
|
||||
|
||||
@overload
|
||||
def update(self, *, input: UserUpdateInput) -> User: ...
|
||||
|
||||
def update(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
company: Optional[str] = None,
|
||||
bio: Optional[str] = None,
|
||||
avatar: Optional[str] = None,
|
||||
):
|
||||
"""Updates your user profile. All arguments are optional.
|
||||
*,
|
||||
input: Optional[UserUpdateInput] = None,
|
||||
) -> User:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Active User Update"})
|
||||
if isinstance(input, UserUpdateInput):
|
||||
return super()._update(input=input)
|
||||
else:
|
||||
return super()._update(
|
||||
input=UserUpdateInput(
|
||||
name=name,
|
||||
company=company,
|
||||
bio=bio,
|
||||
avatar=avatar,
|
||||
)
|
||||
)
|
||||
|
||||
Args:
|
||||
name (Optional[str]): The user's name.
|
||||
company (Optional[str]): The company the user works for.
|
||||
bio (Optional[str]): A brief user biography.
|
||||
avatar (Optional[str]): A URL to an avatar image for the user.
|
||||
def get_projects(
|
||||
self,
|
||||
*,
|
||||
limit: int = 25,
|
||||
cursor: Optional[str] = None,
|
||||
filter: Optional[UserProjectsFilter] = None,
|
||||
) -> ResourceCollection[Project]:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Active User Get Projects"})
|
||||
return super().get_projects(limit=limit, cursor=cursor, filter=filter)
|
||||
|
||||
Returns @deprecated(version=DEPRECATION_VERSION, reason=DEPRECATION_TEXT):
|
||||
bool -- True if your profile was updated successfully
|
||||
"""
|
||||
metrics.track(metrics.SDK, self.account, {"name": "User Active Update"})
|
||||
return super().update(name, company, bio, avatar)
|
||||
def get_project_invites(self) -> List[PendingStreamCollaborator]:
|
||||
metrics.track(
|
||||
metrics.SDK, self.account, {"name": "Active User Get Project Invites"}
|
||||
)
|
||||
return super().get_project_invites()
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def activity(
|
||||
self,
|
||||
limit: int = 20,
|
||||
@@ -78,6 +115,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "User Active Activity"})
|
||||
return super().activity(limit, action_type, before, after, cursor)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get_all_pending_invites(self) -> List[PendingStreamCollaborator]:
|
||||
"""Fetches all of the current user's pending stream invitations.
|
||||
|
||||
@@ -89,6 +127,7 @@ class Resource(CoreResource):
|
||||
)
|
||||
return super().get_all_pending_invites()
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get_pending_invite(
|
||||
self, stream_id: str, token: Optional[str] = None
|
||||
) -> Optional[PendingStreamCollaborator]:
|
||||
@@ -0,0 +1,74 @@
|
||||
from typing import Optional
|
||||
|
||||
from specklepy.core.api.inputs.model_inputs import (
|
||||
CreateModelInput,
|
||||
DeleteModelInput,
|
||||
ModelVersionsFilter,
|
||||
UpdateModelInput,
|
||||
)
|
||||
from specklepy.core.api.inputs.project_inputs import ProjectModelsFilter
|
||||
from specklepy.core.api.models import Model, ModelWithVersions, ResourceCollection
|
||||
from specklepy.core.api.resources import ModelResource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
|
||||
|
||||
class ModelResource(CoreResource):
|
||||
"""API Access class for models"""
|
||||
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
server_version=server_version,
|
||||
)
|
||||
|
||||
def get(self, model_id: str, project_id: str) -> Model:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Model Get"})
|
||||
return super().get(model_id, project_id)
|
||||
|
||||
def get_with_versions(
|
||||
self,
|
||||
model_id: str,
|
||||
project_id: str,
|
||||
*,
|
||||
versions_limit: int = 25,
|
||||
versions_cursor: Optional[str] = None,
|
||||
versions_filter: Optional[ModelVersionsFilter] = None,
|
||||
) -> ModelWithVersions:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Model Get With Versions"})
|
||||
return super().get_with_versions(
|
||||
model_id,
|
||||
project_id,
|
||||
versions_limit=versions_limit,
|
||||
versions_cursor=versions_cursor,
|
||||
versions_filter=versions_filter,
|
||||
)
|
||||
|
||||
def get_models(
|
||||
self,
|
||||
project_id: str,
|
||||
*,
|
||||
models_limit: int = 25,
|
||||
models_cursor: Optional[str] = None,
|
||||
models_filter: Optional[ProjectModelsFilter] = None,
|
||||
) -> ResourceCollection[Model]:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Model Get Models"})
|
||||
return super().get_models(
|
||||
project_id,
|
||||
models_limit=models_limit,
|
||||
models_cursor=models_cursor,
|
||||
models_filter=models_filter,
|
||||
)
|
||||
|
||||
def create(self, input: CreateModelInput) -> Model:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Model Create"})
|
||||
return super().create(input)
|
||||
|
||||
def delete(self, input: DeleteModelInput) -> bool:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Model Delete"})
|
||||
return super().delete(input)
|
||||
|
||||
def update(self, input: UpdateModelInput) -> Model:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Model Update"})
|
||||
return super().update(input)
|
||||
+31
-14
@@ -1,13 +1,23 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from specklepy.api.models import ActivityCollection, LimitedUser
|
||||
from specklepy.core.api.resources.other_user import Resource as CoreResource
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.core.api.models import (
|
||||
ActivityCollection,
|
||||
LimitedUser,
|
||||
UserSearchResultCollection,
|
||||
)
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resources import OtherUserResource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
|
||||
class Resource(CoreResource):
|
||||
class OtherUserResource(CoreResource):
|
||||
"""
|
||||
Provides API access to other users' profiles and activities on the platform.
|
||||
This class enables fetching limited information about users, searching for users by name or email,
|
||||
@@ -19,23 +29,29 @@ class Resource(CoreResource):
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
server_version=server_version,
|
||||
server_version=(server_version,),
|
||||
)
|
||||
self.schema = LimitedUser
|
||||
|
||||
def get(self, id: str) -> LimitedUser:
|
||||
"""
|
||||
Retrieves the profile of a user specified by their user ID.
|
||||
|
||||
Args:
|
||||
id (str): The unique identifier of the user.
|
||||
|
||||
Returns:
|
||||
LimitedUser: The profile of the user with limited information.
|
||||
"""
|
||||
def get(self, id: str) -> Optional[LimitedUser]:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Other User Get"})
|
||||
return super().get(id)
|
||||
|
||||
def user_search(
|
||||
self,
|
||||
query: str,
|
||||
*,
|
||||
limit: int = 25,
|
||||
cursor: Optional[str] = None,
|
||||
archived: bool = False,
|
||||
emailOnly: bool = False,
|
||||
) -> UserSearchResultCollection:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Other User Search"})
|
||||
return super().user_search(
|
||||
query, limit=limit, cursor=cursor, archived=archived, emailOnly=emailOnly
|
||||
)
|
||||
|
||||
@deprecated(reason="Use user_search instead", version=FE1_DEPRECATION_VERSION)
|
||||
def search(
|
||||
self, search_query: str, limit: int = 25
|
||||
) -> Union[List[LimitedUser], SpeckleException]:
|
||||
@@ -59,6 +75,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Other User Search"})
|
||||
return super().search(search_query, limit)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def activity(
|
||||
self,
|
||||
user_id: str,
|
||||
@@ -0,0 +1,54 @@
|
||||
from typing import Any, Optional, Tuple
|
||||
|
||||
from gql import Client
|
||||
|
||||
from specklepy.core.api.credentials import Account
|
||||
from specklepy.core.api.inputs.project_inputs import (
|
||||
ProjectInviteCreateInput,
|
||||
ProjectInviteUseInput,
|
||||
)
|
||||
from specklepy.core.api.models import PendingStreamCollaborator, ProjectWithTeam
|
||||
from specklepy.core.api.resources import ProjectInviteResource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
|
||||
|
||||
class ProjectInviteResource(CoreResource):
|
||||
"""API Access class for project invites"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
account: Account,
|
||||
basepath: str,
|
||||
client: Client,
|
||||
server_version: Optional[Tuple[Any, ...]],
|
||||
) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
server_version=server_version,
|
||||
)
|
||||
|
||||
def create(
|
||||
self, project_id: str, input: ProjectInviteCreateInput
|
||||
) -> ProjectWithTeam:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Invite Create"})
|
||||
return super().create(project_id, input)
|
||||
|
||||
def use(self, input: ProjectInviteUseInput) -> bool:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Invite Use"})
|
||||
return super().use(input)
|
||||
|
||||
def get(
|
||||
self, project_id: str, token: Optional[str]
|
||||
) -> Optional[PendingStreamCollaborator]:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Invite Get"})
|
||||
return super().get(project_id, token)
|
||||
|
||||
def cancel(
|
||||
self,
|
||||
project_id: str,
|
||||
invite_id: str,
|
||||
) -> ProjectWithTeam:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Invite Cancel"})
|
||||
return super().cancel(project_id, invite_id)
|
||||
@@ -0,0 +1,63 @@
|
||||
from typing import Optional
|
||||
|
||||
from specklepy.core.api.inputs.project_inputs import (
|
||||
ProjectCreateInput,
|
||||
ProjectModelsFilter,
|
||||
ProjectUpdateInput,
|
||||
ProjectUpdateRoleInput,
|
||||
)
|
||||
from specklepy.core.api.models import Project, ProjectWithModels, ProjectWithTeam
|
||||
from specklepy.core.api.resources import ProjectResource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
|
||||
|
||||
class ProjectResource(CoreResource):
|
||||
"""API Access class for projects"""
|
||||
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
server_version=server_version,
|
||||
)
|
||||
|
||||
def get(self, project_id: str) -> Project:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Get "})
|
||||
return super().get(project_id)
|
||||
|
||||
def get_with_models(
|
||||
self,
|
||||
project_id: str,
|
||||
*,
|
||||
models_limit: int = 25,
|
||||
models_cursor: Optional[str] = None,
|
||||
models_filter: Optional[ProjectModelsFilter] = None,
|
||||
) -> ProjectWithModels:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Get With Models"})
|
||||
return super().get_with_models(
|
||||
project_id,
|
||||
models_limit=models_limit,
|
||||
models_cursor=models_cursor,
|
||||
models_filter=models_filter,
|
||||
)
|
||||
|
||||
def get_with_team(self, project_id: str) -> ProjectWithTeam:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Get With Team"})
|
||||
return super().get_with_team(project_id)
|
||||
|
||||
def create(self, input: ProjectCreateInput) -> Project:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Create"})
|
||||
return super().create(input)
|
||||
|
||||
def update(self, input: ProjectUpdateInput) -> Project:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Update"})
|
||||
return super().update(input)
|
||||
|
||||
def delete(self, project_id: str) -> bool:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Delete"})
|
||||
return super().delete(project_id)
|
||||
|
||||
def update_role(self, input: ProjectUpdateRoleInput) -> ProjectWithTeam:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Project Update Role"})
|
||||
return super().update_role(input)
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
from typing import Any, Dict, List, Tuple
|
||||
|
||||
from specklepy.api.models import ServerInfo
|
||||
from specklepy.core.api.resources.server import Resource as CoreResource
|
||||
from specklepy.core.api.resources import ServerResource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
|
||||
|
||||
class Resource(CoreResource):
|
||||
class ServerResource(CoreResource):
|
||||
"""API Access class for the server"""
|
||||
|
||||
def __init__(self, account, basepath, client) -> None:
|
||||
@@ -0,0 +1,64 @@
|
||||
from typing import Callable, Optional, Sequence
|
||||
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
from specklepy.core.api.models import (
|
||||
ProjectModelsUpdatedMessage,
|
||||
ProjectUpdatedMessage,
|
||||
ProjectVersionsUpdatedMessage,
|
||||
UserProjectsUpdatedMessage,
|
||||
)
|
||||
from specklepy.core.api.resources import SubscriptionResource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
|
||||
TEventArgs = TypeVar("TEventArgs", bound=BaseModel)
|
||||
|
||||
|
||||
class SubscriptionResource(CoreResource):
|
||||
def __init__(self, account, basepath, client) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
)
|
||||
|
||||
async def user_projects_updated(
|
||||
self, callback: Callable[[UserProjectsUpdatedMessage], None]
|
||||
) -> None:
|
||||
metrics.track(
|
||||
metrics.SDK, self.account, {"name": "Subscription Project Models Updated"}
|
||||
)
|
||||
return await super().user_projects_updated(callback)
|
||||
|
||||
async def project_models_updated(
|
||||
self,
|
||||
callback: Callable[[ProjectModelsUpdatedMessage], None],
|
||||
id: str,
|
||||
*,
|
||||
model_ids: Optional[Sequence[str]] = None,
|
||||
) -> None:
|
||||
metrics.track(
|
||||
metrics.SDK, self.account, {"name": "Subscription Project Models Updated"}
|
||||
)
|
||||
return await super().project_models_updated(callback, id, model_ids=model_ids)
|
||||
|
||||
async def project_updated(
|
||||
self,
|
||||
callback: Callable[[ProjectUpdatedMessage], None],
|
||||
id: str,
|
||||
) -> None:
|
||||
metrics.track(
|
||||
metrics.SDK, self.account, {"name": "Subscription Project Updated"}
|
||||
)
|
||||
return await super().project_updated(callback, id)
|
||||
|
||||
async def project_versions_updated(
|
||||
self,
|
||||
callback: Callable[[ProjectVersionsUpdatedMessage], None],
|
||||
id: str,
|
||||
) -> None:
|
||||
metrics.track(
|
||||
metrics.SDK, self.account, {"name": "Subscription Project Versions Updated"}
|
||||
)
|
||||
return await super().project_versions_updated(callback, id)
|
||||
@@ -0,0 +1,63 @@
|
||||
from typing import Optional
|
||||
|
||||
from specklepy.core.api.inputs.model_inputs import ModelVersionsFilter
|
||||
from specklepy.core.api.inputs.version_inputs import (
|
||||
CreateVersionInput,
|
||||
DeleteVersionsInput,
|
||||
MarkReceivedVersionInput,
|
||||
MoveVersionsInput,
|
||||
UpdateVersionInput,
|
||||
)
|
||||
from specklepy.core.api.models import ResourceCollection, Version
|
||||
from specklepy.core.api.resources import VersionResource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
|
||||
|
||||
class VersionResource(CoreResource):
|
||||
"""API Access class for model versions"""
|
||||
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
server_version=server_version,
|
||||
)
|
||||
|
||||
def get(self, version_id: str, project_id: str) -> Version:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Version Get"})
|
||||
return super().get(version_id, project_id)
|
||||
|
||||
def get_versions(
|
||||
self,
|
||||
model_id: str,
|
||||
project_id: str,
|
||||
*,
|
||||
limit: int = 25,
|
||||
cursor: Optional[str] = None,
|
||||
filter: Optional[ModelVersionsFilter] = None,
|
||||
) -> ResourceCollection[Version]:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Version Get Versions"})
|
||||
return super().get_versions(
|
||||
model_id, project_id, limit=limit, cursor=cursor, filter=filter
|
||||
)
|
||||
|
||||
def create(self, input: CreateVersionInput) -> str:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Version Create"})
|
||||
return super().create(input)
|
||||
|
||||
def update(self, input: UpdateVersionInput) -> Version:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Version Update"})
|
||||
return super().update(input)
|
||||
|
||||
def move_to_model(self, input: MoveVersionsInput) -> str:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Version Move To Model"})
|
||||
return super().move_to_model(input)
|
||||
|
||||
def delete(self, input: DeleteVersionsInput) -> bool:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Version Delete"})
|
||||
return super().delete(input)
|
||||
|
||||
def received(self, input: MarkReceivedVersionInput) -> bool:
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Version Received"})
|
||||
return super().received(input)
|
||||
@@ -0,0 +1,9 @@
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.api.resources import ActiveUserResource
|
||||
from specklepy.core.api.models.deprecated import FE1_DEPRECATION_VERSION
|
||||
|
||||
|
||||
@deprecated(reason="Renamed to ActiveUserResource", version=FE1_DEPRECATION_VERSION)
|
||||
class Resource(ActiveUserResource):
|
||||
"""Renamed to ActiveUserResource"""
|
||||
+12
-1
@@ -1,7 +1,13 @@
|
||||
from typing import Optional, Union
|
||||
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.api.models import Branch
|
||||
from specklepy.core.api.resources.branch import Resource as CoreResource
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resources.deprecated.branch import Resource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
@@ -17,6 +23,7 @@ class Resource(CoreResource):
|
||||
)
|
||||
self.schema = Branch
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def create(
|
||||
self, stream_id: str, name: str, description: str = "No description provided"
|
||||
) -> str:
|
||||
@@ -32,6 +39,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Branch Create"})
|
||||
return super().create(stream_id, name, description)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get(
|
||||
self, stream_id: str, name: str, commits_limit: int = 10
|
||||
) -> Union[Branch, None, SpeckleException]:
|
||||
@@ -48,6 +56,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Branch Get"})
|
||||
return super().get(stream_id, name, commits_limit)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def list(self, stream_id: str, branches_limit: int = 10, commits_limit: int = 10):
|
||||
"""Get a list of branches from a given stream
|
||||
|
||||
@@ -62,6 +71,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Branch List"})
|
||||
return super().list(stream_id, branches_limit, commits_limit)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def update(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -83,6 +93,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Branch Update"})
|
||||
return super().update(stream_id, branch_id, name, description)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def delete(self, stream_id: str, branch_id: str):
|
||||
"""Delete a branch
|
||||
|
||||
+13
-1
@@ -1,7 +1,13 @@
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.api.models import Commit
|
||||
from specklepy.core.api.resources.commit import Resource as CoreResource
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resources.deprecated.commit import Resource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
@@ -17,6 +23,7 @@ class Resource(CoreResource):
|
||||
)
|
||||
self.schema = Commit
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get(self, stream_id: str, commit_id: str) -> Commit:
|
||||
"""
|
||||
Gets a commit given a stream and the commit id
|
||||
@@ -31,6 +38,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Commit Get"})
|
||||
return super().get(stream_id, commit_id)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def list(self, stream_id: str, limit: int = 10) -> List[Commit]:
|
||||
"""
|
||||
Get a list of commits on a given stream
|
||||
@@ -45,6 +53,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Commit List"})
|
||||
return super().list(stream_id, limit)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def create(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -77,6 +86,7 @@ class Resource(CoreResource):
|
||||
stream_id, object_id, branch_name, message, source_application, parents
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def update(self, stream_id: str, commit_id: str, message: str) -> bool:
|
||||
"""
|
||||
Update a commit
|
||||
@@ -93,6 +103,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Commit Update"})
|
||||
return super().update(stream_id, commit_id, message)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def delete(self, stream_id: str, commit_id: str) -> bool:
|
||||
"""
|
||||
Delete a commit
|
||||
@@ -108,6 +119,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Commit Delete"})
|
||||
return super().delete(stream_id, commit_id)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def received(
|
||||
self,
|
||||
stream_id: str,
|
||||
+9
-1
@@ -1,6 +1,12 @@
|
||||
from typing import Dict, List
|
||||
|
||||
from specklepy.core.api.resources.object import Resource as CoreResource
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resources.deprecated.object import Resource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.objects.base import Base
|
||||
|
||||
@@ -16,6 +22,7 @@ class Resource(CoreResource):
|
||||
)
|
||||
self.schema = Base
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get(self, stream_id: str, object_id: str) -> Base:
|
||||
"""
|
||||
Get a stream object
|
||||
@@ -30,6 +37,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Object Get"})
|
||||
return super().get(stream_id, object_id)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def create(self, stream_id: str, objects: List[Dict]) -> str:
|
||||
"""
|
||||
Not advised - generally, you want to use `operations.send()`.
|
||||
@@ -0,0 +1,11 @@
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.api.resources import OtherUserResource
|
||||
from specklepy.core.api.models.deprecated import FE1_DEPRECATION_VERSION
|
||||
|
||||
|
||||
@deprecated(reason="Renamed to OtherUserResource", version=FE1_DEPRECATION_VERSION)
|
||||
class Resource(OtherUserResource):
|
||||
"""
|
||||
Renamed to OtherUserResource
|
||||
"""
|
||||
@@ -0,0 +1,9 @@
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.api.resources import ServerResource
|
||||
from specklepy.core.api.models.deprecated import FE1_DEPRECATION_VERSION
|
||||
|
||||
|
||||
@deprecated(reason="Renamed to ServerResource", version=FE1_DEPRECATION_VERSION)
|
||||
class Resource(ServerResource):
|
||||
"""Renamed to ServerResource"""
|
||||
+22
-1
@@ -1,8 +1,14 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.api.models import PendingStreamCollaborator, Stream
|
||||
from specklepy.core.api.resources.stream import Resource as CoreResource
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resources.deprecated.stream import Resource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
|
||||
|
||||
@@ -19,6 +25,7 @@ class Resource(CoreResource):
|
||||
|
||||
self.schema = Stream
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get(self, id: str, branch_limit: int = 10, commit_limit: int = 10) -> Stream:
|
||||
"""Get the specified stream from the server
|
||||
|
||||
@@ -33,6 +40,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Get"})
|
||||
return super().get(id, branch_limit, commit_limit)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def list(self, stream_limit: int = 10) -> List[Stream]:
|
||||
"""Get a list of the user's streams
|
||||
|
||||
@@ -45,6 +53,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream List"})
|
||||
return super().list(stream_limit)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def create(
|
||||
self,
|
||||
name: str = "Anonymous Python Stream",
|
||||
@@ -65,6 +74,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Create"})
|
||||
return super().create(name, description, is_public)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def update(
|
||||
self,
|
||||
id: str,
|
||||
@@ -87,6 +97,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Update"})
|
||||
return super().update(id, name, description, is_public)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def delete(self, id: str) -> bool:
|
||||
"""Delete a stream given its id
|
||||
|
||||
@@ -99,6 +110,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Delete"})
|
||||
return super().delete(id)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def search(
|
||||
self,
|
||||
search_query: str,
|
||||
@@ -120,6 +132,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Search"})
|
||||
return super().search(search_query, limit, branch_limit, commit_limit)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def favorite(self, stream_id: str, favorited: bool = True):
|
||||
"""Favorite or unfavorite the given stream.
|
||||
|
||||
@@ -134,6 +147,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Favorite"})
|
||||
return super().favorite(stream_id, favorited)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get_all_pending_invites(
|
||||
self, stream_id: str
|
||||
) -> List[PendingStreamCollaborator]:
|
||||
@@ -152,6 +166,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Invite Get"})
|
||||
return super().get_all_pending_invites(stream_id)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def invite(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -179,6 +194,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Invite Create"})
|
||||
return super().invite(stream_id, email, user_id, role, message)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def invite_batch(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -205,6 +221,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Invite Batch Create"})
|
||||
return super().invite_batch(stream_id, emails, user_ids, message)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def invite_cancel(self, stream_id: str, invite_id: str) -> bool:
|
||||
"""Cancel an existing stream invite
|
||||
|
||||
@@ -220,6 +237,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Invite Cancel"})
|
||||
return super().invite_cancel(stream_id, invite_id)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def invite_use(self, stream_id: str, token: str, accept: bool = True) -> bool:
|
||||
"""Accept or decline a stream invite
|
||||
|
||||
@@ -237,6 +255,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Invite Use"})
|
||||
return super().invite_use(stream_id, token, accept)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def update_permission(self, stream_id: str, user_id: str, role: str):
|
||||
"""Updates permissions for a user on a given stream
|
||||
|
||||
@@ -257,6 +276,7 @@ class Resource(CoreResource):
|
||||
)
|
||||
return super().update_permission(stream_id, user_id, role)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def revoke_permission(self, stream_id: str, user_id: str):
|
||||
"""Revoke permissions from a user on a given stream
|
||||
|
||||
@@ -270,6 +290,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Stream Permission Revoke"})
|
||||
return super().revoke_permission(stream_id, user_id)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def activity(
|
||||
self,
|
||||
stream_id: str,
|
||||
+13
-16
@@ -1,24 +1,17 @@
|
||||
from functools import wraps
|
||||
from typing import Callable, Dict, List, Optional, Union
|
||||
|
||||
from deprecated import deprecated
|
||||
from graphql import DocumentNode
|
||||
|
||||
from specklepy.core.api.resources.subscriptions import Resource as CoreResource
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resources.current.subscription_resource import check_wsclient
|
||||
from specklepy.core.api.resources.deprecated.subscriptions import (
|
||||
Resource as CoreResource,
|
||||
)
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
|
||||
def check_wsclient(function):
|
||||
@wraps(function)
|
||||
async def check_wsclient_wrapper(self, *args, **kwargs):
|
||||
if self.client is None:
|
||||
raise SpeckleException(
|
||||
"You must authenticate before you can subscribe to events"
|
||||
)
|
||||
else:
|
||||
return await function(self, *args, **kwargs)
|
||||
|
||||
return check_wsclient_wrapper
|
||||
|
||||
|
||||
class Resource(CoreResource):
|
||||
@@ -31,6 +24,7 @@ class Resource(CoreResource):
|
||||
client=client,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
@check_wsclient
|
||||
async def stream_added(self, callback: Optional[Callable] = None):
|
||||
"""Subscribes to new stream added event for your profile.
|
||||
@@ -46,6 +40,7 @@ class Resource(CoreResource):
|
||||
metrics.track(metrics.SDK, self.account, {"name": "Subscription Stream Added"})
|
||||
return super().stream_added(callback)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
@check_wsclient
|
||||
async def stream_updated(self, id: str, callback: Optional[Callable] = None):
|
||||
"""
|
||||
@@ -66,6 +61,7 @@ class Resource(CoreResource):
|
||||
)
|
||||
return super().stream_updated(id, callback)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
@check_wsclient
|
||||
async def stream_removed(self, callback: Optional[Callable] = None):
|
||||
"""Subscribes to stream removed event for your profile.
|
||||
@@ -87,6 +83,7 @@ class Resource(CoreResource):
|
||||
)
|
||||
return super().stream_removed(callback)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
@check_wsclient
|
||||
async def subscribe(
|
||||
self,
|
||||
+1
-1
@@ -4,7 +4,7 @@ from typing import List, Optional, Union
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.api.models import PendingStreamCollaborator, User
|
||||
from specklepy.core.api.resources.user import Resource as CoreResource
|
||||
from specklepy.core.api.resources.deprecated.user import Resource as CoreResource
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
@@ -11,12 +11,17 @@ from gql.transport.websockets import WebsocketsTransport
|
||||
from specklepy.core.api import resources
|
||||
from specklepy.core.api.credentials import Account, get_account_from_token
|
||||
from specklepy.core.api.resources import (
|
||||
active_user,
|
||||
ActiveUserResource,
|
||||
ModelResource,
|
||||
OtherUserResource,
|
||||
ProjectInviteResource,
|
||||
ProjectResource,
|
||||
ServerResource,
|
||||
SubscriptionResource,
|
||||
VersionResource,
|
||||
branch,
|
||||
commit,
|
||||
object,
|
||||
other_user,
|
||||
server,
|
||||
stream,
|
||||
subscriptions,
|
||||
user,
|
||||
@@ -176,53 +181,81 @@ class SpeckleClient:
|
||||
self._init_resources()
|
||||
|
||||
try:
|
||||
user_or_error = self.active_user.get()
|
||||
if isinstance(user_or_error, SpeckleException):
|
||||
if isinstance(user_or_error.exception, TransportServerError):
|
||||
raise user_or_error.exception
|
||||
else:
|
||||
raise user_or_error
|
||||
except TransportServerError as ex:
|
||||
if ex.code == 403:
|
||||
warn(
|
||||
SpeckleWarning(
|
||||
"Possibly invalid token - could not authenticate Speckle Client"
|
||||
f" for server {self.url}"
|
||||
_ = self.active_user.get()
|
||||
except SpeckleException as ex:
|
||||
if isinstance(ex.exception, TransportServerError):
|
||||
if ex.exception.code == 403:
|
||||
warn(
|
||||
SpeckleWarning(
|
||||
"Possibly invalid token - could not authenticate Speckle Client"
|
||||
f" for server {self.url}"
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise ex
|
||||
else:
|
||||
raise ex
|
||||
|
||||
def execute_query(self, query: str) -> Dict:
|
||||
return self.httpclient.execute(query)
|
||||
|
||||
def _init_resources(self) -> None:
|
||||
self.server = server.Resource(
|
||||
self.server = ServerResource(
|
||||
account=self.account, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
|
||||
server_version = None
|
||||
try:
|
||||
server_version = self.server.version()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.other_user = OtherUserResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.active_user = ActiveUserResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.project = ProjectResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.project_invite = ProjectInviteResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.model = ModelResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.version = VersionResource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.subscription = SubscriptionResource(
|
||||
account=self.account,
|
||||
basepath=self.ws_url,
|
||||
client=self.wsclient,
|
||||
)
|
||||
# Deprecated Resources
|
||||
self.user = user.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.other_user = other_user.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.active_user = active_user.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.stream = stream.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
|
||||
@@ -12,10 +12,11 @@ from specklepy.transports.sqlite import SQLiteTransport
|
||||
|
||||
|
||||
class UserInfo(BaseModel):
|
||||
id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
company: Optional[str] = None
|
||||
id: Optional[str] = None
|
||||
avatar: Optional[str] = None
|
||||
|
||||
|
||||
class Account(BaseModel):
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ProjectVisibility(str, Enum):
|
||||
PRIVATE = "PRIVATE"
|
||||
PUBLIC = "PUBLIC"
|
||||
UNLISTEd = "UNLISTED"
|
||||
|
||||
|
||||
class UserProjectsUpdatedMessageType(str, Enum):
|
||||
ADDED = "ADDED"
|
||||
REMOVED = "REMOVED"
|
||||
|
||||
|
||||
class ProjectModelsUpdatedMessageType(str, Enum):
|
||||
CREATED = "CREATED"
|
||||
DELETED = "DELETED"
|
||||
UPDATED = "UPDATED"
|
||||
|
||||
|
||||
class ProjectUpdatedMessageType(str, Enum):
|
||||
DELETED = "DELETED"
|
||||
UPDATED = "UPDATED"
|
||||
|
||||
|
||||
class ProjectVersionsUpdatedMessageType(str, Enum):
|
||||
CREATED = "CREATED"
|
||||
DELETED = "DELETED"
|
||||
UPDATED = "UPDATED"
|
||||
@@ -0,0 +1,42 @@
|
||||
from specklepy.core.api.inputs.model_inputs import (
|
||||
CreateModelInput,
|
||||
DeleteModelInput,
|
||||
ModelVersionsFilter,
|
||||
UpdateModelInput,
|
||||
)
|
||||
from specklepy.core.api.inputs.project_inputs import (
|
||||
ProjectCreateInput,
|
||||
ProjectInviteCreateInput,
|
||||
ProjectInviteUseInput,
|
||||
ProjectModelsFilter,
|
||||
ProjectUpdateInput,
|
||||
ProjectUpdateRoleInput,
|
||||
)
|
||||
from specklepy.core.api.inputs.user_inputs import UserProjectsFilter, UserUpdateInput
|
||||
from specklepy.core.api.inputs.version_inputs import (
|
||||
CreateVersionInput,
|
||||
DeleteVersionsInput,
|
||||
MarkReceivedVersionInput,
|
||||
MoveVersionsInput,
|
||||
UpdateVersionInput,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"CreateModelInput",
|
||||
"DeleteModelInput",
|
||||
"UpdateModelInput",
|
||||
"ModelVersionsFilter",
|
||||
"ProjectCreateInput",
|
||||
"ProjectInviteCreateInput",
|
||||
"ProjectInviteUseInput",
|
||||
"ProjectModelsFilter",
|
||||
"ProjectUpdateInput",
|
||||
"ProjectUpdateRoleInput",
|
||||
"UserProjectsFilter",
|
||||
"UserUpdateInput",
|
||||
"UpdateVersionInput",
|
||||
"MoveVersionsInput",
|
||||
"DeleteVersionsInput",
|
||||
"CreateVersionInput",
|
||||
"MarkReceivedVersionInput",
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
from typing import Optional, Sequence
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class CreateModelInput(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
projectId: str
|
||||
|
||||
|
||||
class DeleteModelInput(BaseModel):
|
||||
id: str
|
||||
projectId: str
|
||||
|
||||
|
||||
class UpdateModelInput(BaseModel):
|
||||
id: str
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
projectId: str
|
||||
|
||||
|
||||
class ModelVersionsFilter(BaseModel):
|
||||
priorityIds: Sequence[str]
|
||||
priorityIdsOnly: Optional[bool] = None
|
||||
@@ -0,0 +1,47 @@
|
||||
from typing import Optional, Sequence
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from specklepy.core.api.enums import ProjectVisibility
|
||||
|
||||
|
||||
class ProjectCreateInput(BaseModel):
|
||||
name: Optional[str]
|
||||
description: Optional[str]
|
||||
visibility: Optional[ProjectVisibility]
|
||||
|
||||
|
||||
class ProjectInviteCreateInput(BaseModel):
|
||||
email: Optional[str]
|
||||
role: Optional[str]
|
||||
serverRole: Optional[str]
|
||||
userId: Optional[str]
|
||||
|
||||
|
||||
class ProjectInviteUseInput(BaseModel):
|
||||
accept: bool
|
||||
projectId: str
|
||||
token: str
|
||||
|
||||
|
||||
class ProjectModelsFilter(BaseModel):
|
||||
contributors: Optional[Sequence[str]] = None
|
||||
excludeIds: Optional[Sequence[str]] = None
|
||||
ids: Optional[Sequence[str]] = None
|
||||
onlyWithVersions: Optional[bool] = None
|
||||
search: Optional[str] = None
|
||||
sourceApps: Optional[Sequence[str]] = None
|
||||
|
||||
|
||||
class ProjectUpdateInput(BaseModel):
|
||||
id: str
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
allowPublicComments: Optional[bool] = None
|
||||
visibility: Optional[ProjectVisibility] = None
|
||||
|
||||
|
||||
class ProjectUpdateRoleInput(BaseModel):
|
||||
userId: str
|
||||
projectId: str
|
||||
role: Optional[str]
|
||||
@@ -0,0 +1,15 @@
|
||||
from typing import Optional, Sequence
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserUpdateInput(BaseModel):
|
||||
avatar: Optional[str] = None
|
||||
bio: Optional[str] = None
|
||||
company: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
|
||||
|
||||
class UserProjectsFilter(BaseModel):
|
||||
search: str
|
||||
onlyWithRoles: Optional[Sequence[str]] = None
|
||||
@@ -0,0 +1,37 @@
|
||||
from typing import Optional, Sequence
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UpdateVersionInput(BaseModel):
|
||||
versionId: str
|
||||
projectId: str
|
||||
message: Optional[str]
|
||||
|
||||
|
||||
class MoveVersionsInput(BaseModel):
|
||||
targetModelName: str
|
||||
versionIds: Sequence[str]
|
||||
projectId: str
|
||||
|
||||
|
||||
class DeleteVersionsInput(BaseModel):
|
||||
versionIds: Sequence[str]
|
||||
projectId: str
|
||||
|
||||
|
||||
class CreateVersionInput(BaseModel):
|
||||
objectId: str
|
||||
modelId: str
|
||||
projectId: str
|
||||
message: Optional[str] = None
|
||||
sourceApplication: Optional[str] = "py"
|
||||
totalChildrenCount: Optional[int] = None
|
||||
parents: Optional[Sequence[str]] = None
|
||||
|
||||
|
||||
class MarkReceivedVersionInput(BaseModel):
|
||||
versionId: str
|
||||
projectId: str
|
||||
sourceApplication: str
|
||||
message: Optional[str] = None
|
||||
@@ -0,0 +1,71 @@
|
||||
from specklepy.core.api.models.current import (
|
||||
AuthStrategy,
|
||||
LimitedUser,
|
||||
Model,
|
||||
ModelWithVersions,
|
||||
PendingStreamCollaborator,
|
||||
Project,
|
||||
ProjectCollaborator,
|
||||
ProjectCommentCollection,
|
||||
ProjectWithModels,
|
||||
ProjectWithTeam,
|
||||
ResourceCollection,
|
||||
ServerConfiguration,
|
||||
ServerInfo,
|
||||
ServerMigration,
|
||||
User,
|
||||
UserSearchResultCollection,
|
||||
Version,
|
||||
)
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
Activity,
|
||||
ActivityCollection,
|
||||
Branch,
|
||||
Branches,
|
||||
Collaborator,
|
||||
Commit,
|
||||
Commits,
|
||||
Object,
|
||||
Stream,
|
||||
Streams,
|
||||
)
|
||||
from specklepy.core.api.models.subscription_messages import (
|
||||
ProjectModelsUpdatedMessage,
|
||||
ProjectUpdatedMessage,
|
||||
ProjectVersionsUpdatedMessage,
|
||||
UserProjectsUpdatedMessage,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"User",
|
||||
"ResourceCollection",
|
||||
"ServerMigration",
|
||||
"AuthStrategy",
|
||||
"ServerConfiguration",
|
||||
"ServerInfo",
|
||||
"LimitedUser",
|
||||
"PendingStreamCollaborator",
|
||||
"ProjectCollaborator",
|
||||
"Version",
|
||||
"Model",
|
||||
"ModelWithVersions",
|
||||
"Project",
|
||||
"ProjectWithModels",
|
||||
"ProjectWithTeam",
|
||||
"ProjectCommentCollection",
|
||||
"UserSearchResultCollection",
|
||||
"UserProjectsUpdatedMessage",
|
||||
"ProjectModelsUpdatedMessage",
|
||||
"ProjectUpdatedMessage",
|
||||
"ProjectVersionsUpdatedMessage",
|
||||
"Collaborator",
|
||||
"Commit",
|
||||
"Commits",
|
||||
"Object",
|
||||
"Branch",
|
||||
"Branches",
|
||||
"Stream",
|
||||
"Streams",
|
||||
"Activity",
|
||||
"ActivityCollection",
|
||||
]
|
||||
@@ -0,0 +1,171 @@
|
||||
from datetime import datetime
|
||||
from typing import Generic, List, Optional, TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from specklepy.core.api.enums import ProjectVisibility
|
||||
from specklepy.core.api.models.deprecated import Streams
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: str
|
||||
email: Optional[str] = None
|
||||
name: str
|
||||
bio: Optional[str] = None
|
||||
company: Optional[str] = None
|
||||
avatar: Optional[str] = None
|
||||
verified: Optional[bool] = None
|
||||
role: Optional[str] = None
|
||||
streams: Optional["Streams"] = None
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"User( id: {self.id}, name: {self.name}, email: {self.email}, company:"
|
||||
f" {self.company} )"
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class ResourceCollection(BaseModel, Generic[T]):
|
||||
totalCount: int
|
||||
items: List[T]
|
||||
cursor: Optional[str] = None
|
||||
|
||||
|
||||
class ServerMigration(BaseModel):
|
||||
movedFrom: Optional[str]
|
||||
movedTo: Optional[str]
|
||||
|
||||
|
||||
class AuthStrategy(BaseModel):
|
||||
color: Optional[str]
|
||||
icon: str
|
||||
id: str
|
||||
name: str
|
||||
url: str
|
||||
|
||||
|
||||
class ServerConfiguration(BaseModel):
|
||||
blobSizeLimitBytes: int
|
||||
objectMultipartUploadSizeLimitBytes: int
|
||||
objectSizeLimitBytes: int
|
||||
|
||||
|
||||
# Keeping this one all Optionals at the minute, because its used both as a deserialization model for GQL and Account Management
|
||||
class ServerInfo(BaseModel):
|
||||
name: Optional[str] = None
|
||||
company: Optional[str] = None
|
||||
url: Optional[str] = None
|
||||
adminContact: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
canonicalUrl: Optional[str] = None
|
||||
roles: Optional[List[dict]] = None
|
||||
scopes: Optional[List[dict]] = None
|
||||
authStrategies: Optional[List[dict]] = None
|
||||
version: Optional[str] = None
|
||||
frontend2: Optional[bool] = None
|
||||
migration: Optional[ServerMigration] = None
|
||||
|
||||
# TODO separate gql model from account management model
|
||||
|
||||
|
||||
class LimitedUser(BaseModel):
|
||||
"""Limited user type, for showing public info about a user to another user."""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
bio: Optional[str]
|
||||
company: Optional[str]
|
||||
avatar: Optional[str]
|
||||
verified: Optional[bool]
|
||||
role: Optional[str]
|
||||
|
||||
|
||||
class PendingStreamCollaborator(BaseModel):
|
||||
id: str
|
||||
inviteId: str
|
||||
streamId: Optional[str] = None
|
||||
projectId: str
|
||||
streamName: Optional[str] = None
|
||||
projectName: str
|
||||
title: str
|
||||
role: str
|
||||
invitedBy: LimitedUser
|
||||
user: Optional[LimitedUser] = None
|
||||
token: Optional[str]
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"PendingStreamCollaborator( inviteId: {self.inviteId}, streamId:"
|
||||
f" {self.streamId}, role: {self.role}, title: {self.title}, invitedBy:"
|
||||
f" {self.user.name if self.user else None})"
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class ProjectCollaborator(BaseModel):
|
||||
id: str
|
||||
role: str
|
||||
user: LimitedUser
|
||||
|
||||
|
||||
class Version(BaseModel):
|
||||
authorUser: Optional[LimitedUser]
|
||||
createdAt: datetime
|
||||
id: str
|
||||
message: Optional[str]
|
||||
previewUrl: str
|
||||
referencedObject: str
|
||||
sourceApplication: Optional[str]
|
||||
|
||||
|
||||
class Model(BaseModel):
|
||||
author: LimitedUser
|
||||
createdAt: datetime
|
||||
description: Optional[str]
|
||||
displayName: str
|
||||
id: str
|
||||
name: str
|
||||
previewUrl: Optional[str]
|
||||
updatedAt: datetime
|
||||
|
||||
|
||||
class ModelWithVersions(Model):
|
||||
versions: ResourceCollection[Version]
|
||||
|
||||
|
||||
class Project(BaseModel):
|
||||
allowPublicComments: bool
|
||||
createdAt: datetime
|
||||
description: Optional[str]
|
||||
id: str
|
||||
name: str
|
||||
role: Optional[str]
|
||||
sourceApps: List[str]
|
||||
updatedAt: datetime
|
||||
visibility: ProjectVisibility
|
||||
workspaceId: Optional[str]
|
||||
|
||||
|
||||
class ProjectWithModels(Project):
|
||||
models: ResourceCollection[Model]
|
||||
|
||||
|
||||
class ProjectWithTeam(Project):
|
||||
invitedTeam: List[PendingStreamCollaborator]
|
||||
team: List[ProjectCollaborator]
|
||||
|
||||
|
||||
class ProjectCommentCollection(ResourceCollection[T], Generic[T]):
|
||||
totalArchivedCount: int
|
||||
|
||||
|
||||
class UserSearchResultCollection(BaseModel):
|
||||
items: List[LimitedUser]
|
||||
cursor: Optional[str] = None
|
||||
@@ -1,9 +1,14 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from deprecated import deprecated
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
FE1_DEPRECATION_REASON = "Stream/Branch/Commit API is now deprecated, Use the new Project/Model/Version API functions in Client}"
|
||||
FE1_DEPRECATION_VERSION = "2.20"
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class Collaborator(BaseModel):
|
||||
id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
@@ -11,6 +16,7 @@ class Collaborator(BaseModel):
|
||||
avatar: Optional[str] = None
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class Commit(BaseModel):
|
||||
id: Optional[str] = None
|
||||
message: Optional[str] = None
|
||||
@@ -35,12 +41,14 @@ class Commit(BaseModel):
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class Commits(BaseModel):
|
||||
totalCount: Optional[int] = None
|
||||
cursor: Optional[datetime] = None
|
||||
items: List[Commit] = []
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class Object(BaseModel):
|
||||
id: Optional[str] = None
|
||||
speckleType: Optional[str] = None
|
||||
@@ -49,6 +57,7 @@ class Object(BaseModel):
|
||||
createdAt: Optional[datetime] = None
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class Branch(BaseModel):
|
||||
id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
@@ -56,12 +65,14 @@ class Branch(BaseModel):
|
||||
commits: Optional[Commits] = None
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class Branches(BaseModel):
|
||||
totalCount: Optional[int] = None
|
||||
cursor: Optional[datetime] = None
|
||||
items: List[Branch] = []
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class Stream(BaseModel):
|
||||
id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
@@ -88,67 +99,14 @@ class Stream(BaseModel):
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class Streams(BaseModel):
|
||||
totalCount: Optional[int] = None
|
||||
cursor: Optional[datetime] = None
|
||||
items: List[Stream] = []
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
bio: Optional[str] = None
|
||||
company: Optional[str] = None
|
||||
avatar: Optional[str] = None
|
||||
verified: Optional[bool] = None
|
||||
role: Optional[str] = None
|
||||
streams: Optional[Streams] = None
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"User( id: {self.id}, name: {self.name}, email: {self.email}, company:"
|
||||
f" {self.company} )"
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class LimitedUser(BaseModel):
|
||||
"""Limited user type, for showing public info about a user to another user."""
|
||||
|
||||
id: str
|
||||
name: Optional[str] = None
|
||||
bio: Optional[str] = None
|
||||
company: Optional[str] = None
|
||||
avatar: Optional[str] = None
|
||||
verified: Optional[bool] = None
|
||||
role: Optional[str] = None
|
||||
|
||||
|
||||
class PendingStreamCollaborator(BaseModel):
|
||||
id: Optional[str] = None
|
||||
inviteId: Optional[str] = None
|
||||
streamId: Optional[str] = None
|
||||
streamName: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
role: Optional[str] = None
|
||||
invitedBy: Optional[User] = None
|
||||
user: Optional[User] = None
|
||||
token: Optional[str] = None
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"PendingStreamCollaborator( inviteId: {self.inviteId}, streamId:"
|
||||
f" {self.streamId}, role: {self.role}, title: {self.title}, invitedBy:"
|
||||
f" {self.user.name if self.user else None})"
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class Activity(BaseModel):
|
||||
actionType: Optional[str] = None
|
||||
info: Optional[dict] = None
|
||||
@@ -169,6 +127,7 @@ class Activity(BaseModel):
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
class ActivityCollection(BaseModel):
|
||||
totalCount: Optional[int] = None
|
||||
items: Optional[List[Activity]] = None
|
||||
@@ -183,23 +142,3 @@ class ActivityCollection(BaseModel):
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class ServerMigration(BaseModel):
|
||||
movedTo: Optional[str] = None
|
||||
movedFrom: Optional[str] = None
|
||||
|
||||
|
||||
class ServerInfo(BaseModel):
|
||||
name: Optional[str] = None
|
||||
company: Optional[str] = None
|
||||
url: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
adminContact: Optional[str] = None
|
||||
canonicalUrl: Optional[str] = None
|
||||
roles: Optional[List[dict]] = None
|
||||
scopes: Optional[List[dict]] = None
|
||||
authStrategies: Optional[List[dict]] = None
|
||||
version: Optional[str] = None
|
||||
frontend2: Optional[bool] = None
|
||||
migration: Optional[ServerMigration] = None
|
||||
@@ -0,0 +1,36 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from specklepy.core.api.enums import (
|
||||
ProjectModelsUpdatedMessageType,
|
||||
ProjectUpdatedMessageType,
|
||||
ProjectVersionsUpdatedMessageType,
|
||||
UserProjectsUpdatedMessageType,
|
||||
)
|
||||
from specklepy.core.api.models.current import Model, Project, Version
|
||||
|
||||
|
||||
class UserProjectsUpdatedMessage(BaseModel):
|
||||
id: str
|
||||
type: UserProjectsUpdatedMessageType
|
||||
project: Optional[Project]
|
||||
|
||||
|
||||
class ProjectModelsUpdatedMessage(BaseModel):
|
||||
id: str
|
||||
type: ProjectModelsUpdatedMessageType
|
||||
model: Optional[Model]
|
||||
|
||||
|
||||
class ProjectUpdatedMessage(BaseModel):
|
||||
id: str
|
||||
type: ProjectUpdatedMessageType
|
||||
project: Optional[Project]
|
||||
|
||||
|
||||
class ProjectVersionsUpdatedMessage(BaseModel):
|
||||
id: str
|
||||
type: ProjectVersionsUpdatedMessageType
|
||||
modelId: Optional[str]
|
||||
version: Optional[Version]
|
||||
@@ -1,9 +1,10 @@
|
||||
from threading import Lock
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Union
|
||||
|
||||
from gql.client import Client
|
||||
from gql.transport.exceptions import TransportQueryError
|
||||
from graphql import DocumentNode
|
||||
from pydantic import BaseModel
|
||||
|
||||
from specklepy.core.api.credentials import Account
|
||||
from specklepy.logging.exceptions import (
|
||||
@@ -14,6 +15,8 @@ from specklepy.logging.exceptions import (
|
||||
from specklepy.serialization.base_object_serializer import BaseObjectSerializer
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
|
||||
T = TypeVar("T", bound=BaseModel)
|
||||
|
||||
|
||||
class ResourceBase(object):
|
||||
def __init__(
|
||||
@@ -43,6 +46,35 @@ class ResourceBase(object):
|
||||
response = response[key]
|
||||
return response
|
||||
|
||||
def make_request_and_parse_response(
|
||||
self,
|
||||
schema: Type[T],
|
||||
query: DocumentNode,
|
||||
variables: Optional[Dict[str, Any]] = None,
|
||||
) -> T:
|
||||
try:
|
||||
with self.__lock:
|
||||
response = self.client.execute(query, variable_values=variables)
|
||||
except TransportQueryError as ex:
|
||||
raise GraphQLException(
|
||||
message=(
|
||||
f"Failed to execute the GraphQL {self.name} request. Errors:"
|
||||
f" {ex.errors}"
|
||||
),
|
||||
errors=ex.errors,
|
||||
data=ex.data,
|
||||
) from ex
|
||||
except Exception as ex:
|
||||
raise SpeckleException(
|
||||
message=(
|
||||
f"Failed to execute the GraphQL {self.name} request. Inner"
|
||||
f" exception: {ex}"
|
||||
),
|
||||
exception=ex,
|
||||
) from ex
|
||||
|
||||
return schema.model_validate(response)
|
||||
|
||||
def _parse_response(self, response: Union[dict, list, None], schema=None):
|
||||
"""Try to create a class instance from the response"""
|
||||
if response is None:
|
||||
@@ -69,6 +101,8 @@ class ResourceBase(object):
|
||||
parse_response: bool = True,
|
||||
) -> Any:
|
||||
"""Executes the GraphQL query"""
|
||||
# This method has quite complex and ambiguous typing, and counter-intuitive error handling
|
||||
# We are going to phase it out in favour of `make_request_and_parse_response`
|
||||
try:
|
||||
with self.__lock:
|
||||
response = self.client.execute(query, variable_values=params)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from specklepy.core.api.resources.current.active_user_resource import ActiveUserResource
|
||||
from specklepy.core.api.resources.current.model_resource import ModelResource
|
||||
from specklepy.core.api.resources.current.other_user_resource import OtherUserResource
|
||||
from specklepy.core.api.resources.current.project_invite_resource import (
|
||||
ProjectInviteResource,
|
||||
)
|
||||
from specklepy.core.api.resources.current.project_resource import ProjectResource
|
||||
from specklepy.core.api.resources.current.server_resource import ServerResource
|
||||
from specklepy.core.api.resources.current.subscription_resource import (
|
||||
SubscriptionResource,
|
||||
)
|
||||
from specklepy.core.api.resources.current.version_resource import VersionResource
|
||||
from specklepy.core.api.resources.deprecated import (
|
||||
active_user,
|
||||
branch,
|
||||
commit,
|
||||
object,
|
||||
other_user,
|
||||
server,
|
||||
stream,
|
||||
subscriptions,
|
||||
user,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"ActiveUserResource",
|
||||
"ModelResource",
|
||||
"OtherUserResource",
|
||||
"ProjectInviteResource",
|
||||
"ProjectResource",
|
||||
"ServerResource",
|
||||
"SubscriptionResource",
|
||||
"VersionResource",
|
||||
"active_user",
|
||||
"branch",
|
||||
"commit",
|
||||
"object",
|
||||
"other_user",
|
||||
"server",
|
||||
"stream",
|
||||
"subscriptions",
|
||||
"user",
|
||||
]
|
||||
|
||||
+200
-58
@@ -1,21 +1,30 @@
|
||||
from datetime import datetime, timezone
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, overload
|
||||
|
||||
from deprecated import deprecated
|
||||
from gql import gql
|
||||
|
||||
from specklepy.core.api.inputs.user_inputs import UserProjectsFilter, UserUpdateInput
|
||||
from specklepy.core.api.models import (
|
||||
ActivityCollection,
|
||||
PendingStreamCollaborator,
|
||||
Project,
|
||||
ResourceCollection,
|
||||
User,
|
||||
)
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.core.api.responses import DataResponse
|
||||
from specklepy.logging.exceptions import GraphQLException
|
||||
|
||||
NAME = "active_user"
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for users"""
|
||||
class ActiveUserResource(ResourceBase):
|
||||
"""API Access class for the active user"""
|
||||
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
@@ -27,38 +36,73 @@ class Resource(ResourceBase):
|
||||
)
|
||||
self.schema = User
|
||||
|
||||
def get(self) -> User:
|
||||
"""Gets the profile of a user. If no id argument is provided,
|
||||
will return the current authenticated user's profile
|
||||
(as extracted from the authorization header).
|
||||
|
||||
Arguments:
|
||||
id {str} -- the user id
|
||||
def get(self) -> Optional[User]:
|
||||
"""Gets the currently active user profile (as extracted from the authorization header)
|
||||
|
||||
Returns:
|
||||
User -- the retrieved user
|
||||
User -- the requested user, or none if no authentication token is provided to the Client
|
||||
"""
|
||||
query = gql(
|
||||
QUERY = gql(
|
||||
"""
|
||||
query User {
|
||||
activeUser {
|
||||
id
|
||||
email
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
profiles
|
||||
role
|
||||
}
|
||||
}
|
||||
"""
|
||||
data:activeUser {
|
||||
id
|
||||
email
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {}
|
||||
variables = {}
|
||||
|
||||
return self.make_request(query=query, params=params, return_type="activeUser")
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[Optional[User]], QUERY, variables
|
||||
).data
|
||||
|
||||
def _update(self, input: UserUpdateInput) -> User:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ActiveUserMutations($input: UserUpdateInput!) {
|
||||
data:activeUserMutations {
|
||||
data:update(user: $input) {
|
||||
id
|
||||
email
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {"input": input.model_dump(warnings="error")}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[User]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
@deprecated("Use UserUpdateInput overload", version=FE1_DEPRECATION_VERSION)
|
||||
@overload
|
||||
def update(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
company: Optional[str] = None,
|
||||
bio: Optional[str] = None,
|
||||
avatar: Optional[str] = None,
|
||||
) -> User: ...
|
||||
|
||||
@overload
|
||||
def update(self, *, input: UserUpdateInput) -> User: ...
|
||||
|
||||
def update(
|
||||
self,
|
||||
@@ -66,40 +110,125 @@ class Resource(ResourceBase):
|
||||
company: Optional[str] = None,
|
||||
bio: Optional[str] = None,
|
||||
avatar: Optional[str] = None,
|
||||
):
|
||||
"""Updates your user profile. All arguments are optional.
|
||||
|
||||
Arguments:
|
||||
name {str} -- your name
|
||||
company {str} -- the company you may or may not work for
|
||||
bio {str} -- tell us about yourself
|
||||
avatar {str} -- a nice photo of yourself
|
||||
|
||||
Returns @deprecated(version=DEPRECATION_VERSION, reason=DEPRECATION_TEXT):
|
||||
bool -- True if your profile was updated successfully
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation UserUpdate($user: UserUpdateInput!) {
|
||||
userUpdate(user: $user)
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {"name": name, "company": company, "bio": bio, "avatar": avatar}
|
||||
|
||||
params = {"user": {k: v for k, v in params.items() if v is not None}}
|
||||
|
||||
if not params["user"]:
|
||||
return SpeckleException(
|
||||
message=(
|
||||
"You must provide at least one field to update your user profile"
|
||||
*,
|
||||
input: Optional[UserUpdateInput] = None,
|
||||
) -> User:
|
||||
if isinstance(input, UserUpdateInput):
|
||||
return self._update(input=input)
|
||||
else:
|
||||
return self._update(
|
||||
input=UserUpdateInput(
|
||||
name=name,
|
||||
company=company,
|
||||
bio=bio,
|
||||
avatar=avatar,
|
||||
)
|
||||
)
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type="userUpdate", parse_response=False
|
||||
def get_projects(
|
||||
self,
|
||||
*,
|
||||
limit: int = 25,
|
||||
cursor: Optional[str] = None,
|
||||
filter: Optional[UserProjectsFilter] = None,
|
||||
) -> ResourceCollection[Project]:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query User($limit : Int!, $cursor: String, $filter: UserProjectsFilter) {
|
||||
data:activeUser {
|
||||
data:projects(limit: $limit, cursor: $cursor, filter: $filter) {
|
||||
totalCount
|
||||
cursor
|
||||
items {
|
||||
id
|
||||
name
|
||||
description
|
||||
visibility
|
||||
allowPublicComments
|
||||
role
|
||||
createdAt
|
||||
updatedAt
|
||||
sourceApps
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"limit": limit,
|
||||
"cursor": cursor,
|
||||
"filter": filter.model_dump(warnings="error") if filter else None,
|
||||
}
|
||||
|
||||
response = self.make_request_and_parse_response(
|
||||
DataResponse[Optional[DataResponse[ResourceCollection[Project]]]],
|
||||
QUERY,
|
||||
variables,
|
||||
)
|
||||
|
||||
if response.data is None:
|
||||
raise GraphQLException(
|
||||
"GraphQL response indicated that the ActiveUser could not be found"
|
||||
)
|
||||
|
||||
return response.data.data
|
||||
|
||||
def get_project_invites(self) -> List[PendingStreamCollaborator]:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query ProjectInvites {
|
||||
data:activeUser {
|
||||
data:projectInvites {
|
||||
id
|
||||
inviteId
|
||||
invitedBy {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
projectId
|
||||
projectName
|
||||
role
|
||||
title
|
||||
token
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
verified
|
||||
avatar
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {}
|
||||
|
||||
response = self.make_request_and_parse_response(
|
||||
DataResponse[Optional[DataResponse[List[PendingStreamCollaborator]]]],
|
||||
QUERY,
|
||||
variables,
|
||||
)
|
||||
|
||||
if response.data is None:
|
||||
raise GraphQLException(
|
||||
"GraphQL response indicated that the ActiveUser could not be found"
|
||||
)
|
||||
|
||||
return response.data.data
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def activity(
|
||||
self,
|
||||
limit: int = 20,
|
||||
@@ -107,7 +236,7 @@ class Resource(ResourceBase):
|
||||
before: Optional[datetime] = None,
|
||||
after: Optional[datetime] = None,
|
||||
cursor: Optional[datetime] = None,
|
||||
):
|
||||
) -> ActivityCollection:
|
||||
"""
|
||||
Get the activity from a given stream in an Activity collection.
|
||||
Step into the activity `items` for the list of activity.
|
||||
@@ -178,6 +307,7 @@ class Resource(ResourceBase):
|
||||
schema=ActivityCollection,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get_all_pending_invites(self) -> List[PendingStreamCollaborator]:
|
||||
"""Get all of the active user's pending stream invites
|
||||
|
||||
@@ -198,13 +328,18 @@ class Resource(ResourceBase):
|
||||
inviteId
|
||||
streamId
|
||||
streamName
|
||||
projectId
|
||||
projectName
|
||||
title
|
||||
role
|
||||
invitedBy {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,6 +352,7 @@ class Resource(ResourceBase):
|
||||
schema=PendingStreamCollaborator,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get_pending_invite(
|
||||
self, stream_id: str, token: Optional[str] = None
|
||||
) -> Optional[PendingStreamCollaborator]:
|
||||
@@ -241,15 +377,21 @@ class Resource(ResourceBase):
|
||||
streamInvite(streamId: $streamId, token: $token) {
|
||||
id
|
||||
token
|
||||
inviteId
|
||||
streamId
|
||||
streamName
|
||||
projectId
|
||||
projectName
|
||||
title
|
||||
role
|
||||
invitedBy {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
from typing import Optional
|
||||
|
||||
from gql import gql
|
||||
|
||||
from specklepy.core.api.inputs.model_inputs import (
|
||||
CreateModelInput,
|
||||
DeleteModelInput,
|
||||
ModelVersionsFilter,
|
||||
UpdateModelInput,
|
||||
)
|
||||
from specklepy.core.api.inputs.project_inputs import ProjectModelsFilter
|
||||
from specklepy.core.api.models import Model, ModelWithVersions, ResourceCollection
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.core.api.responses import DataResponse
|
||||
|
||||
NAME = "model"
|
||||
|
||||
|
||||
class ModelResource(ResourceBase):
|
||||
"""API Access class for models"""
|
||||
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
server_version=server_version,
|
||||
)
|
||||
|
||||
def get(self, model_id: str, project_id: str) -> Model:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query ModelGet($modelId: String!, $projectId: String!) {
|
||||
data:project(id: $projectId) {
|
||||
data:model(id: $modelId) {
|
||||
id
|
||||
name
|
||||
previewUrl
|
||||
updatedAt
|
||||
description
|
||||
displayName
|
||||
createdAt
|
||||
author {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"modelId": model_id,
|
||||
"projectId": project_id,
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[Model]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def get_with_versions(
|
||||
self,
|
||||
model_id: str,
|
||||
project_id: str,
|
||||
*,
|
||||
versions_limit: int = 25,
|
||||
versions_cursor: Optional[str] = None,
|
||||
versions_filter: Optional[ModelVersionsFilter] = None,
|
||||
) -> ModelWithVersions:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query ModelGetWithVersions($modelId: String!, $projectId: String!, $versionsLimit: Int!, $versionsCursor: String, $versionsFilter: ModelVersionsFilter) {
|
||||
data:project(id: $projectId) {
|
||||
data:model(id: $modelId) {
|
||||
id
|
||||
name
|
||||
previewUrl
|
||||
updatedAt
|
||||
versions(limit: $versionsLimit, cursor: $versionsCursor, filter: $versionsFilter) {
|
||||
items {
|
||||
id
|
||||
referencedObject
|
||||
message
|
||||
sourceApplication
|
||||
createdAt
|
||||
previewUrl
|
||||
authorUser {
|
||||
avatar
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
totalCount
|
||||
cursor
|
||||
}
|
||||
description
|
||||
displayName
|
||||
createdAt
|
||||
author {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
"modelId": model_id,
|
||||
"versionsLimit": versions_limit,
|
||||
"versionsCursor": versions_cursor,
|
||||
"versionsFilter": (
|
||||
versions_filter.model_dump(warnings="error")
|
||||
if versions_filter
|
||||
else None
|
||||
),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[ModelWithVersions]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def get_models(
|
||||
self,
|
||||
project_id: str,
|
||||
*,
|
||||
models_limit: int = 25,
|
||||
models_cursor: Optional[str] = None,
|
||||
models_filter: Optional[ProjectModelsFilter] = None,
|
||||
) -> ResourceCollection[Model]:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) {
|
||||
data:project(id: $projectId) {
|
||||
data:models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
previewUrl
|
||||
updatedAt
|
||||
displayName
|
||||
description
|
||||
createdAt
|
||||
author {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
}
|
||||
totalCount
|
||||
cursor
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
"modelsLimit": models_limit,
|
||||
"modelsCursor": models_cursor,
|
||||
"modelsFilter": (
|
||||
models_filter.model_dump(warnings="error") if models_filter else None
|
||||
),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[ResourceCollection[Model]]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def create(self, input: CreateModelInput) -> Model:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ModelCreate($input: CreateModelInput!) {
|
||||
data:modelMutations {
|
||||
data:create(input: $input) {
|
||||
id
|
||||
displayName
|
||||
name
|
||||
description
|
||||
createdAt
|
||||
updatedAt
|
||||
previewUrl
|
||||
author {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[Model]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def delete(self, input: DeleteModelInput) -> bool:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ModelDelete($input: DeleteModelInput!) {
|
||||
data:modelMutations {
|
||||
data:delete(input: $input)
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {"input": input.model_dump(warnings="error")}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[bool]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def update(self, input: UpdateModelInput) -> Model:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ModelUpdate($input: UpdateModelInput!) {
|
||||
data:modelMutations {
|
||||
data:update(input: $input) {
|
||||
id
|
||||
name
|
||||
displayName
|
||||
description
|
||||
createdAt
|
||||
updatedAt
|
||||
previewUrl
|
||||
author {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[Model]], QUERY, variables
|
||||
).data.data
|
||||
+89
-22
@@ -1,16 +1,26 @@
|
||||
from datetime import datetime, timezone
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from deprecated import deprecated
|
||||
from gql import gql
|
||||
|
||||
from specklepy.core.api.models import ActivityCollection, LimitedUser
|
||||
from specklepy.core.api.models import (
|
||||
ActivityCollection,
|
||||
LimitedUser,
|
||||
UserSearchResultCollection,
|
||||
)
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.core.api.responses import DataResponse
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
NAME = "other_user"
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
class OtherUserResource(ResourceBase):
|
||||
"""API Access class for other users, that are not the currently active user."""
|
||||
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
@@ -23,7 +33,7 @@ class Resource(ResourceBase):
|
||||
)
|
||||
self.schema = LimitedUser
|
||||
|
||||
def get(self, id: str) -> LimitedUser:
|
||||
def get(self, id: str) -> Optional[LimitedUser]:
|
||||
"""
|
||||
Gets the profile of another user.
|
||||
|
||||
@@ -33,26 +43,81 @@ class Resource(ResourceBase):
|
||||
Returns:
|
||||
LimitedUser -- the retrieved profile of another user
|
||||
"""
|
||||
query = gql(
|
||||
QUERY = gql(
|
||||
"""
|
||||
query OtherUser($id: String!) {
|
||||
otherUser(id: $id) {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
query LimitedUser($id: String!) {
|
||||
data:otherUser(id: $id){
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {"id": id}
|
||||
variables = {"id": id}
|
||||
|
||||
return self.make_request(query=query, params=params, return_type="otherUser")
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[Optional[LimitedUser]], QUERY, variables
|
||||
).data
|
||||
|
||||
def user_search(
|
||||
self,
|
||||
query: str,
|
||||
*,
|
||||
limit: int = 25,
|
||||
cursor: Optional[str] = None,
|
||||
archived: bool = False,
|
||||
emailOnly: bool = False,
|
||||
) -> UserSearchResultCollection:
|
||||
"""Searches for a user on the server, by name or email. The search query must be at least
|
||||
3 characters long
|
||||
|
||||
Arguments:
|
||||
search_query {str} -- a string to search for
|
||||
limit {int} -- the maximum number of results to return
|
||||
cursor {Optional[str]} --
|
||||
archived {bool} --
|
||||
emailOnly {bool} --
|
||||
Returns:
|
||||
ResourceCollection[LimitedUser] -- User objects that match the search query
|
||||
"""
|
||||
|
||||
QUERY = gql(
|
||||
"""
|
||||
query UserSearch($query: String!, $limit: Int!, $cursor: String, $archived: Boolean, $emailOnly: Boolean) {
|
||||
data:userSearch(query: $query, limit: $limit, cursor: $cursor, archived: $archived, emailOnly: $emailOnly) {
|
||||
cursor
|
||||
items {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
variables = {
|
||||
"query": query,
|
||||
"limit": limit,
|
||||
"cursor": cursor,
|
||||
"archived": archived,
|
||||
"emailOnly": emailOnly,
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[UserSearchResultCollection], QUERY, variables
|
||||
).data
|
||||
|
||||
@deprecated(reason="Use user_search instead", version=FE1_DEPRECATION_VERSION)
|
||||
def search(
|
||||
self, search_query: str, limit: int = 25
|
||||
) -> Union[List[LimitedUser], SpeckleException]:
|
||||
@@ -75,12 +140,13 @@ class Resource(ResourceBase):
|
||||
query UserSearch($search_query: String!, $limit: Int!) {
|
||||
userSearch(query: $search_query, limit: $limit) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +158,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type=["userSearch", "items"]
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def activity(
|
||||
self,
|
||||
user_id: str,
|
||||
@@ -0,0 +1,254 @@
|
||||
from typing import Any, Optional, Tuple
|
||||
|
||||
from gql import Client, gql
|
||||
|
||||
from specklepy.core.api.credentials import Account
|
||||
from specklepy.core.api.inputs.project_inputs import (
|
||||
ProjectInviteCreateInput,
|
||||
ProjectInviteUseInput,
|
||||
)
|
||||
from specklepy.core.api.models import PendingStreamCollaborator, ProjectWithTeam
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.core.api.responses import DataResponse
|
||||
|
||||
NAME = "project_invite"
|
||||
|
||||
|
||||
class ProjectInviteResource(ResourceBase):
|
||||
"""API Access class for project invites"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
account: Account,
|
||||
basepath: str,
|
||||
client: Client,
|
||||
server_version: Optional[Tuple[Any, ...]],
|
||||
) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
server_version=server_version,
|
||||
)
|
||||
|
||||
def create(
|
||||
self, project_id: str, input: ProjectInviteCreateInput
|
||||
) -> ProjectWithTeam:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ProjectInviteCreate($projectId: ID!, $input: ProjectInviteCreateInput!) {
|
||||
data:projectMutations {
|
||||
data:invites {
|
||||
data:create(projectId: $projectId, input: $input) {
|
||||
id
|
||||
name
|
||||
description
|
||||
visibility
|
||||
allowPublicComments
|
||||
role
|
||||
createdAt
|
||||
updatedAt
|
||||
workspaceId
|
||||
sourceApps
|
||||
team {
|
||||
id
|
||||
role
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
invitedTeam {
|
||||
id
|
||||
inviteId
|
||||
projectId
|
||||
projectName
|
||||
title
|
||||
role
|
||||
token
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
invitedBy {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[DataResponse[ProjectWithTeam]]], QUERY, variables
|
||||
).data.data.data
|
||||
|
||||
def use(self, input: ProjectInviteUseInput) -> bool:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ProjectInviteUse($input: ProjectInviteUseInput!) {
|
||||
data:projectMutations {
|
||||
data:invites {
|
||||
data:use(input: $input)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[DataResponse[bool]]], QUERY, variables
|
||||
).data.data.data
|
||||
|
||||
def get(
|
||||
self, project_id: str, token: Optional[str]
|
||||
) -> Optional[PendingStreamCollaborator]:
|
||||
"""Returns: The invite, or None if no invite exists"""
|
||||
|
||||
QUERY = gql(
|
||||
"""
|
||||
query ProjectInvite($projectId: String!, $token: String) {
|
||||
data:projectInvite(projectId: $projectId, token: $token) {
|
||||
id
|
||||
inviteId
|
||||
invitedBy {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
projectId
|
||||
projectName
|
||||
role
|
||||
title
|
||||
token
|
||||
user {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
"token": token,
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[Optional[PendingStreamCollaborator]], QUERY, variables
|
||||
).data
|
||||
|
||||
def cancel(
|
||||
self,
|
||||
project_id: str,
|
||||
invite_id: str,
|
||||
) -> ProjectWithTeam:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ProjectInviteCancel($projectId: ID!, $inviteId: String!) {
|
||||
data:projectMutations {
|
||||
data:invites {
|
||||
data:cancel(projectId: $projectId, inviteId: $inviteId) {
|
||||
id
|
||||
name
|
||||
description
|
||||
visibility
|
||||
allowPublicComments
|
||||
role
|
||||
createdAt
|
||||
updatedAt
|
||||
sourceApps
|
||||
workspaceId
|
||||
team {
|
||||
id
|
||||
role
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
invitedTeam {
|
||||
id
|
||||
inviteId
|
||||
projectId
|
||||
projectName
|
||||
title
|
||||
role
|
||||
token
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
invitedBy {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
"inviteId": invite_id,
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[DataResponse[ProjectWithTeam]]], QUERY, variables
|
||||
).data.data.data
|
||||
@@ -0,0 +1,336 @@
|
||||
from typing import Optional
|
||||
|
||||
from gql import gql
|
||||
|
||||
from specklepy.core.api.inputs.project_inputs import (
|
||||
ProjectCreateInput,
|
||||
ProjectModelsFilter,
|
||||
ProjectUpdateInput,
|
||||
ProjectUpdateRoleInput,
|
||||
)
|
||||
from specklepy.core.api.models import Project, ProjectWithModels, ProjectWithTeam
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.core.api.responses import DataResponse
|
||||
|
||||
NAME = "project"
|
||||
|
||||
|
||||
class ProjectResource(ResourceBase):
|
||||
"""API Access class for projects"""
|
||||
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
server_version=server_version,
|
||||
)
|
||||
|
||||
def get(self, project_id: str) -> Project:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query Project($projectId: String!) {
|
||||
data:project(id: $projectId) {
|
||||
allowPublicComments
|
||||
createdAt
|
||||
description
|
||||
id
|
||||
name
|
||||
role
|
||||
sourceApps
|
||||
updatedAt
|
||||
visibility
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[Project], QUERY, variables
|
||||
).data
|
||||
|
||||
def get_with_models(
|
||||
self,
|
||||
project_id: str,
|
||||
*,
|
||||
models_limit: int = 25,
|
||||
models_cursor: Optional[str] = None,
|
||||
models_filter: Optional[ProjectModelsFilter] = None,
|
||||
) -> ProjectWithModels:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) {
|
||||
data:project(id: $projectId) {
|
||||
id
|
||||
name
|
||||
description
|
||||
visibility
|
||||
allowPublicComments
|
||||
role
|
||||
createdAt
|
||||
updatedAt
|
||||
sourceApps
|
||||
workspaceId
|
||||
models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
previewUrl
|
||||
updatedAt
|
||||
displayName
|
||||
description
|
||||
createdAt
|
||||
author {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
}
|
||||
cursor
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
"modelsLimit": models_limit,
|
||||
"modelsCursor": models_cursor,
|
||||
"modelsFilter": (
|
||||
models_filter.model_dump(warnings="error") if models_filter else None
|
||||
),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[ProjectWithModels], QUERY, variables
|
||||
).data
|
||||
|
||||
def get_with_team(self, project_id: str) -> ProjectWithTeam:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query ProjectGetWithTeam($projectId: String!) {
|
||||
data:project(id: $projectId) {
|
||||
id
|
||||
name
|
||||
description
|
||||
visibility
|
||||
allowPublicComments
|
||||
role
|
||||
createdAt
|
||||
updatedAt
|
||||
workspaceId
|
||||
sourceApps
|
||||
team {
|
||||
id
|
||||
role
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
invitedTeam {
|
||||
id
|
||||
inviteId
|
||||
projectId
|
||||
projectName
|
||||
title
|
||||
role
|
||||
token
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
invitedBy {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[ProjectWithTeam], QUERY, variables
|
||||
).data
|
||||
|
||||
def create(self, input: ProjectCreateInput) -> Project:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ProjectCreate($input: ProjectCreateInput) {
|
||||
data:projectMutations {
|
||||
data:create(input: $input) {
|
||||
id
|
||||
name
|
||||
description
|
||||
visibility
|
||||
allowPublicComments
|
||||
role
|
||||
createdAt
|
||||
updatedAt
|
||||
sourceApps
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[Project]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def update(self, input: ProjectUpdateInput) -> Project:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ProjectUpdate($input: ProjectUpdateInput!) {
|
||||
data:projectMutations{
|
||||
data:update(update: $input) {
|
||||
allowPublicComments
|
||||
createdAt
|
||||
description
|
||||
id
|
||||
name
|
||||
role
|
||||
sourceApps
|
||||
updatedAt
|
||||
visibility
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[Project]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def delete(self, project_id: str) -> bool:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ProjectDelete($projectId: String!) {
|
||||
data:projectMutations {
|
||||
data:delete(id: $projectId)
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[bool]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def update_role(self, input: ProjectUpdateRoleInput) -> ProjectWithTeam:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation ProjectUpdateRole($input: ProjectUpdateRoleInput!) {
|
||||
data:projectMutations {
|
||||
data:updateRole(input: $input) {
|
||||
id
|
||||
name
|
||||
description
|
||||
visibility
|
||||
allowPublicComments
|
||||
role
|
||||
createdAt
|
||||
updatedAt
|
||||
sourceApps
|
||||
workspaceId
|
||||
team {
|
||||
id
|
||||
role
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
invitedTeam {
|
||||
id
|
||||
inviteId
|
||||
projectId
|
||||
projectName
|
||||
title
|
||||
role
|
||||
token
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
invitedBy {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[ProjectWithTeam]], QUERY, variables
|
||||
).data.data
|
||||
+1
-1
@@ -11,7 +11,7 @@ from specklepy.logging.exceptions import GraphQLException
|
||||
NAME = "server"
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
class ServerResource(ResourceBase):
|
||||
"""API Access class for the server"""
|
||||
|
||||
def __init__(self, account, basepath, client) -> None:
|
||||
@@ -0,0 +1,218 @@
|
||||
from functools import wraps
|
||||
from typing import Any, Callable, Dict, Optional, Sequence, Type
|
||||
|
||||
from gql import gql
|
||||
from graphql import DocumentNode
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
from specklepy.core.api.models import (
|
||||
ProjectModelsUpdatedMessage,
|
||||
ProjectUpdatedMessage,
|
||||
ProjectVersionsUpdatedMessage,
|
||||
UserProjectsUpdatedMessage,
|
||||
)
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.core.api.responses import DataResponse
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
NAME = "subscribe"
|
||||
|
||||
TEventArgs = TypeVar("TEventArgs", bound=BaseModel)
|
||||
|
||||
|
||||
def check_wsclient(function):
|
||||
@wraps(function)
|
||||
async def check_wsclient_wrapper(self, *args, **kwargs):
|
||||
if self.client is None:
|
||||
raise SpeckleException(
|
||||
"You must authenticate before you can subscribe to events"
|
||||
)
|
||||
else:
|
||||
return await function(self, *args, **kwargs)
|
||||
|
||||
return check_wsclient_wrapper
|
||||
|
||||
|
||||
class SubscriptionResource(ResourceBase):
|
||||
"""API Access class for subscriptions"""
|
||||
|
||||
def __init__(self, account, basepath, client) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
)
|
||||
|
||||
async def user_projects_updated(
|
||||
self, callback: Callable[[UserProjectsUpdatedMessage], None]
|
||||
) -> None:
|
||||
QUERY = gql(
|
||||
"""
|
||||
subscription UserProjectsUpdated {
|
||||
data:userProjectsUpdated {
|
||||
id
|
||||
project {
|
||||
id
|
||||
name
|
||||
description
|
||||
visibility
|
||||
allowPublicComments
|
||||
role
|
||||
createdAt
|
||||
updatedAt
|
||||
sourceApps
|
||||
workspaceId
|
||||
}
|
||||
type
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
await self.subscribe_2(
|
||||
DataResponse[UserProjectsUpdatedMessage],
|
||||
QUERY,
|
||||
None,
|
||||
callback=lambda d: callback(d.data),
|
||||
)
|
||||
|
||||
async def project_models_updated(
|
||||
self,
|
||||
callback: Callable[[ProjectModelsUpdatedMessage], None],
|
||||
id: str,
|
||||
model_ids: Optional[Sequence[str]] = None,
|
||||
) -> None:
|
||||
QUERY = gql(
|
||||
"""
|
||||
subscription ProjectModelsUpdated($id: String!, $modelIds: [String!]) {
|
||||
data:projectModelsUpdated(id: $id, modelIds: $modelIds) {
|
||||
id
|
||||
model {
|
||||
id
|
||||
name
|
||||
previewUrl
|
||||
updatedAt
|
||||
description
|
||||
displayName
|
||||
createdAt
|
||||
author {
|
||||
avatar
|
||||
bio
|
||||
company
|
||||
id
|
||||
name
|
||||
role
|
||||
verified
|
||||
}
|
||||
}
|
||||
type
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {"id": id, "modelIds": model_ids}
|
||||
|
||||
await self.subscribe_2(
|
||||
DataResponse[ProjectModelsUpdatedMessage],
|
||||
QUERY,
|
||||
variables,
|
||||
callback=lambda d: callback(d.data),
|
||||
)
|
||||
|
||||
async def project_updated(
|
||||
self,
|
||||
callback: Callable[[ProjectUpdatedMessage], None],
|
||||
id: str,
|
||||
) -> None:
|
||||
QUERY = gql(
|
||||
"""
|
||||
subscription ProjectUpdated($id: String!) {
|
||||
data:projectUpdated(id: $id) {
|
||||
id
|
||||
project {
|
||||
id
|
||||
name
|
||||
description
|
||||
visibility
|
||||
allowPublicComments
|
||||
role
|
||||
createdAt
|
||||
updatedAt
|
||||
sourceApps
|
||||
workspaceId
|
||||
}
|
||||
type
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {"id": id}
|
||||
|
||||
await self.subscribe_2(
|
||||
DataResponse[ProjectUpdatedMessage],
|
||||
QUERY,
|
||||
variables,
|
||||
callback=lambda d: callback(d.data),
|
||||
)
|
||||
|
||||
async def project_versions_updated(
|
||||
self,
|
||||
callback: Callable[[ProjectVersionsUpdatedMessage], None],
|
||||
id: str,
|
||||
) -> None:
|
||||
QUERY = gql(
|
||||
"""
|
||||
subscription ProjectVersionsUpdated($id: String!) {
|
||||
data:projectVersionsUpdated(id: $id) {
|
||||
id
|
||||
modelId
|
||||
type
|
||||
version {
|
||||
id
|
||||
referencedObject
|
||||
message
|
||||
sourceApplication
|
||||
createdAt
|
||||
previewUrl
|
||||
authorUser {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
verified
|
||||
role
|
||||
avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {"id": id}
|
||||
|
||||
await self.subscribe_2(
|
||||
DataResponse[ProjectVersionsUpdatedMessage],
|
||||
QUERY,
|
||||
variables,
|
||||
callback=lambda d: callback(d.data),
|
||||
)
|
||||
|
||||
@check_wsclient
|
||||
async def subscribe_2(
|
||||
self,
|
||||
response_type: Type[TEventArgs],
|
||||
query: DocumentNode,
|
||||
variables: Optional[Dict[str, Any]],
|
||||
callback: Callable[[TEventArgs], None],
|
||||
) -> None:
|
||||
async with self.client as session:
|
||||
self.session = session
|
||||
gen = session.subscribe(query, variable_values=variables)
|
||||
async for res in gen:
|
||||
event_arg = response_type.model_validate(res)
|
||||
callback(event_arg)
|
||||
@@ -0,0 +1,234 @@
|
||||
from typing import Optional
|
||||
|
||||
from gql import gql
|
||||
|
||||
from specklepy.core.api.inputs.model_inputs import ModelVersionsFilter
|
||||
from specklepy.core.api.inputs.version_inputs import (
|
||||
CreateVersionInput,
|
||||
DeleteVersionsInput,
|
||||
MarkReceivedVersionInput,
|
||||
MoveVersionsInput,
|
||||
UpdateVersionInput,
|
||||
)
|
||||
from specklepy.core.api.models import ResourceCollection, Version
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.core.api.responses import DataResponse
|
||||
|
||||
NAME = "model"
|
||||
|
||||
|
||||
class VersionResource(ResourceBase):
|
||||
"""API Access class for model versions"""
|
||||
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
server_version=server_version,
|
||||
)
|
||||
|
||||
def get(self, version_id: str, project_id: str) -> Version:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query VersionGet($projectId: String!, $versionId: String!) {
|
||||
data:project(id: $projectId) {
|
||||
data:version(id: $versionId) {
|
||||
id
|
||||
referencedObject
|
||||
message
|
||||
sourceApplication
|
||||
createdAt
|
||||
previewUrl
|
||||
authorUser {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
verified
|
||||
role
|
||||
avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
"versionId": version_id,
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[Version]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def get_versions(
|
||||
self,
|
||||
model_id: str,
|
||||
project_id: str,
|
||||
*,
|
||||
limit: int = 25,
|
||||
cursor: Optional[str] = None,
|
||||
filter: Optional[ModelVersionsFilter] = None,
|
||||
) -> ResourceCollection[Version]:
|
||||
QUERY = gql(
|
||||
"""
|
||||
query VersionGetVersions($projectId: String!, $modelId: String!, $limit: Int!, $cursor: String, $filter: ModelVersionsFilter) {
|
||||
data:project(id: $projectId) {
|
||||
data:model(id: $modelId) {
|
||||
data:versions(limit: $limit, cursor: $cursor, filter: $filter) {
|
||||
items {
|
||||
id
|
||||
referencedObject
|
||||
message
|
||||
sourceApplication
|
||||
createdAt
|
||||
previewUrl
|
||||
authorUser {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
verified
|
||||
role
|
||||
avatar
|
||||
}
|
||||
}
|
||||
cursor
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"projectId": project_id,
|
||||
"modelId": model_id,
|
||||
"limit": limit,
|
||||
"cursor": cursor,
|
||||
"filter": filter.model_dump(warnings="error") if filter else None,
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[DataResponse[ResourceCollection[Version]]]],
|
||||
QUERY,
|
||||
variables,
|
||||
).data.data.data
|
||||
|
||||
def create(self, input: CreateVersionInput) -> str:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation Create($input: CreateVersionInput!) {
|
||||
data:versionMutations {
|
||||
data:create(input: $input) {
|
||||
data:id
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[DataResponse[str]]], QUERY, variables
|
||||
).data.data.data
|
||||
|
||||
def update(self, input: UpdateVersionInput) -> Version:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation VersionUpdate($input: UpdateVersionInput!) {
|
||||
data:versionMutations {
|
||||
data:update(input: $input) {
|
||||
id
|
||||
referencedObject
|
||||
message
|
||||
sourceApplication
|
||||
createdAt
|
||||
previewUrl
|
||||
authorUser {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
verified
|
||||
role
|
||||
avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {"input": input.model_dump(warnings="error")}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[Version]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def move_to_model(self, input: MoveVersionsInput) -> str:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation VersionMoveToModel($input: MoveVersionsInput!) {
|
||||
data:versionMutations {
|
||||
data:moveToModel(input: $input) {
|
||||
data:id
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[DataResponse[str]]], QUERY, variables
|
||||
).data.data.data
|
||||
|
||||
def delete(self, input: DeleteVersionsInput) -> bool:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation VersionDelete($input: DeleteVersionsInput!) {
|
||||
data:versionMutations {
|
||||
data:delete(input: $input)
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[bool]], QUERY, variables
|
||||
).data.data
|
||||
|
||||
def received(self, input: MarkReceivedVersionInput) -> bool:
|
||||
QUERY = gql(
|
||||
"""
|
||||
mutation MarkReceived($input: MarkReceivedVersionInput!) {
|
||||
data:versionMutations {
|
||||
data:markReceived(input: $input)
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
variables = {
|
||||
"input": input.model_dump(warnings="error"),
|
||||
}
|
||||
|
||||
return self.make_request_and_parse_response(
|
||||
DataResponse[DataResponse[bool]], QUERY, variables
|
||||
).data.data
|
||||
@@ -0,0 +1,15 @@
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.core.api.models.deprecated import FE1_DEPRECATION_VERSION
|
||||
from specklepy.core.api.resources import ActiveUserResource
|
||||
|
||||
|
||||
@deprecated(
|
||||
reason="Class renamed to ActiveUserResource", version=FE1_DEPRECATION_VERSION
|
||||
)
|
||||
class Resource(ActiveUserResource):
|
||||
"""
|
||||
Class renamed to ActiveUserResource
|
||||
"""
|
||||
|
||||
pass
|
||||
+15
-2
@@ -1,8 +1,13 @@
|
||||
from typing import Optional
|
||||
|
||||
from deprecated import deprecated
|
||||
from gql import gql
|
||||
|
||||
from specklepy.core.api.models import Branch
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
Branch,
|
||||
)
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
@@ -10,7 +15,10 @@ NAME = "branch"
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for branches"""
|
||||
"""
|
||||
API Access class for branches
|
||||
Branch resource is deprecated, please use model resource instead
|
||||
"""
|
||||
|
||||
def __init__(self, account, basepath, client) -> None:
|
||||
super().__init__(
|
||||
@@ -21,6 +29,7 @@ class Resource(ResourceBase):
|
||||
)
|
||||
self.schema = Branch
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def create(
|
||||
self, stream_id: str, name: str, description: str = "No description provided"
|
||||
) -> str:
|
||||
@@ -54,6 +63,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type="branchCreate", parse_response=False
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get(self, stream_id: str, name: str, commits_limit: int = 10):
|
||||
"""Get a branch by name from a stream
|
||||
|
||||
@@ -101,6 +111,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type=["stream", "branch"]
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def list(self, stream_id: str, branches_limit: int = 10, commits_limit: int = 10):
|
||||
"""Get a list of branches from a given stream
|
||||
|
||||
@@ -156,6 +167,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type=["stream", "branches", "items"]
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def update(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -197,6 +209,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type="branchUpdate", parse_response=False
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def delete(self, stream_id: str, branch_id: str):
|
||||
"""Delete a branch
|
||||
|
||||
+16
-2
@@ -1,8 +1,13 @@
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from deprecated import deprecated
|
||||
from gql import gql
|
||||
|
||||
from specklepy.core.api.models import Commit
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
Commit,
|
||||
)
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
@@ -10,7 +15,10 @@ NAME = "commit"
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for commits"""
|
||||
"""
|
||||
API Access class for commits
|
||||
Commit resource is deprecated, please use version resource instead
|
||||
"""
|
||||
|
||||
def __init__(self, account, basepath, client) -> None:
|
||||
super().__init__(
|
||||
@@ -21,6 +29,7 @@ class Resource(ResourceBase):
|
||||
)
|
||||
self.schema = Commit
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get(self, stream_id: str, commit_id: str) -> Commit:
|
||||
"""
|
||||
Gets a commit given a stream and the commit id
|
||||
@@ -59,6 +68,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type=["stream", "commit"]
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def list(self, stream_id: str, limit: int = 10) -> List[Commit]:
|
||||
"""
|
||||
Get a list of commits on a given stream
|
||||
@@ -100,6 +110,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type=["stream", "commits", "items"]
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def create(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -149,6 +160,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type="commitCreate", parse_response=False
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def update(self, stream_id: str, commit_id: str, message: str) -> bool:
|
||||
"""
|
||||
Update a commit
|
||||
@@ -176,6 +188,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type="commitUpdate", parse_response=False
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def delete(self, stream_id: str, commit_id: str) -> bool:
|
||||
"""
|
||||
Delete a commit
|
||||
@@ -200,6 +213,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type="commitDelete", parse_response=False
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def received(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -0,0 +1,15 @@
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.core.api.models.deprecated import FE1_DEPRECATION_VERSION
|
||||
from specklepy.core.api.resources import OtherUserResource
|
||||
|
||||
|
||||
@deprecated(
|
||||
reason="Class renamed to OtherUserResource", version=FE1_DEPRECATION_VERSION
|
||||
)
|
||||
class Resource(OtherUserResource):
|
||||
"""
|
||||
Class renamed to OtherUserResource
|
||||
"""
|
||||
|
||||
pass
|
||||
@@ -0,0 +1,11 @@
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.core.api.models.deprecated import FE1_DEPRECATION_VERSION
|
||||
from specklepy.core.api.resources import ServerResource
|
||||
|
||||
NAME = "server"
|
||||
|
||||
|
||||
@deprecated(reason="Renamed to ServerResource", version=FE1_DEPRECATION_VERSION)
|
||||
class Resource(ServerResource):
|
||||
"""API Access class for the server"""
|
||||
+34
-3
@@ -1,6 +1,7 @@
|
||||
from datetime import datetime, timezone
|
||||
from typing import List, Optional
|
||||
|
||||
from deprecated import deprecated
|
||||
from gql import gql
|
||||
|
||||
from specklepy.core.api.models import (
|
||||
@@ -8,6 +9,10 @@ from specklepy.core.api.models import (
|
||||
PendingStreamCollaborator,
|
||||
Stream,
|
||||
)
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
)
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.logging.exceptions import SpeckleException, UnsupportedException
|
||||
|
||||
@@ -15,7 +20,10 @@ NAME = "stream"
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for streams"""
|
||||
"""
|
||||
API Access class for streams
|
||||
Stream resource is deprecated, please use project resource instead
|
||||
"""
|
||||
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
@@ -28,6 +36,7 @@ class Resource(ResourceBase):
|
||||
|
||||
self.schema = Stream
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get(self, id: str, branch_limit: int = 10, commit_limit: int = 10) -> Stream:
|
||||
"""Get the specified stream from the server
|
||||
|
||||
@@ -89,6 +98,7 @@ class Resource(ResourceBase):
|
||||
|
||||
return self.make_request(query=query, params=params, return_type="stream")
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def list(self, stream_limit: int = 10) -> List[Stream]:
|
||||
"""Get a list of the user's streams
|
||||
|
||||
@@ -101,7 +111,7 @@ class Resource(ResourceBase):
|
||||
query = gql(
|
||||
"""
|
||||
query User($stream_limit: Int!) {
|
||||
user {
|
||||
activeUser {
|
||||
id
|
||||
bio
|
||||
name
|
||||
@@ -139,9 +149,10 @@ class Resource(ResourceBase):
|
||||
params = {"stream_limit": stream_limit}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type=["user", "streams", "items"]
|
||||
query=query, params=params, return_type=["activeUser", "streams", "items"]
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def create(
|
||||
self,
|
||||
name: str = "Anonymous Python Stream",
|
||||
@@ -176,6 +187,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type="streamCreate", parse_response=False
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def update(
|
||||
self,
|
||||
id: str,
|
||||
@@ -216,6 +228,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type="streamUpdate", parse_response=False
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def delete(self, id: str) -> bool:
|
||||
"""Delete a stream given its id
|
||||
|
||||
@@ -239,6 +252,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type="streamDelete", parse_response=False
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def search(
|
||||
self,
|
||||
search_query: str,
|
||||
@@ -318,6 +332,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type=["streams", "items"]
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def favorite(self, stream_id: str, favorited: bool = True):
|
||||
"""Favorite or unfavorite the given stream.
|
||||
|
||||
@@ -351,6 +366,7 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type=["streamFavorite"]
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def get_all_pending_invites(
|
||||
self, stream_id: str
|
||||
) -> List[PendingStreamCollaborator]:
|
||||
@@ -378,19 +394,27 @@ class Resource(ResourceBase):
|
||||
inviteId
|
||||
streamId
|
||||
streamName
|
||||
projectName
|
||||
projectId
|
||||
title
|
||||
role
|
||||
invitedBy{
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
user {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,6 +430,7 @@ class Resource(ResourceBase):
|
||||
schema=PendingStreamCollaborator,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def invite(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -462,6 +487,7 @@ class Resource(ResourceBase):
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def invite_batch(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -521,6 +547,7 @@ class Resource(ResourceBase):
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def invite_cancel(self, stream_id: str, invite_id: str) -> bool:
|
||||
"""Cancel an existing stream invite
|
||||
|
||||
@@ -552,6 +579,7 @@ class Resource(ResourceBase):
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def invite_use(self, stream_id: str, token: str, accept: bool = True) -> bool:
|
||||
"""Accept or decline a stream invite
|
||||
|
||||
@@ -589,6 +617,7 @@ class Resource(ResourceBase):
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def update_permission(self, stream_id: str, user_id: str, role: str):
|
||||
"""Updates permissions for a user on a given stream
|
||||
|
||||
@@ -635,6 +664,7 @@ class Resource(ResourceBase):
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def revoke_permission(self, stream_id: str, user_id: str):
|
||||
"""Revoke permissions from a user on a given stream
|
||||
|
||||
@@ -664,6 +694,7 @@ class Resource(ResourceBase):
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
def activity(
|
||||
self,
|
||||
stream_id: str,
|
||||
+7
-1
@@ -1,11 +1,16 @@
|
||||
from functools import wraps
|
||||
from typing import Callable, Dict, List, Optional, Union
|
||||
|
||||
from deprecated import deprecated
|
||||
from gql import gql
|
||||
from graphql import DocumentNode
|
||||
|
||||
from specklepy.core.api.models.deprecated import (
|
||||
FE1_DEPRECATION_REASON,
|
||||
FE1_DEPRECATION_VERSION,
|
||||
Stream,
|
||||
)
|
||||
from specklepy.core.api.resource import ResourceBase
|
||||
from specklepy.core.api.resources.stream import Stream
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
NAME = "subscribe"
|
||||
@@ -35,6 +40,7 @@ class Resource(ResourceBase):
|
||||
name=NAME,
|
||||
)
|
||||
|
||||
@deprecated(reason=FE1_DEPRECATION_REASON, version=FE1_DEPRECATION_VERSION)
|
||||
@check_wsclient
|
||||
async def stream_added(self, callback: Optional[Callable] = None):
|
||||
"""Subscribes to new stream added event for your profile.
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: https://app.speckle.systems/graphql
|
||||
documents: '**/*.graphql'
|
||||
@@ -0,0 +1,9 @@
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class DataResponse(BaseModel, Generic[T]):
|
||||
data: T
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Provides uniform and consistent path helpers for `specklepy`
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
"""Builtin Speckle object kit."""
|
||||
|
||||
from specklepy.objects.GIS.CRS import CRS
|
||||
from specklepy.objects.GIS.features import (
|
||||
GisMultipatchFeature,
|
||||
GisNonGeometricFeature,
|
||||
GisPointFeature,
|
||||
GisPolygonFeature,
|
||||
GisPolylineFeature,
|
||||
)
|
||||
from specklepy.objects.GIS.geometry import (
|
||||
GisLineElement,
|
||||
GisPointElement,
|
||||
@@ -15,8 +8,6 @@ from specklepy.objects.GIS.geometry import (
|
||||
GisPolygonGeometry,
|
||||
GisRasterElement,
|
||||
PolygonGeometry,
|
||||
PolygonGeometry3d,
|
||||
GisMultipatchGeometry,
|
||||
)
|
||||
from specklepy.objects.GIS.layers import RasterLayer, VectorLayer
|
||||
|
||||
@@ -25,16 +16,9 @@ __all__ = [
|
||||
"RasterLayer",
|
||||
"GisPolygonGeometry",
|
||||
"PolygonGeometry",
|
||||
"PolygonGeometry3d",
|
||||
"GisMultipatchGeometry",
|
||||
"GisPolygonElement",
|
||||
"GisLineElement",
|
||||
"GisPointElement",
|
||||
"GisRasterElement",
|
||||
"CRS",
|
||||
"GisPointFeature",
|
||||
"GisPolylineFeature",
|
||||
"GisPolygonFeature",
|
||||
"GisMultipatchFeature",
|
||||
"GisNonGeometricFeature",
|
||||
]
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.objects.geometry import Mesh, Point, Polyline
|
||||
from specklepy.objects.GIS.geometry import PolygonGeometry
|
||||
|
||||
|
||||
class GisNonGeometricFeature(Base, speckle_type="Objects.GIS.GisNonGeometricFeature"):
|
||||
"""GIS Table feature"""
|
||||
|
||||
attributes: Base
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
attributes: Optional[Base] = None,
|
||||
) -> None:
|
||||
self.attributes = attributes or Base()
|
||||
|
||||
|
||||
class GisPointFeature(
|
||||
Base,
|
||||
detachable={"displayValue"},
|
||||
speckle_type="Objects.GIS.GisPointFeature",
|
||||
):
|
||||
"""Gis Point Feature"""
|
||||
|
||||
attributes: Base
|
||||
displayValue: List[Point]
|
||||
|
||||
@property
|
||||
def geometry(self) -> List[Point]:
|
||||
return self.displayValue
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
attributes: Optional[Base] = None,
|
||||
displayValue: Optional[List[Point]] = None,
|
||||
) -> None:
|
||||
self.attributes = attributes or Base()
|
||||
displayValue = displayValue or []
|
||||
|
||||
|
||||
class GisPolylineFeature(
|
||||
Base,
|
||||
detachable={"displayValue"},
|
||||
speckle_type="Objects.GIS.GisPolylineFeature",
|
||||
):
|
||||
"""Gis Polyline Feature"""
|
||||
|
||||
attributes: Base
|
||||
displayValue: List[Polyline]
|
||||
|
||||
@property
|
||||
def geometry(self) -> List[Polyline]:
|
||||
return self.displayValue
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
attributes: Optional[Base] = None,
|
||||
displayValue: Optional[List[Point]] = None,
|
||||
) -> None:
|
||||
self.attributes = attributes or Base()
|
||||
displayValue = displayValue or []
|
||||
|
||||
|
||||
class GisPolygonFeature(
|
||||
Base,
|
||||
detachable={"displayValue", "geometry"},
|
||||
speckle_type="Objects.GIS.GisPolygonFeature",
|
||||
):
|
||||
"""Gis Polygon Feature"""
|
||||
|
||||
attributes: Base
|
||||
displayValue: List[Mesh]
|
||||
geometry: List[PolygonGeometry]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
geometry: List[PolygonGeometry],
|
||||
attributes: Optional[Base] = None,
|
||||
displayValue: Optional[List[Point]] = None,
|
||||
) -> None:
|
||||
self.geometry = geometry
|
||||
self.attributes = attributes or Base()
|
||||
displayValue = displayValue or []
|
||||
|
||||
|
||||
class GisMultipatchFeature(
|
||||
Base,
|
||||
detachable={"displayValue", "geometry"},
|
||||
speckle_type="Objects.GIS.GisMultipatchFeature",
|
||||
):
|
||||
"""Gis Multipatch Feature"""
|
||||
|
||||
attributes: Base
|
||||
displayValue: List[Mesh]
|
||||
geometry: List[Base] # GisMultipatchGeometry or PolygonGeometry3d
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
geometry: List[Base],
|
||||
attributes: Optional[Base] = None,
|
||||
displayValue: Optional[List[Point]] = None,
|
||||
) -> None:
|
||||
self.geometry = geometry
|
||||
self.attributes = attributes or Base()
|
||||
displayValue = displayValue or []
|
||||
@@ -1,7 +1,5 @@
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from deprecated import deprecated
|
||||
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.objects.geometry import (
|
||||
Arc,
|
||||
@@ -17,66 +15,20 @@ from specklepy.objects.geometry import (
|
||||
class PolygonGeometry(Base, speckle_type="Objects.GIS.PolygonGeometry"):
|
||||
"""GIS Polygon Geometry"""
|
||||
|
||||
boundary: Polyline
|
||||
voids: List[Polyline]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
units: str,
|
||||
boundary: Polyline,
|
||||
voids: Optional[List[Polyline]] = None,
|
||||
) -> None:
|
||||
super().__init__(units=units)
|
||||
self.boundary = boundary
|
||||
self.voids = voids or []
|
||||
boundary: Optional[Polyline]
|
||||
voids: Optional[List[Polyline]]
|
||||
|
||||
|
||||
GisPolygonGeometry = PolygonGeometry
|
||||
|
||||
|
||||
class PolygonGeometry3d(
|
||||
PolygonGeometry,
|
||||
speckle_type="Objects.GIS.PolygonGeometry3d",
|
||||
):
|
||||
"""GIS Polygon3d Geometry"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
units: str,
|
||||
boundary: Polyline,
|
||||
voids: Optional[List[Polyline]] = None,
|
||||
) -> None:
|
||||
super().__init__(units=units, boundary=boundary, voids=voids)
|
||||
|
||||
|
||||
class GisMultipatchGeometry(
|
||||
Base,
|
||||
speckle_type="Objects.GIS.GisMultipatchGeometry",
|
||||
):
|
||||
"""GIS Polygon3d Geometry"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
units: str,
|
||||
faces: List[int],
|
||||
vertices: List[float],
|
||||
colors: Optional[List[int]],
|
||||
) -> None:
|
||||
super().__init__(units=units)
|
||||
self.faces = faces
|
||||
self.vertices = vertices
|
||||
self.colors = colors or []
|
||||
|
||||
|
||||
@deprecated(version="2.20", reason="Replaced with GisPolygonFeature")
|
||||
class GisPolygonElement(Base, speckle_type="Objects.GIS.PolygonElement"):
|
||||
"""GIS Polygon element"""
|
||||
|
||||
geometry: Optional[List[PolygonGeometry]] = None
|
||||
geometry: Optional[List[GisPolygonGeometry]] = None
|
||||
attributes: Optional[Base] = None
|
||||
|
||||
|
||||
@deprecated(version="2.20", reason="Replaced with GisPolyineFeature")
|
||||
class GisLineElement(Base, speckle_type="Objects.GIS.LineElement"):
|
||||
"""GIS Polyline element"""
|
||||
|
||||
@@ -84,7 +36,6 @@ class GisLineElement(Base, speckle_type="Objects.GIS.LineElement"):
|
||||
attributes: Optional[Base] = None
|
||||
|
||||
|
||||
@deprecated(version="2.20", reason="Replaced with GisPointFeature")
|
||||
class GisPointElement(Base, speckle_type="Objects.GIS.PointElement"):
|
||||
"""GIS Point element"""
|
||||
|
||||
@@ -117,7 +68,6 @@ class GisTopography(
|
||||
"""GIS Raster element with 3d Topography representation"""
|
||||
|
||||
|
||||
@deprecated(version="2.20", reason="Replaced with GisNonGeometricFeature")
|
||||
class GisNonGeometryElement(Base, speckle_type="Objects.GIS.NonGeometryElement"):
|
||||
"""GIS Table feature"""
|
||||
|
||||
|
||||
@@ -303,25 +303,9 @@ class RevitParameter(Base, speckle_type="Objects.BuiltElements.Revit.Parameter")
|
||||
isTypeParameter: bool = False
|
||||
|
||||
|
||||
@deprecated(
|
||||
version="2.20", reason="Collections namespace changed, collectionType deprecated"
|
||||
)
|
||||
class Collection(
|
||||
Base, speckle_type="Speckle.Core.Models.Collection", detachable={"elements"}
|
||||
):
|
||||
name: Optional[str] = None
|
||||
collectionType: Optional[str] = None
|
||||
elements: Optional[List[Base]] = None
|
||||
|
||||
|
||||
class Collection( # noqa: F811
|
||||
Base,
|
||||
speckle_type="Speckle.Core.Models.Collections.Collection",
|
||||
detachable={"elements"},
|
||||
):
|
||||
name: str
|
||||
elements: List[Base]
|
||||
|
||||
def init(self, name: str, elements: Optional[List[Base]] = None):
|
||||
self.name = name
|
||||
self.elements = elements or []
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import pytest
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.core.api.inputs.project_inputs import ProjectCreateInput
|
||||
from specklepy.core.api.inputs.user_inputs import UserProjectsFilter, UserUpdateInput
|
||||
from specklepy.core.api.models import ResourceCollection, User
|
||||
|
||||
|
||||
@pytest.mark.run()
|
||||
class TestActiveUserResource:
|
||||
def test_active_user_get(self, client: SpeckleClient):
|
||||
res = client.active_user.get()
|
||||
|
||||
assert isinstance(res, User)
|
||||
|
||||
def test_active_user_update(self, client: SpeckleClient):
|
||||
NEW_NAME = "Ron"
|
||||
NEW_BIO = "Now I have a bio, isn't that nice!"
|
||||
NEW_COMPANY = "Limited Cooperation Organization Inc"
|
||||
|
||||
input = UserUpdateInput(name=NEW_NAME, bio=NEW_BIO, company=NEW_COMPANY)
|
||||
res = client.active_user.update(input=input)
|
||||
|
||||
assert isinstance(res, User)
|
||||
assert res.name == NEW_NAME
|
||||
assert res.bio == NEW_BIO
|
||||
assert res.company == NEW_COMPANY
|
||||
|
||||
def test_active_user_get_projects(self, client: SpeckleClient):
|
||||
existing = client.active_user.get_projects()
|
||||
|
||||
p1 = client.project.create(
|
||||
ProjectCreateInput(name="Project 1", description=None, visibility=None)
|
||||
)
|
||||
p2 = client.project.create(
|
||||
ProjectCreateInput(name="Project 2", description=None, visibility=None)
|
||||
)
|
||||
|
||||
res = client.active_user.get_projects()
|
||||
|
||||
assert isinstance(res, ResourceCollection)
|
||||
assert len(res.items) == len(existing.items) + 2
|
||||
assert any(project.id == p1.id for project in res.items)
|
||||
assert any(project.id == p2.id for project in res.items)
|
||||
|
||||
def test_active_user_get_projects_with_filter(self, client: SpeckleClient):
|
||||
# Since the client may be reused for other tests,
|
||||
# this test does rely on no other test creating a project with "Search for me" in its name
|
||||
p1 = client.project.create(
|
||||
ProjectCreateInput(name="Search for me!", description=None, visibility=None)
|
||||
)
|
||||
_ = client.project.create(
|
||||
ProjectCreateInput(name="But not me!", description=None, visibility=None)
|
||||
)
|
||||
filter = UserProjectsFilter(search="Search for me")
|
||||
|
||||
res = client.active_user.get_projects(filter=filter)
|
||||
|
||||
assert isinstance(res, ResourceCollection)
|
||||
assert len(res.items) == 1
|
||||
assert res.totalCount == 1
|
||||
assert res.items[0].id == p1.id
|
||||
@@ -0,0 +1,151 @@
|
||||
import pytest
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.core.api.inputs.model_inputs import (
|
||||
CreateModelInput,
|
||||
DeleteModelInput,
|
||||
UpdateModelInput,
|
||||
)
|
||||
from specklepy.core.api.inputs.project_inputs import (
|
||||
ProjectCreateInput,
|
||||
ProjectModelsFilter,
|
||||
)
|
||||
from specklepy.core.api.models.current import (
|
||||
Model,
|
||||
Project,
|
||||
ProjectWithModels,
|
||||
ResourceCollection,
|
||||
)
|
||||
from specklepy.logging.exceptions import GraphQLException
|
||||
|
||||
|
||||
@pytest.mark.run()
|
||||
class TestModelResource:
|
||||
@pytest.fixture()
|
||||
def test_project(self, client: SpeckleClient) -> Project:
|
||||
project = client.project.create(
|
||||
ProjectCreateInput(name="Test project", description="", visibility=None)
|
||||
)
|
||||
return project
|
||||
|
||||
@pytest.fixture()
|
||||
def test_model(self, client: SpeckleClient, test_project: Project) -> Model:
|
||||
model = client.model.create(
|
||||
CreateModelInput(
|
||||
name="Test Model", description="", projectId=test_project.id
|
||||
)
|
||||
)
|
||||
return model
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name, description",
|
||||
[
|
||||
("My Model", "My model description"),
|
||||
("my/nested/model", None),
|
||||
],
|
||||
)
|
||||
def test_model_create(
|
||||
self, client: SpeckleClient, test_project: Project, name: str, description: str
|
||||
):
|
||||
input = CreateModelInput(
|
||||
name=name, description=description, projectId=test_project.id
|
||||
)
|
||||
result = client.model.create(input)
|
||||
|
||||
assert isinstance(result, Model)
|
||||
assert result.name.lower() == name.lower()
|
||||
assert result.description == description
|
||||
|
||||
def test_model_get(
|
||||
self, client: SpeckleClient, test_model: Model, test_project: Project
|
||||
):
|
||||
result = client.model.get(test_model.id, test_project.id)
|
||||
|
||||
assert isinstance(result, Model)
|
||||
assert result.id == test_model.id
|
||||
assert result.name == test_model.name
|
||||
assert result.description == test_model.description
|
||||
assert result.createdAt == test_model.createdAt
|
||||
assert result.updatedAt == test_model.updatedAt
|
||||
|
||||
def test_models_get_with_filter(
|
||||
self, client: SpeckleClient, test_model: Model, test_project: Project
|
||||
):
|
||||
filter = ProjectModelsFilter(search=test_model.name)
|
||||
|
||||
result = client.model.get_models(test_project.id, models_filter=filter)
|
||||
|
||||
assert isinstance(result, ResourceCollection)
|
||||
assert len(result.items) == 1
|
||||
assert result.totalCount == 1
|
||||
assert result.items[0].id == test_model.id
|
||||
|
||||
def test_get_models(
|
||||
self, client: SpeckleClient, test_project: Project, test_model: Model
|
||||
):
|
||||
result = client.model.get_models(test_project.id)
|
||||
|
||||
assert isinstance(result, ResourceCollection)
|
||||
assert len(result.items) == 1
|
||||
assert result.totalCount == 1
|
||||
assert result.items[0].id == test_model.id
|
||||
|
||||
def test_project_get_models(
|
||||
self, client: SpeckleClient, test_project: Project, test_model: Model
|
||||
):
|
||||
result = client.project.get_with_models(test_project.id)
|
||||
|
||||
assert isinstance(result, ProjectWithModels)
|
||||
assert result.id == test_project.id
|
||||
assert isinstance(result.models, ResourceCollection)
|
||||
assert len(result.models.items) == 1
|
||||
assert result.models.totalCount == 1
|
||||
assert result.models.items[0].id == test_model.id
|
||||
|
||||
def test_project_get_models_with_filter(
|
||||
self, client: SpeckleClient, test_project: Project, test_model: Model
|
||||
):
|
||||
filter = ProjectModelsFilter(search=test_model.name)
|
||||
result = client.project.get_with_models(test_project.id, models_filter=filter)
|
||||
|
||||
assert isinstance(result, ProjectWithModels)
|
||||
assert result.id == test_project.id
|
||||
assert isinstance(result.models, ResourceCollection)
|
||||
assert len(result.models.items) == 1
|
||||
assert result.models.totalCount == 1
|
||||
assert result.models.items[0].id == test_model.id
|
||||
|
||||
def test_model_update(
|
||||
self, client: SpeckleClient, test_model: Model, test_project: Project
|
||||
):
|
||||
new_name = "MY new name"
|
||||
new_description = "MY new desc"
|
||||
|
||||
update_data = UpdateModelInput(
|
||||
id=test_model.id,
|
||||
name=new_name,
|
||||
description=new_description,
|
||||
projectId=test_project.id,
|
||||
)
|
||||
|
||||
updated_model = client.model.update(update_data)
|
||||
|
||||
assert isinstance(updated_model, Model)
|
||||
assert updated_model.id == test_model.id
|
||||
assert updated_model.name.lower() == new_name.lower()
|
||||
assert updated_model.description == new_description
|
||||
assert updated_model.updatedAt >= test_model.updatedAt
|
||||
|
||||
def test_model_delete(
|
||||
self, client: SpeckleClient, test_model: Model, test_project: Project
|
||||
):
|
||||
delete_data = DeleteModelInput(id=test_model.id, projectId=test_project.id)
|
||||
|
||||
response = client.model.delete(delete_data)
|
||||
assert response is True
|
||||
|
||||
with pytest.raises(GraphQLException):
|
||||
client.model.get(test_model.id, test_project.id)
|
||||
|
||||
with pytest.raises(GraphQLException):
|
||||
client.model.delete(delete_data)
|
||||
@@ -0,0 +1,32 @@
|
||||
import pytest
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.core.api.models import User
|
||||
|
||||
|
||||
@pytest.mark.run()
|
||||
class TestOtherUserResource:
|
||||
@pytest.fixture(scope="class")
|
||||
def test_data(self, second_client: SpeckleClient) -> User:
|
||||
user_info = second_client.active_user.get()
|
||||
assert user_info
|
||||
return user_info
|
||||
|
||||
def test_other_user_get(self, client: SpeckleClient, test_data: User):
|
||||
res = client.other_user.get(test_data.id)
|
||||
assert res is not None
|
||||
assert res.name == test_data.name
|
||||
|
||||
def test_other_user_get_non_existent_user(self, client: SpeckleClient):
|
||||
result = client.other_user.get("AnIdThatDoesntExist")
|
||||
assert result is None
|
||||
|
||||
def test_user_search(self, client: SpeckleClient, test_data: User):
|
||||
assert test_data.email
|
||||
res = client.other_user.user_search(test_data.email, limit=25)
|
||||
assert len(res.items) == 1
|
||||
assert res.items[0].id == test_data.id
|
||||
|
||||
def test_user_search_non_existent_user(self, client: SpeckleClient):
|
||||
res = client.other_user.user_search("idontexist@example.com", limit=25)
|
||||
assert len(res.items) == 0
|
||||
@@ -0,0 +1,176 @@
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.core.api.inputs.project_inputs import (
|
||||
ProjectCreateInput,
|
||||
ProjectInviteCreateInput,
|
||||
ProjectInviteUseInput,
|
||||
ProjectUpdateRoleInput,
|
||||
)
|
||||
from specklepy.core.api.models import (
|
||||
LimitedUser,
|
||||
PendingStreamCollaborator,
|
||||
Project,
|
||||
ProjectWithTeam,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.run()
|
||||
class TestProjectInviteResource:
|
||||
@pytest.fixture
|
||||
def project(self, client: SpeckleClient):
|
||||
return client.project.create(
|
||||
ProjectCreateInput(name="test", description=None, visibility=None)
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def created_invite(
|
||||
self, client: SpeckleClient, second_client: SpeckleClient, project: Project
|
||||
):
|
||||
input = ProjectInviteCreateInput(
|
||||
email=second_client.account.userInfo.email,
|
||||
role=None,
|
||||
serverRole=None,
|
||||
userId=None,
|
||||
)
|
||||
res = client.project_invite.create(project.id, input)
|
||||
invites = second_client.active_user.get_project_invites()
|
||||
return next(i for i in invites if i.projectId == res.id)
|
||||
|
||||
def test_project_invite_create_by_email(
|
||||
self, client: SpeckleClient, second_client: SpeckleClient, project: Project
|
||||
):
|
||||
input = ProjectInviteCreateInput(
|
||||
email=second_client.account.userInfo.email,
|
||||
role=None,
|
||||
serverRole=None,
|
||||
userId=None,
|
||||
)
|
||||
res = client.project_invite.create(project.id, input)
|
||||
|
||||
invites = second_client.active_user.get_project_invites()
|
||||
invite = next(i for i in invites if i.projectId == res.id)
|
||||
|
||||
assert isinstance(res, ProjectWithTeam)
|
||||
assert res.id == project.id
|
||||
assert len(res.invitedTeam) == 1
|
||||
|
||||
assert isinstance(invite.user, LimitedUser)
|
||||
assert invite.user.id == second_client.account.userInfo.id
|
||||
assert invite.token
|
||||
|
||||
def test_project_invite_create_by_user_id(
|
||||
self, client: SpeckleClient, second_client: SpeckleClient, project: Project
|
||||
):
|
||||
input = ProjectInviteCreateInput(
|
||||
email=None,
|
||||
role=None,
|
||||
serverRole=None,
|
||||
userId=second_client.account.userInfo.id,
|
||||
)
|
||||
res = client.project_invite.create(project.id, input)
|
||||
|
||||
assert isinstance(res, ProjectWithTeam)
|
||||
assert res.id == project.id
|
||||
assert len(res.invitedTeam) == 1
|
||||
invited_team_member = res.invitedTeam[0].user
|
||||
assert isinstance(invited_team_member, LimitedUser)
|
||||
assert invited_team_member.id == second_client.account.userInfo.id
|
||||
|
||||
def test_project_invite_get(
|
||||
self,
|
||||
second_client: SpeckleClient,
|
||||
project: Project,
|
||||
created_invite: PendingStreamCollaborator,
|
||||
):
|
||||
collaborator = second_client.project_invite.get(
|
||||
project.id, created_invite.token
|
||||
)
|
||||
assert isinstance(collaborator, PendingStreamCollaborator)
|
||||
assert collaborator.inviteId == created_invite.inviteId
|
||||
|
||||
assert isinstance(collaborator.user, LimitedUser)
|
||||
assert isinstance(created_invite.user, LimitedUser)
|
||||
|
||||
assert collaborator.user.id == created_invite.user.id
|
||||
|
||||
def test_project_invite_get_non_existing(
|
||||
self, second_client: SpeckleClient, project: Project
|
||||
):
|
||||
collaborator = second_client.project_invite.get(
|
||||
project.id, "this is not a real token"
|
||||
)
|
||||
|
||||
assert collaborator is None
|
||||
|
||||
def test_project_invite_use_member_added(
|
||||
self,
|
||||
client: SpeckleClient,
|
||||
second_client: SpeckleClient,
|
||||
project: Project,
|
||||
created_invite: PendingStreamCollaborator,
|
||||
):
|
||||
assert created_invite.token
|
||||
|
||||
input = ProjectInviteUseInput(
|
||||
accept=True, projectId=created_invite.projectId, token=created_invite.token
|
||||
)
|
||||
res = second_client.project_invite.use(input)
|
||||
|
||||
assert res is True
|
||||
|
||||
project = client.project.get_with_team(project.id)
|
||||
assert isinstance(project, ProjectWithTeam)
|
||||
|
||||
team_members = [c.user.id for c in project.team]
|
||||
expected_team_members = [
|
||||
client.account.userInfo.id,
|
||||
second_client.account.userInfo.id,
|
||||
]
|
||||
|
||||
assert set(team_members) == set(expected_team_members)
|
||||
|
||||
def test_project_invite_cancel_member_not_added(
|
||||
self, client: SpeckleClient, created_invite: PendingStreamCollaborator
|
||||
):
|
||||
res = client.project_invite.cancel(
|
||||
created_invite.projectId, created_invite.inviteId
|
||||
)
|
||||
|
||||
assert isinstance(res, ProjectWithTeam)
|
||||
assert len(res.invitedTeam) == 0
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"new_role", ["stream:owner", "stream:contributor", "stream:reviewer", None]
|
||||
)
|
||||
def test_project_update_role(
|
||||
self,
|
||||
client: SpeckleClient,
|
||||
second_client: SpeckleClient,
|
||||
project: Project,
|
||||
new_role: Optional[str],
|
||||
created_invite: PendingStreamCollaborator,
|
||||
):
|
||||
assert created_invite.token
|
||||
|
||||
input = ProjectInviteUseInput(
|
||||
accept=True, projectId=created_invite.projectId, token=created_invite.token
|
||||
)
|
||||
res = second_client.project_invite.use(input)
|
||||
|
||||
invitee_id = second_client.account.userInfo.id
|
||||
assert invitee_id
|
||||
input = ProjectUpdateRoleInput(
|
||||
userId=invitee_id,
|
||||
projectId=project.id,
|
||||
role=new_role,
|
||||
)
|
||||
res = client.project.update_role(input)
|
||||
|
||||
assert isinstance(res, ProjectWithTeam)
|
||||
final_project = second_client.project.get(project.id)
|
||||
|
||||
assert isinstance(res, Project)
|
||||
assert final_project.role == new_role
|
||||
@@ -0,0 +1,93 @@
|
||||
import pytest
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.core.api.enums import ProjectVisibility
|
||||
from specklepy.core.api.inputs.project_inputs import (
|
||||
ProjectCreateInput,
|
||||
ProjectUpdateInput,
|
||||
)
|
||||
from specklepy.core.api.models import Project
|
||||
from specklepy.logging.exceptions import GraphQLException
|
||||
|
||||
|
||||
@pytest.mark.run()
|
||||
class TestProjectResource:
|
||||
@pytest.fixture()
|
||||
def test_project(self, client: SpeckleClient) -> Project:
|
||||
project = client.project.create(
|
||||
ProjectCreateInput(
|
||||
name="test project123",
|
||||
description="desc",
|
||||
visibility=ProjectVisibility.PRIVATE,
|
||||
)
|
||||
)
|
||||
return project
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name, description, visibility",
|
||||
[
|
||||
("Very private project", "My secret project", ProjectVisibility.PRIVATE),
|
||||
("Very public project", None, ProjectVisibility.PUBLIC),
|
||||
],
|
||||
)
|
||||
def test_project_create(
|
||||
self,
|
||||
client: SpeckleClient,
|
||||
name: str,
|
||||
description: str,
|
||||
visibility: ProjectVisibility,
|
||||
):
|
||||
input = ProjectCreateInput(
|
||||
name=name,
|
||||
description=description,
|
||||
visibility=visibility,
|
||||
)
|
||||
result = client.project.create(input)
|
||||
|
||||
assert isinstance(result, Project)
|
||||
assert result.id is not None
|
||||
assert result.name == name
|
||||
assert result.description == (description or "")
|
||||
assert result.visibility == visibility
|
||||
|
||||
def test_project_get(self, client: SpeckleClient, test_project: Project):
|
||||
result = client.project.get(test_project.id)
|
||||
|
||||
assert isinstance(result, Project)
|
||||
assert result.id == test_project.id
|
||||
assert result.name == test_project.name
|
||||
assert result.description == test_project.description
|
||||
assert result.visibility == test_project.visibility
|
||||
assert result.createdAt == test_project.createdAt
|
||||
|
||||
def test_project_update(self, client: SpeckleClient, test_project: Project):
|
||||
new_name = "MY new name"
|
||||
new_description = "MY new desc"
|
||||
new_visibility = ProjectVisibility.PUBLIC
|
||||
|
||||
update_data = ProjectUpdateInput(
|
||||
id=test_project.id,
|
||||
name=new_name,
|
||||
description=new_description,
|
||||
visibility=new_visibility,
|
||||
)
|
||||
|
||||
updated_project = client.project.update(update_data)
|
||||
|
||||
assert isinstance(updated_project, Project)
|
||||
assert updated_project.id == test_project.id
|
||||
assert updated_project.name == new_name
|
||||
assert updated_project.description == new_description
|
||||
assert updated_project.visibility == new_visibility
|
||||
|
||||
def test_project_delete(self, client: SpeckleClient):
|
||||
"""Test deleting a project."""
|
||||
project_to_delete = client.project.create(
|
||||
ProjectCreateInput(name="Delete me", description=None, visibility=None)
|
||||
)
|
||||
|
||||
response = client.project.delete(project_to_delete.id)
|
||||
assert response is True
|
||||
|
||||
with pytest.raises(GraphQLException):
|
||||
client.project.get(project_to_delete.id)
|
||||
@@ -0,0 +1,185 @@
|
||||
import asyncio
|
||||
from typing import Dict, Optional
|
||||
|
||||
import pytest
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.core.api.enums import (
|
||||
ProjectModelsUpdatedMessageType,
|
||||
ProjectUpdatedMessageType,
|
||||
ProjectVersionsUpdatedMessageType,
|
||||
UserProjectsUpdatedMessageType,
|
||||
)
|
||||
from specklepy.core.api.inputs.model_inputs import CreateModelInput
|
||||
from specklepy.core.api.inputs.project_inputs import (
|
||||
ProjectCreateInput,
|
||||
ProjectUpdateInput,
|
||||
)
|
||||
from specklepy.core.api.models import (
|
||||
Model,
|
||||
Project,
|
||||
ProjectModelsUpdatedMessage,
|
||||
ProjectUpdatedMessage,
|
||||
ProjectVersionsUpdatedMessage,
|
||||
UserProjectsUpdatedMessage,
|
||||
Version,
|
||||
)
|
||||
from tests.integration.conftest import create_client, create_version
|
||||
|
||||
WAIT_PERIOD = 0.4 # time in seconds
|
||||
|
||||
|
||||
@pytest.mark.run()
|
||||
class TestSubscriptionResource:
|
||||
@pytest.fixture
|
||||
def subscription_client(
|
||||
self, host: str, user_dict: Dict[str, str]
|
||||
) -> SpeckleClient:
|
||||
return create_client(host, user_dict["token"])
|
||||
|
||||
@pytest.fixture
|
||||
def test_project(self, subscription_client: SpeckleClient) -> Project:
|
||||
project = subscription_client.project.create(
|
||||
ProjectCreateInput(name="Test project", description="", visibility=None)
|
||||
)
|
||||
return project
|
||||
|
||||
@pytest.fixture
|
||||
def test_model(
|
||||
self, subscription_client: SpeckleClient, test_project: Project
|
||||
) -> Model:
|
||||
model1 = subscription_client.model.create(
|
||||
CreateModelInput(
|
||||
name="Test Model 1", description="", projectId=test_project.id
|
||||
)
|
||||
)
|
||||
return model1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_projects_updated(
|
||||
self,
|
||||
subscription_client: SpeckleClient,
|
||||
) -> None:
|
||||
message: Optional[UserProjectsUpdatedMessage] = None
|
||||
|
||||
task = None
|
||||
|
||||
def callback(d: UserProjectsUpdatedMessage):
|
||||
nonlocal message
|
||||
message = d
|
||||
|
||||
task = asyncio.create_task(
|
||||
subscription_client.subscription.user_projects_updated(callback)
|
||||
)
|
||||
|
||||
await asyncio.sleep(WAIT_PERIOD) # Give time to subscription to be setup
|
||||
|
||||
input = ProjectCreateInput(name=None, description=None, visibility=None)
|
||||
created = subscription_client.project.create(input)
|
||||
|
||||
await asyncio.sleep(WAIT_PERIOD) # Give time for subscription to be triggered
|
||||
|
||||
assert isinstance(message, UserProjectsUpdatedMessage)
|
||||
assert message.id == created.id
|
||||
assert message.type == UserProjectsUpdatedMessageType.ADDED
|
||||
assert isinstance(message.project, Project)
|
||||
task.cancel()
|
||||
await task
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_project_models_updated(
|
||||
self, subscription_client: SpeckleClient, test_project: Project
|
||||
) -> None:
|
||||
message: Optional[ProjectModelsUpdatedMessage] = None
|
||||
|
||||
task = None
|
||||
|
||||
def callback(d: ProjectModelsUpdatedMessage):
|
||||
nonlocal message
|
||||
message = d
|
||||
|
||||
task = asyncio.create_task(
|
||||
subscription_client.subscription.project_models_updated(
|
||||
callback, test_project.id
|
||||
)
|
||||
)
|
||||
|
||||
await asyncio.sleep(WAIT_PERIOD) # Give time to subscription to be setup
|
||||
|
||||
input = CreateModelInput(
|
||||
name="my model", description="myDescription", projectId=test_project.id
|
||||
)
|
||||
created = subscription_client.model.create(input)
|
||||
|
||||
await asyncio.sleep(WAIT_PERIOD) # Give time for subscription to be triggered
|
||||
|
||||
assert isinstance(message, ProjectModelsUpdatedMessage)
|
||||
assert message.id == created.id
|
||||
assert message.type == ProjectModelsUpdatedMessageType.CREATED
|
||||
assert isinstance(message.model, Model)
|
||||
task.cancel()
|
||||
await task
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_project_updated(
|
||||
self, subscription_client: SpeckleClient, test_project: Project
|
||||
) -> None:
|
||||
message: Optional[ProjectUpdatedMessage] = None
|
||||
|
||||
task = None
|
||||
|
||||
def callback(d: ProjectUpdatedMessage):
|
||||
nonlocal message
|
||||
message = d
|
||||
|
||||
task = asyncio.create_task(
|
||||
subscription_client.subscription.project_updated(callback, test_project.id)
|
||||
)
|
||||
|
||||
await asyncio.sleep(WAIT_PERIOD) # Give time to subscription to be setup
|
||||
|
||||
input = ProjectUpdateInput(id=test_project.id, name="This is my new name")
|
||||
created = subscription_client.project.update(input)
|
||||
|
||||
await asyncio.sleep(WAIT_PERIOD) # Give time for subscription to be triggered
|
||||
|
||||
assert isinstance(message, ProjectUpdatedMessage)
|
||||
assert message.id == created.id
|
||||
assert message.type == ProjectUpdatedMessageType.UPDATED
|
||||
assert isinstance(message.project, Project)
|
||||
task.cancel()
|
||||
await task
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_project_versions_updated(
|
||||
self,
|
||||
subscription_client: SpeckleClient,
|
||||
test_project: Project,
|
||||
test_model: Model,
|
||||
) -> None:
|
||||
message: Optional[ProjectVersionsUpdatedMessage] = None
|
||||
|
||||
task = None
|
||||
|
||||
def callback(d: ProjectVersionsUpdatedMessage):
|
||||
nonlocal message
|
||||
message = d
|
||||
|
||||
task = asyncio.create_task(
|
||||
subscription_client.subscription.project_versions_updated(
|
||||
callback, test_project.id
|
||||
)
|
||||
)
|
||||
|
||||
await asyncio.sleep(WAIT_PERIOD) # Give time to subscription to be setup
|
||||
|
||||
created = create_version(subscription_client, test_project.id, test_model.id)
|
||||
|
||||
await asyncio.sleep(WAIT_PERIOD) # Give time for subscription to be triggered
|
||||
|
||||
assert isinstance(message, ProjectVersionsUpdatedMessage)
|
||||
assert message.id == created.id
|
||||
assert message.type == ProjectVersionsUpdatedMessageType.CREATED
|
||||
assert isinstance(message.version, Version)
|
||||
task.cancel()
|
||||
await task
|
||||
@@ -0,0 +1,198 @@
|
||||
import pytest
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.core.api.inputs.model_inputs import CreateModelInput, ModelVersionsFilter
|
||||
from specklepy.core.api.inputs.project_inputs import ProjectCreateInput
|
||||
from specklepy.core.api.inputs.version_inputs import (
|
||||
DeleteVersionsInput,
|
||||
MarkReceivedVersionInput,
|
||||
MoveVersionsInput,
|
||||
UpdateVersionInput,
|
||||
)
|
||||
from specklepy.core.api.models import (
|
||||
Model,
|
||||
ModelWithVersions,
|
||||
Project,
|
||||
ResourceCollection,
|
||||
Version,
|
||||
)
|
||||
from specklepy.logging.exceptions import GraphQLException
|
||||
from tests.integration.conftest import create_version
|
||||
|
||||
|
||||
@pytest.mark.run()
|
||||
class TestVersionResource:
|
||||
@pytest.fixture
|
||||
def test_project(self, client: SpeckleClient) -> Project:
|
||||
project = client.project.create(
|
||||
ProjectCreateInput(name="Test project", description="", visibility=None)
|
||||
)
|
||||
return project
|
||||
|
||||
@pytest.fixture
|
||||
def test_model_1(self, client: SpeckleClient, test_project: Project) -> Model:
|
||||
model1 = client.model.create(
|
||||
CreateModelInput(
|
||||
name="Test Model 1", description="", projectId=test_project.id
|
||||
)
|
||||
)
|
||||
return model1
|
||||
|
||||
@pytest.fixture
|
||||
def test_model_2(self, client: SpeckleClient, test_project: Project) -> Model:
|
||||
model2 = client.model.create(
|
||||
CreateModelInput(
|
||||
name="Test Model 2", description="", projectId=test_project.id
|
||||
)
|
||||
)
|
||||
return model2
|
||||
|
||||
@pytest.fixture
|
||||
def test_version(
|
||||
self, client: SpeckleClient, test_project: Project, test_model_1: Model
|
||||
) -> Version:
|
||||
return create_version(client, test_project.id, test_model_1.id)
|
||||
|
||||
def test_version_get(
|
||||
self, client: SpeckleClient, test_version: Version, test_project: Project
|
||||
):
|
||||
result = client.version.get(test_version.id, test_project.id)
|
||||
|
||||
assert isinstance(result, Version)
|
||||
assert result.id == test_version.id
|
||||
assert result.message == test_version.message
|
||||
|
||||
def test_versions_get(
|
||||
self,
|
||||
client: SpeckleClient,
|
||||
test_model_1: Model,
|
||||
test_project: Project,
|
||||
test_version: Version,
|
||||
):
|
||||
result = client.version.get_versions(test_model_1.id, test_project.id)
|
||||
|
||||
assert isinstance(result, ResourceCollection)
|
||||
assert len(result.items) == 1
|
||||
assert result.totalCount == 1
|
||||
assert result.items[0].id == test_version.id
|
||||
|
||||
def test_versions_get_with_filter(
|
||||
self,
|
||||
client: SpeckleClient,
|
||||
test_model_1: Model,
|
||||
test_project: Project,
|
||||
test_version: Version,
|
||||
):
|
||||
filter = ModelVersionsFilter(
|
||||
priorityIds=[test_version.id], priorityIdsOnly=True
|
||||
)
|
||||
|
||||
result = client.version.get_versions(
|
||||
test_model_1.id, test_project.id, filter=filter
|
||||
)
|
||||
|
||||
assert isinstance(result, ResourceCollection)
|
||||
assert len(result.items) == 1
|
||||
assert result.totalCount == 1
|
||||
assert result.items[0].id == test_version.id
|
||||
|
||||
def test_version_received(
|
||||
self, client: SpeckleClient, test_version: Version, test_project: Project
|
||||
):
|
||||
input = MarkReceivedVersionInput(
|
||||
versionId=test_version.id,
|
||||
projectId=test_project.id,
|
||||
sourceApplication="Integration test",
|
||||
)
|
||||
result = client.version.received(input)
|
||||
|
||||
assert result is True
|
||||
|
||||
def test_model_get_with_versions(
|
||||
self,
|
||||
client: SpeckleClient,
|
||||
test_model_1: Model,
|
||||
test_project: Project,
|
||||
test_version: Version,
|
||||
):
|
||||
result = client.model.get_with_versions(test_model_1.id, test_project.id)
|
||||
|
||||
assert isinstance(result, ModelWithVersions)
|
||||
assert result.id == test_model_1.id
|
||||
assert len(result.versions.items) == 1
|
||||
assert result.versions.totalCount == 1
|
||||
assert result.versions.items[0].id == test_version.id
|
||||
|
||||
def test_model_get_with_versions_with_filter(
|
||||
self,
|
||||
client: SpeckleClient,
|
||||
test_model_1: Model,
|
||||
test_project: Project,
|
||||
test_version: Version,
|
||||
):
|
||||
filter = ModelVersionsFilter(
|
||||
priorityIds=[test_version.id], priorityIdsOnly=True
|
||||
)
|
||||
|
||||
result = client.model.get_with_versions(
|
||||
test_model_1.id, test_project.id, versions_filter=filter
|
||||
)
|
||||
|
||||
assert isinstance(result, ModelWithVersions)
|
||||
assert len(result.versions.items) == 1
|
||||
assert result.versions.totalCount == 1
|
||||
assert isinstance(result.versions, ResourceCollection)
|
||||
assert result.versions.items[0].id == test_version.id
|
||||
|
||||
def test_version_update(
|
||||
self, client: SpeckleClient, test_version: Version, test_project: Project
|
||||
):
|
||||
new_message = "MY new version message"
|
||||
input = UpdateVersionInput(
|
||||
versionId=test_version.id, projectId=test_project.id, message=new_message
|
||||
)
|
||||
updated_version = client.version.update(input)
|
||||
|
||||
assert isinstance(updated_version, Version)
|
||||
assert updated_version.id == test_version.id
|
||||
assert updated_version.message == new_message
|
||||
assert updated_version.previewUrl == test_version.previewUrl
|
||||
|
||||
def test_version_move_to_model(
|
||||
self,
|
||||
client: SpeckleClient,
|
||||
test_project: Project,
|
||||
test_version: Version,
|
||||
test_model_2: Model,
|
||||
):
|
||||
input = MoveVersionsInput(
|
||||
targetModelName=test_model_2.name,
|
||||
versionIds=[test_version.id],
|
||||
projectId=test_project.id,
|
||||
)
|
||||
moved_model_id = client.version.move_to_model(input)
|
||||
|
||||
assert isinstance(moved_model_id, str)
|
||||
assert moved_model_id == test_model_2.id
|
||||
moved_version = client.version.get(test_version.id, test_project.id)
|
||||
|
||||
assert isinstance(moved_version, Version)
|
||||
assert moved_version.id == test_version.id
|
||||
assert moved_version.message == test_version.message
|
||||
assert moved_version.previewUrl == test_version.previewUrl
|
||||
|
||||
def test_version_delete(
|
||||
self, client: SpeckleClient, test_version: Version, test_project: Project
|
||||
):
|
||||
input = DeleteVersionsInput(
|
||||
versionIds=[test_version.id], projectId=test_project.id
|
||||
)
|
||||
|
||||
response = client.version.delete(input)
|
||||
assert response is True
|
||||
|
||||
with pytest.raises(GraphQLException):
|
||||
client.version.get(test_version.id, test_project.id)
|
||||
|
||||
with pytest.raises(GraphQLException):
|
||||
client.version.delete(input)
|
||||
+6
-7
@@ -2,7 +2,7 @@ import pytest
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.api.models import Activity, ActivityCollection, User
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.logging.exceptions import GraphQLException
|
||||
|
||||
|
||||
@pytest.mark.run(order=2)
|
||||
@@ -19,15 +19,14 @@ class TestUser:
|
||||
def test_user_update(self, client: SpeckleClient):
|
||||
bio = "i am a ghost in the machine"
|
||||
|
||||
failed_update = client.active_user.update()
|
||||
assert isinstance(failed_update, SpeckleException)
|
||||
with pytest.raises(GraphQLException):
|
||||
client.active_user.update(bio=None)
|
||||
|
||||
updated = client.active_user.update(bio=bio)
|
||||
|
||||
me = client.active_user.get()
|
||||
|
||||
assert updated is True
|
||||
assert me.bio == bio
|
||||
assert isinstance(updated, User)
|
||||
assert isinstance(updated, User)
|
||||
assert updated.bio == bio
|
||||
|
||||
def test_user_activity(self, client: SpeckleClient, second_user_dict):
|
||||
my_activity = client.active_user.activity(limit=10)
|
||||
+2
-2
@@ -13,7 +13,7 @@ class TestOtherUser:
|
||||
with pytest.raises(TypeError):
|
||||
client.other_user.get()
|
||||
|
||||
def test_user_search(self, client, second_user_dict):
|
||||
def test_user_search(self, client: SpeckleClient, second_user_dict):
|
||||
search_results = client.other_user.search(
|
||||
search_query=second_user_dict["name"][:5]
|
||||
)
|
||||
@@ -27,7 +27,7 @@ class TestOtherUser:
|
||||
second_user_dict["id"] = result_user.id
|
||||
assert getattr(result_user, "email", None) is None
|
||||
|
||||
def test_user_get(self, client, second_user_dict):
|
||||
def test_user_get(self, client: SpeckleClient, second_user_dict):
|
||||
fetched_user = client.other_user.get(id=second_user_dict["id"])
|
||||
|
||||
assert isinstance(fetched_user, LimitedUser)
|
||||
@@ -1,26 +1,30 @@
|
||||
import random
|
||||
import uuid
|
||||
from typing import Dict
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.api.models import Stream
|
||||
from specklepy.core.api import operations
|
||||
from specklepy.core.api.inputs.version_inputs import CreateVersionInput
|
||||
from specklepy.core.api.models import Stream, Version
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.objects.fakemesh import FakeDirection, FakeMesh
|
||||
from specklepy.objects.geometry import Point
|
||||
from specklepy.transports.server.server import ServerTransport
|
||||
|
||||
metrics.disable()
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def host():
|
||||
def host() -> str:
|
||||
return "localhost:3000"
|
||||
|
||||
|
||||
def seed_user(host):
|
||||
def seed_user(host: str) -> Dict[str, str]:
|
||||
seed = uuid.uuid4().hex
|
||||
user_dict = {
|
||||
"email": f"{seed[0:7]}@example.org",
|
||||
@@ -57,32 +61,53 @@ def seed_user(host):
|
||||
return user_dict
|
||||
|
||||
|
||||
def create_version(client: SpeckleClient, project_id: str, model_id: str) -> Version:
|
||||
remote = ServerTransport(project_id, client)
|
||||
objectId = operations.send(
|
||||
Base(applicationId="ASDF"), [remote], use_default_cache=False
|
||||
)
|
||||
input = CreateVersionInput(
|
||||
objectId=objectId, modelId=model_id, projectId=project_id
|
||||
)
|
||||
version_id = client.version.create(input)
|
||||
return client.version.get(version_id, project_id)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def user_dict(host):
|
||||
def user_dict(host: str) -> Dict[str, str]:
|
||||
return seed_user(host)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def second_user_dict(host):
|
||||
def second_user_dict(host: str) -> Dict[str, str]:
|
||||
return seed_user(host)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def client(host, user_dict):
|
||||
def create_client(host: str, token: str) -> SpeckleClient:
|
||||
client = SpeckleClient(host=host, use_ssl=False)
|
||||
client.authenticate_with_token(user_dict["token"])
|
||||
client.authenticate_with_token(token)
|
||||
user = client.active_user.get()
|
||||
assert user
|
||||
client.account.userInfo.id = user.id
|
||||
client.account.userInfo.email = user.email
|
||||
client.account.userInfo.name = user.name
|
||||
client.account.userInfo.company = user.company
|
||||
client.account.userInfo.avatar = user.avatar
|
||||
return client
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def second_client(host, second_user_dict):
|
||||
client = SpeckleClient(host=host, use_ssl=False)
|
||||
client.authenticate_with_token(second_user_dict["token"])
|
||||
return client
|
||||
def client(host: str, user_dict: Dict[str, str]) -> SpeckleClient:
|
||||
return create_client(host, user_dict["token"])
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def sample_stream(client):
|
||||
def second_client(host: str, second_user_dict: Dict[str, str]):
|
||||
return create_client(host, second_user_dict["token"])
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def sample_stream(client: SpeckleClient) -> Stream:
|
||||
stream = Stream(
|
||||
name="a sample stream for testing",
|
||||
description="a stream created for testing",
|
||||
@@ -93,7 +118,7 @@ def sample_stream(client):
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def mesh():
|
||||
def mesh() -> FakeMesh:
|
||||
mesh = FakeMesh()
|
||||
mesh.name = "my_mesh"
|
||||
mesh.vertices = [random.uniform(0, 10) for _ in range(1, 210)]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Run integration tests with a speckle server."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
@@ -72,7 +72,7 @@ def line(point, interval):
|
||||
start=point,
|
||||
end=point,
|
||||
domain=interval,
|
||||
units="none"
|
||||
units="none",
|
||||
# These attributes are not handled in C#
|
||||
# bbox=None,
|
||||
# length=None
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Provides uniform and consistent path helpers for `specklepy`
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from importlib import import_module, invalidate_caches
|
||||
|
||||
Reference in New Issue
Block a user