pyspiral 0.8.9__cp311-abi3-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyspiral-0.8.9.dist-info/METADATA +53 -0
- pyspiral-0.8.9.dist-info/RECORD +114 -0
- pyspiral-0.8.9.dist-info/WHEEL +4 -0
- pyspiral-0.8.9.dist-info/entry_points.txt +3 -0
- spiral/__init__.py +55 -0
- spiral/_lib.abi3.so +0 -0
- spiral/adbc.py +411 -0
- spiral/api/__init__.py +78 -0
- spiral/api/admin.py +15 -0
- spiral/api/client.py +165 -0
- spiral/api/filesystems.py +152 -0
- spiral/api/key_space_indexes.py +23 -0
- spiral/api/organizations.py +78 -0
- spiral/api/projects.py +219 -0
- spiral/api/telemetry.py +19 -0
- spiral/api/text_indexes.py +56 -0
- spiral/api/types.py +23 -0
- spiral/api/workers.py +40 -0
- spiral/api/workloads.py +52 -0
- spiral/arrow_.py +202 -0
- spiral/cli/__init__.py +89 -0
- spiral/cli/__main__.py +4 -0
- spiral/cli/admin.py +33 -0
- spiral/cli/app.py +108 -0
- spiral/cli/console.py +95 -0
- spiral/cli/fs.py +109 -0
- spiral/cli/iceberg.py +97 -0
- spiral/cli/key_spaces.py +103 -0
- spiral/cli/login.py +25 -0
- spiral/cli/orgs.py +81 -0
- spiral/cli/printer.py +53 -0
- spiral/cli/projects.py +148 -0
- spiral/cli/state.py +7 -0
- spiral/cli/tables.py +225 -0
- spiral/cli/telemetry.py +17 -0
- spiral/cli/text.py +115 -0
- spiral/cli/types.py +50 -0
- spiral/cli/workloads.py +86 -0
- spiral/client.py +279 -0
- spiral/core/__init__.pyi +0 -0
- spiral/core/_tools/__init__.pyi +5 -0
- spiral/core/authn/__init__.pyi +21 -0
- spiral/core/client/__init__.pyi +270 -0
- spiral/core/config/__init__.pyi +35 -0
- spiral/core/expr/__init__.pyi +15 -0
- spiral/core/expr/images/__init__.pyi +3 -0
- spiral/core/expr/list_/__init__.pyi +4 -0
- spiral/core/expr/pushdown/__init__.pyi +3 -0
- spiral/core/expr/refs/__init__.pyi +4 -0
- spiral/core/expr/s3/__init__.pyi +3 -0
- spiral/core/expr/str_/__init__.pyi +3 -0
- spiral/core/expr/struct_/__init__.pyi +6 -0
- spiral/core/expr/text/__init__.pyi +5 -0
- spiral/core/expr/udf/__init__.pyi +14 -0
- spiral/core/expr/video/__init__.pyi +3 -0
- spiral/core/table/__init__.pyi +142 -0
- spiral/core/table/manifests/__init__.pyi +35 -0
- spiral/core/table/metastore/__init__.pyi +58 -0
- spiral/core/table/spec/__init__.pyi +214 -0
- spiral/dataloader.py +310 -0
- spiral/dataset.py +264 -0
- spiral/datetime_.py +27 -0
- spiral/debug/__init__.py +0 -0
- spiral/debug/manifests.py +103 -0
- spiral/debug/metrics.py +56 -0
- spiral/debug/scan.py +266 -0
- spiral/demo.py +100 -0
- spiral/enrichment.py +290 -0
- spiral/expressions/__init__.py +274 -0
- spiral/expressions/base.py +186 -0
- spiral/expressions/file.py +17 -0
- spiral/expressions/http.py +17 -0
- spiral/expressions/list_.py +77 -0
- spiral/expressions/pushdown.py +12 -0
- spiral/expressions/s3.py +16 -0
- spiral/expressions/str_.py +39 -0
- spiral/expressions/struct.py +59 -0
- spiral/expressions/text.py +62 -0
- spiral/expressions/tiff.py +225 -0
- spiral/expressions/udf.py +66 -0
- spiral/grpc_.py +32 -0
- spiral/iceberg.py +31 -0
- spiral/iterable_dataset.py +106 -0
- spiral/key_space_index.py +44 -0
- spiral/project.py +247 -0
- spiral/protogen/_/__init__.py +0 -0
- spiral/protogen/_/arrow/__init__.py +0 -0
- spiral/protogen/_/arrow/flight/__init__.py +0 -0
- spiral/protogen/_/arrow/flight/protocol/__init__.py +0 -0
- spiral/protogen/_/arrow/flight/protocol/sql/__init__.py +2548 -0
- spiral/protogen/_/google/__init__.py +0 -0
- spiral/protogen/_/google/protobuf/__init__.py +2310 -0
- spiral/protogen/_/message_pool.py +3 -0
- spiral/protogen/_/py.typed +0 -0
- spiral/protogen/_/scandal/__init__.py +190 -0
- spiral/protogen/_/spfs/__init__.py +72 -0
- spiral/protogen/_/spql/__init__.py +61 -0
- spiral/protogen/_/substrait/__init__.py +6196 -0
- spiral/protogen/_/substrait/extensions/__init__.py +169 -0
- spiral/protogen/__init__.py +0 -0
- spiral/protogen/util.py +41 -0
- spiral/py.typed +0 -0
- spiral/scan.py +383 -0
- spiral/server.py +37 -0
- spiral/settings.py +36 -0
- spiral/snapshot.py +61 -0
- spiral/streaming_/__init__.py +3 -0
- spiral/streaming_/reader.py +133 -0
- spiral/streaming_/stream.py +156 -0
- spiral/substrait_.py +274 -0
- spiral/table.py +216 -0
- spiral/text_index.py +17 -0
- spiral/transaction.py +156 -0
- spiral/types_.py +6 -0
spiral/api/__init__.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from .client import _Client
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from spiral.core.authn import Authn
|
|
10
|
+
|
|
11
|
+
from .admin import AdminService
|
|
12
|
+
from .filesystems import FileSystemsService
|
|
13
|
+
from .key_space_indexes import KeySpaceIndexesService
|
|
14
|
+
from .organizations import OrganizationsService
|
|
15
|
+
from .projects import ProjectsService
|
|
16
|
+
from .telemetry import TelemetryService
|
|
17
|
+
from .text_indexes import TextIndexesService
|
|
18
|
+
from .workloads import WorkloadsService
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SpiralAPI:
|
|
22
|
+
def __init__(self, base_url: str, authn: "Authn"):
|
|
23
|
+
self.base_url = base_url
|
|
24
|
+
self.client = _Client(
|
|
25
|
+
httpx.Client(
|
|
26
|
+
base_url=self.base_url,
|
|
27
|
+
timeout=None if "PYTEST_VERSION" in os.environ else 60,
|
|
28
|
+
),
|
|
29
|
+
authn,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def _admin(self) -> "AdminService":
|
|
34
|
+
from .admin import AdminService
|
|
35
|
+
|
|
36
|
+
return AdminService(self.client)
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def organizations(self) -> "OrganizationsService":
|
|
40
|
+
from .organizations import OrganizationsService
|
|
41
|
+
|
|
42
|
+
return OrganizationsService(self.client)
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def projects(self) -> "ProjectsService":
|
|
46
|
+
from .projects import ProjectsService
|
|
47
|
+
|
|
48
|
+
return ProjectsService(self.client)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def file_systems(self) -> "FileSystemsService":
|
|
52
|
+
from .filesystems import FileSystemsService
|
|
53
|
+
|
|
54
|
+
return FileSystemsService(self.client)
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def workloads(self) -> "WorkloadsService":
|
|
58
|
+
from .workloads import WorkloadsService
|
|
59
|
+
|
|
60
|
+
return WorkloadsService(self.client)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def text_indexes(self) -> "TextIndexesService":
|
|
64
|
+
from .text_indexes import TextIndexesService
|
|
65
|
+
|
|
66
|
+
return TextIndexesService(self.client)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def key_space_indexes(self) -> "KeySpaceIndexesService":
|
|
70
|
+
from .key_space_indexes import KeySpaceIndexesService
|
|
71
|
+
|
|
72
|
+
return KeySpaceIndexesService(self.client)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def telemetry(self) -> "TelemetryService":
|
|
76
|
+
from .telemetry import TelemetryService
|
|
77
|
+
|
|
78
|
+
return TelemetryService(self.client)
|
spiral/api/admin.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .client import Paged, PagedResponse, ServiceBase
|
|
2
|
+
from .organizations import OrgMembership
|
|
3
|
+
from .types import OrgId
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AdminService(ServiceBase):
|
|
7
|
+
def sync_memberships(self, org_id: OrgId | None = None) -> Paged[OrgMembership]:
|
|
8
|
+
params = {}
|
|
9
|
+
if org_id:
|
|
10
|
+
params["org_id"] = str(org_id)
|
|
11
|
+
return self.client.paged("/v1/admin/sync-memberships", PagedResponse[OrgMembership], params=params)
|
|
12
|
+
|
|
13
|
+
def sync_orgs(self) -> Paged[OrgId]:
|
|
14
|
+
params = {}
|
|
15
|
+
return self.client.paged("/v1/admin/sync-orgs", PagedResponse[OrgId], params=params)
|
spiral/api/client.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Iterable, Iterator, Mapping
|
|
5
|
+
from typing import Any, Generic, TypeVar
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
from httpx import HTTPStatusError
|
|
9
|
+
from pydantic import BaseModel, Field, TypeAdapter
|
|
10
|
+
|
|
11
|
+
from spiral.core.authn import Authn
|
|
12
|
+
|
|
13
|
+
log = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
E = TypeVar("E")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PagedRequest(BaseModel):
|
|
20
|
+
page_token: str | None = None
|
|
21
|
+
page_size: int = 50
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PagedResponse(BaseModel, Generic[E]):
|
|
25
|
+
items: list[E] = Field(default_factory=list)
|
|
26
|
+
next_page_token: str | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
PagedReqT = TypeVar("PagedReqT", bound=PagedRequest)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Paged(Iterable[E], Generic[E]):
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
client: _Client,
|
|
36
|
+
path: str,
|
|
37
|
+
page_token: str | None,
|
|
38
|
+
page_size: int,
|
|
39
|
+
response_cls: type[PagedResponse[E]],
|
|
40
|
+
params: Mapping[str, str] | None = None,
|
|
41
|
+
):
|
|
42
|
+
self._client = client
|
|
43
|
+
self._path = path
|
|
44
|
+
self._response_cls = response_cls
|
|
45
|
+
|
|
46
|
+
self._page_size = page_size
|
|
47
|
+
|
|
48
|
+
self._params = params or {}
|
|
49
|
+
if page_token is not None:
|
|
50
|
+
self._params["page_token"] = str(page_token)
|
|
51
|
+
# TODO(marko): Support paging.
|
|
52
|
+
# if page_size is not None:
|
|
53
|
+
# self._params["page_size"] = str(page_size)
|
|
54
|
+
|
|
55
|
+
self._response: PagedResponse[E] = client.get(path, response_cls, params=self._params)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def page(self) -> PagedResponse[E]:
|
|
59
|
+
return self._response
|
|
60
|
+
|
|
61
|
+
def __iter__(self) -> Iterator[E]:
|
|
62
|
+
while True:
|
|
63
|
+
yield from self._response.items
|
|
64
|
+
|
|
65
|
+
if self._response.next_page_token is None:
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
params = self._params.copy()
|
|
69
|
+
params["page_token"] = self._response.next_page_token
|
|
70
|
+
self._response = self._client.get(self._path, self._response_cls, params=params)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ServiceBase:
|
|
74
|
+
def __init__(self, client: _Client):
|
|
75
|
+
self.client = client
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class SpiralHTTPError(Exception):
|
|
79
|
+
def __init__(self, body: str, code: int):
|
|
80
|
+
super().__init__(body)
|
|
81
|
+
self.body = body
|
|
82
|
+
self.code = code
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class _Client:
|
|
86
|
+
RequestT = TypeVar("RequestT")
|
|
87
|
+
ResponseT = TypeVar("ResponseT")
|
|
88
|
+
|
|
89
|
+
def __init__(self, http: httpx.Client, authn: Authn):
|
|
90
|
+
self.http = http
|
|
91
|
+
self.authn = authn
|
|
92
|
+
|
|
93
|
+
def get(
|
|
94
|
+
self, path: str, response_cls: type[ResponseT], *, params: Mapping[str, str | list[str]] | None = None
|
|
95
|
+
) -> ResponseT:
|
|
96
|
+
return self.request("GET", path, None, response_cls, params=params)
|
|
97
|
+
|
|
98
|
+
def post(
|
|
99
|
+
self,
|
|
100
|
+
path: str,
|
|
101
|
+
req: RequestT,
|
|
102
|
+
response_cls: type[ResponseT],
|
|
103
|
+
*,
|
|
104
|
+
params: Mapping[str, str | list[str]] | None = None,
|
|
105
|
+
) -> ResponseT:
|
|
106
|
+
return self.request("POST", path, req, response_cls, params=params)
|
|
107
|
+
|
|
108
|
+
def put(
|
|
109
|
+
self,
|
|
110
|
+
path: str,
|
|
111
|
+
req: RequestT,
|
|
112
|
+
response_cls: type[ResponseT],
|
|
113
|
+
*,
|
|
114
|
+
params: Mapping[str, str | list[str]] | None = None,
|
|
115
|
+
) -> ResponseT:
|
|
116
|
+
return self.request("PUT", path, req, response_cls, params=params)
|
|
117
|
+
|
|
118
|
+
def delete(
|
|
119
|
+
self, path: str, response_cls: type[ResponseT], *, params: Mapping[str, str | list[str]] | None = None
|
|
120
|
+
) -> ResponseT:
|
|
121
|
+
return self.request("DELETE", path, None, response_cls, params=params)
|
|
122
|
+
|
|
123
|
+
def request(
|
|
124
|
+
self,
|
|
125
|
+
method: str,
|
|
126
|
+
path: str,
|
|
127
|
+
req: RequestT | None,
|
|
128
|
+
response_cls: type[ResponseT],
|
|
129
|
+
*,
|
|
130
|
+
params: Mapping[str, str | list[str]] | None = None,
|
|
131
|
+
) -> ResponseT:
|
|
132
|
+
req_data: dict[str, Any] = {}
|
|
133
|
+
if req is not None:
|
|
134
|
+
req_data = dict(json=TypeAdapter(req.__class__).dump_python(req, mode="json", exclude_none=True))
|
|
135
|
+
|
|
136
|
+
token = self.authn.token()
|
|
137
|
+
resp = self.http.request(
|
|
138
|
+
method,
|
|
139
|
+
path,
|
|
140
|
+
params=params or {},
|
|
141
|
+
headers={"Authorization": f"Bearer {token.expose_secret()}"} if token else None,
|
|
142
|
+
**req_data,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
resp.raise_for_status()
|
|
147
|
+
except HTTPStatusError as e:
|
|
148
|
+
# Enrich the exception with the response body
|
|
149
|
+
raise SpiralHTTPError(body=resp.text, code=resp.status_code) from e
|
|
150
|
+
|
|
151
|
+
if response_cls == type[None]:
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
return TypeAdapter(response_cls).validate_python(resp.json())
|
|
155
|
+
|
|
156
|
+
def paged(
|
|
157
|
+
self,
|
|
158
|
+
path: str,
|
|
159
|
+
response_cls: type[PagedResponse[E]],
|
|
160
|
+
*,
|
|
161
|
+
page_token: str | None = None,
|
|
162
|
+
page_size: int = 50,
|
|
163
|
+
params: Mapping[str, str] | None = None,
|
|
164
|
+
) -> Paged[E]:
|
|
165
|
+
return Paged(self, path, page_token, page_size, response_cls, params)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from types import NoneType
|
|
3
|
+
from typing import Annotated, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import AfterValidator, BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from .client import Paged, PagedResponse, ServiceBase
|
|
8
|
+
from .types import ProjectId
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _validate_directory_path(path: str) -> str:
|
|
12
|
+
if not path.startswith("/"):
|
|
13
|
+
raise ValueError("Directory path must start with a slash.")
|
|
14
|
+
if not path.endswith("/"):
|
|
15
|
+
raise ValueError("Directory path must not end with a slash.")
|
|
16
|
+
return path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
DirectoryPath = Annotated[str, AfterValidator(_validate_directory_path)]
|
|
20
|
+
FilePath = str # Path or directory
|
|
21
|
+
FsLoc = str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BuiltinFileSystem(BaseModel):
|
|
25
|
+
"""Spiral supports several builtin file systems in different cloud provider regions."""
|
|
26
|
+
|
|
27
|
+
type: Literal["builtin"] = "builtin"
|
|
28
|
+
provider: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class UpstreamFileSystem(BaseModel):
|
|
32
|
+
"""File system that points to another project, usually a "file system" project.
|
|
33
|
+
|
|
34
|
+
Upstream project must have an external file system configured,
|
|
35
|
+
and not a builtin file system or another upstream file system.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
type: Literal["upstream"] = "upstream"
|
|
39
|
+
project_id: ProjectId
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class S3FileSystem(BaseModel):
|
|
43
|
+
"""File system backed by an AWS S3 bucket."""
|
|
44
|
+
|
|
45
|
+
type: Literal["s3"] = "s3"
|
|
46
|
+
endpoint: str | None = None
|
|
47
|
+
region: str
|
|
48
|
+
bucket: str
|
|
49
|
+
directory: DirectoryPath | None = None
|
|
50
|
+
# ARN of the role to assume when accessing the bucket
|
|
51
|
+
role_arn: str
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class VaultSecret(BaseModel):
|
|
55
|
+
type: Literal["secret"] = "secret"
|
|
56
|
+
# Reference to a secret stored in vault
|
|
57
|
+
secret: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class S3LikeFileSystem(BaseModel):
|
|
61
|
+
"""File system backed by an AWS S3 bucket that is compatible with S3 APIs."""
|
|
62
|
+
|
|
63
|
+
type: Literal["s3like"] = "s3like"
|
|
64
|
+
endpoint: str
|
|
65
|
+
region: str
|
|
66
|
+
bucket: str
|
|
67
|
+
directory: DirectoryPath | None = None
|
|
68
|
+
access_key_id: str
|
|
69
|
+
secret_access_key: str | VaultSecret
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class GCSFileSystem(BaseModel):
|
|
73
|
+
"""File system backed by a Google Cloud Storage bucket."""
|
|
74
|
+
|
|
75
|
+
type: Literal["gcs"] = "gcs"
|
|
76
|
+
region: str
|
|
77
|
+
bucket: str
|
|
78
|
+
directory: DirectoryPath | None = None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
FileSystem = Annotated[
|
|
82
|
+
BuiltinFileSystem | UpstreamFileSystem | S3FileSystem | S3LikeFileSystem | GCSFileSystem,
|
|
83
|
+
Field(discriminator="type"),
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class Mode(str, Enum):
|
|
88
|
+
READ_ONLY = "ro"
|
|
89
|
+
READ_WRITE = "rw"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class Mount(BaseModel):
|
|
93
|
+
"""Mount grants permission to a Spiral resource to use a specific directory within the file system."""
|
|
94
|
+
|
|
95
|
+
id: str
|
|
96
|
+
project_id: ProjectId
|
|
97
|
+
directory: DirectoryPath
|
|
98
|
+
mode: Mode
|
|
99
|
+
principal: str
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class CreateMountRequest(BaseModel):
|
|
103
|
+
directory: DirectoryPath
|
|
104
|
+
mode: Mode
|
|
105
|
+
principal: str
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class CreateMountResponse(BaseModel):
|
|
109
|
+
mount: Mount
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class GetMountAndFileSystemResponse(BaseModel):
|
|
113
|
+
mount: Mount
|
|
114
|
+
file_system: FileSystem
|
|
115
|
+
fs_loc: FsLoc
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class FileSystemsService(ServiceBase):
|
|
119
|
+
"""Service for file system operations."""
|
|
120
|
+
|
|
121
|
+
def list_providers(self) -> list[str]:
|
|
122
|
+
"""List builtin providers."""
|
|
123
|
+
response = self.client.get("/v1/file-systems/builtin-providers", dict)
|
|
124
|
+
return response.get("providers", [])
|
|
125
|
+
|
|
126
|
+
def update_file_system(self, project_id: ProjectId, request: FileSystem) -> FileSystem:
|
|
127
|
+
"""Update project's default file system."""
|
|
128
|
+
return self.client.post(f"/v1/file-systems/{project_id}", request, FileSystem)
|
|
129
|
+
|
|
130
|
+
def get_file_system(self, project_id: ProjectId) -> FileSystem:
|
|
131
|
+
"""Get project's default file system."""
|
|
132
|
+
return self.client.get(f"/v1/file-systems/{project_id}", FileSystem)
|
|
133
|
+
|
|
134
|
+
def create_mount(self, project_id: ProjectId, request: CreateMountRequest) -> CreateMountResponse:
|
|
135
|
+
"""Create a mount."""
|
|
136
|
+
return self.client.post(f"/v1/file-systems/{project_id}/mounts", request, CreateMountResponse)
|
|
137
|
+
|
|
138
|
+
def list_mounts(self, project_id: ProjectId) -> Paged[Mount]:
|
|
139
|
+
"""List active mounts in project's file system."""
|
|
140
|
+
return self.client.paged(f"/v1/file-systems/{project_id}/mounts", PagedResponse[Mount])
|
|
141
|
+
|
|
142
|
+
def get_mount(self, mount_id: str) -> Mount:
|
|
143
|
+
"""Get a mount."""
|
|
144
|
+
return self.client.get(f"/v1/mounts/{mount_id}", Mount)
|
|
145
|
+
|
|
146
|
+
def get_mount_and_file_system(self, mount_id: str) -> GetMountAndFileSystemResponse:
|
|
147
|
+
"""Get the mount and its associated file system."""
|
|
148
|
+
return self.client.get(f"/v1/mounts/{mount_id}/with-filesystem", GetMountAndFileSystemResponse)
|
|
149
|
+
|
|
150
|
+
def remove_mount(self, mount_id: str) -> None:
|
|
151
|
+
"""Remove mount."""
|
|
152
|
+
return self.client.delete(f"/v1/mounts/{mount_id}", NoneType)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
from .client import ServiceBase
|
|
4
|
+
from .types import IndexId, WorkerId
|
|
5
|
+
from .workers import ResourceClass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SyncIndexRequest(BaseModel):
|
|
9
|
+
"""Request to sync a text index."""
|
|
10
|
+
|
|
11
|
+
resources: ResourceClass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SyncIndexResponse(BaseModel):
|
|
15
|
+
worker_id: WorkerId
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class KeySpaceIndexesService(ServiceBase):
|
|
19
|
+
"""Service for key space index operations."""
|
|
20
|
+
|
|
21
|
+
def sync_index(self, index_id: IndexId, request: SyncIndexRequest) -> SyncIndexResponse:
|
|
22
|
+
"""Start a job to sync an index."""
|
|
23
|
+
return self.client.post(f"/v1/key-space-indexes/{index_id}/sync", request, SyncIndexResponse)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from .client import Paged, PagedResponse, ServiceBase
|
|
6
|
+
from .types import OrgId
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OrgRole(str, Enum):
|
|
10
|
+
OWNER = "owner"
|
|
11
|
+
MEMBER = "member"
|
|
12
|
+
GUEST = "guest"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Org(BaseModel):
|
|
16
|
+
id: OrgId
|
|
17
|
+
name: str | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OrgMembership(BaseModel):
|
|
21
|
+
user_id: str
|
|
22
|
+
org: Org
|
|
23
|
+
role: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CreateOrgRequest(BaseModel):
|
|
27
|
+
name: str | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CreateOrgResponse(BaseModel):
|
|
31
|
+
org: Org
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PortalLinkIntent(str, Enum):
|
|
35
|
+
SSO = "sso"
|
|
36
|
+
DIRECTORY_SYNC = "directory-sync"
|
|
37
|
+
AUDIT_LOGS = "audit-logs"
|
|
38
|
+
LOG_STREAMS = "log-streams"
|
|
39
|
+
DOMAIN_VERIFICATION = "domain-verification"
|
|
40
|
+
BYOK = "bring-your-own-key"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class PortalLinkRequest(BaseModel):
|
|
44
|
+
intent: PortalLinkIntent
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class PortalLinkResponse(BaseModel):
|
|
48
|
+
url: str
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class InviteUserRequest(BaseModel):
|
|
52
|
+
email: str
|
|
53
|
+
role: OrgRole
|
|
54
|
+
expires_in_days: int | None = 7
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class InviteUserResponse(BaseModel):
|
|
58
|
+
invite_id: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class OrganizationsService(ServiceBase):
|
|
62
|
+
"""Service for organization operations."""
|
|
63
|
+
|
|
64
|
+
def create(self, request: CreateOrgRequest) -> CreateOrgResponse:
|
|
65
|
+
"""Create a new organization."""
|
|
66
|
+
return self.client.post("/v1/organizations", request, CreateOrgResponse)
|
|
67
|
+
|
|
68
|
+
def list_memberships(self) -> Paged[OrgMembership]:
|
|
69
|
+
"""List organization memberships."""
|
|
70
|
+
return self.client.paged("/v1/organizations", PagedResponse[OrgMembership])
|
|
71
|
+
|
|
72
|
+
def invite_user(self, request: InviteUserRequest) -> InviteUserResponse:
|
|
73
|
+
"""Invite a user to the organization."""
|
|
74
|
+
return self.client.post("/v1/organizations/invite-user", request, InviteUserResponse)
|
|
75
|
+
|
|
76
|
+
def portal_link(self, request: PortalLinkRequest) -> PortalLinkResponse:
|
|
77
|
+
"""Get configuration portal link for the organization."""
|
|
78
|
+
return self.client.put("/v1/organizations/portal-link", request, PortalLinkResponse)
|