retab 0.0.43__py3-none-any.whl → 0.0.45__py3-none-any.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.
- retab/client.py +11 -7
- retab/generate_types.py +180 -0
- retab/resources/deployments/__init__.py +3 -0
- retab/resources/deployments/automations/__init__.py +9 -0
- retab/resources/deployments/automations/client.py +244 -0
- retab/resources/deployments/automations/endpoints.py +290 -0
- retab/resources/deployments/automations/links.py +303 -0
- retab/resources/deployments/automations/logs.py +222 -0
- retab/resources/deployments/automations/mailboxes.py +423 -0
- retab/resources/deployments/automations/outlook.py +377 -0
- retab/resources/deployments/automations/tests.py +161 -0
- retab/resources/deployments/client.py +148 -0
- retab/resources/evaluations/__init__.py +2 -2
- retab/resources/evaluations/client.py +46 -75
- retab/resources/evaluations/documents.py +41 -31
- retab/resources/evaluations/iterations.py +12 -12
- retab/resources/projects/__init__.py +3 -0
- retab/resources/projects/client.py +285 -0
- retab/resources/projects/documents.py +244 -0
- retab/resources/projects/iterations.py +470 -0
- retab/types/ai_models.py +1 -0
- retab/types/deprecated_evals.py +7 -7
- retab/types/evaluations/model.py +2 -5
- retab/types/jobs/base.py +1 -1
- retab/types/jobs/evaluation.py +1 -1
- retab/types/projects/__init__.py +34 -0
- retab/types/projects/documents.py +30 -0
- retab/types/projects/iterations.py +78 -0
- retab/types/projects/model.py +68 -0
- retab/types/schemas/evaluate.py +1 -1
- retab/types/schemas/object.py +5 -29
- retab/utils/usage/usage.py +0 -1
- {retab-0.0.43.dist-info → retab-0.0.45.dist-info}/METADATA +2 -2
- {retab-0.0.43.dist-info → retab-0.0.45.dist-info}/RECORD +36 -17
- {retab-0.0.43.dist-info → retab-0.0.45.dist-info}/WHEEL +0 -0
- {retab-0.0.43.dist-info → retab-0.0.45.dist-info}/top_level.txt +0 -0
retab/client.py
CHANGED
@@ -8,7 +8,7 @@ import backoff.types
|
|
8
8
|
import httpx
|
9
9
|
import truststore
|
10
10
|
|
11
|
-
from .resources import consensus, documents, files, finetuning, models, processors, schemas, secrets, usage,
|
11
|
+
from .resources import consensus, deployments, documents, files, finetuning, models, processors, schemas, secrets, usage, projects
|
12
12
|
from .types.standards import PreparedRequest, FieldUnset
|
13
13
|
|
14
14
|
|
@@ -135,7 +135,7 @@ class Retab(BaseRetab):
|
|
135
135
|
"""Synchronous client for interacting with the Retab API.
|
136
136
|
|
137
137
|
This client provides synchronous access to all Retab API resources including files, fine-tuning,
|
138
|
-
prompt optimization, documents, models,
|
138
|
+
prompt optimization, documents, models, processors, deployments, and schemas.
|
139
139
|
|
140
140
|
Args:
|
141
141
|
api_key (str, optional): Retab API key. If not provided, will look for RETAB_API_KEY env variable.
|
@@ -151,7 +151,8 @@ class Retab(BaseRetab):
|
|
151
151
|
prompt_optimization: Access to prompt optimization operations
|
152
152
|
documents: Access to document operations
|
153
153
|
models: Access to model operations
|
154
|
-
|
154
|
+
processors: Access to processor operations
|
155
|
+
deployments: Access to deployment operations
|
155
156
|
schemas: Access to schema operations
|
156
157
|
responses: Access to responses API (OpenAI Responses API compatible interface)
|
157
158
|
"""
|
@@ -175,13 +176,14 @@ class Retab(BaseRetab):
|
|
175
176
|
)
|
176
177
|
|
177
178
|
self.client = httpx.Client(timeout=self.timeout)
|
178
|
-
self.
|
179
|
+
self.projects = projects.Projects(client=self)
|
179
180
|
self.files = files.Files(client=self)
|
180
181
|
self.fine_tuning = finetuning.FineTuning(client=self)
|
181
182
|
self.documents = documents.Documents(client=self)
|
182
183
|
self.models = models.Models(client=self)
|
183
184
|
self.schemas = schemas.Schemas(client=self)
|
184
185
|
self.processors = processors.Processors(client=self)
|
186
|
+
self.deployments = deployments.Deployments(client=self)
|
185
187
|
self.secrets = secrets.Secrets(client=self)
|
186
188
|
self.usage = usage.Usage(client=self)
|
187
189
|
self.consensus = consensus.Consensus(client=self)
|
@@ -395,7 +397,7 @@ class AsyncRetab(BaseRetab):
|
|
395
397
|
"""Asynchronous client for interacting with the Retab API.
|
396
398
|
|
397
399
|
This client provides asynchronous access to all Retab API resources including files, fine-tuning,
|
398
|
-
prompt optimization, documents, models,
|
400
|
+
prompt optimization, documents, models, processors, deployments, and schemas.
|
399
401
|
|
400
402
|
Args:
|
401
403
|
api_key (str, optional): Retab API key. If not provided, will look for RETAB_API_KEY env variable.
|
@@ -413,7 +415,8 @@ class AsyncRetab(BaseRetab):
|
|
413
415
|
prompt_optimization: Access to asynchronous prompt optimization operations
|
414
416
|
documents: Access to asynchronous document operations
|
415
417
|
models: Access to asynchronous model operations
|
416
|
-
|
418
|
+
processors: Access to asynchronous processor operations
|
419
|
+
deployments: Access to asynchronous deployment operations
|
417
420
|
schemas: Access to asynchronous schema operations
|
418
421
|
responses: Access to responses API (OpenAI Responses API compatible interface)
|
419
422
|
"""
|
@@ -438,13 +441,14 @@ class AsyncRetab(BaseRetab):
|
|
438
441
|
|
439
442
|
self.client = httpx.AsyncClient(timeout=self.timeout)
|
440
443
|
|
441
|
-
self.
|
444
|
+
self.projects = projects.AsyncProjects(client=self)
|
442
445
|
self.files = files.AsyncFiles(client=self)
|
443
446
|
self.fine_tuning = finetuning.AsyncFineTuning(client=self)
|
444
447
|
self.documents = documents.AsyncDocuments(client=self)
|
445
448
|
self.models = models.AsyncModels(client=self)
|
446
449
|
self.schemas = schemas.AsyncSchemas(client=self)
|
447
450
|
self.processors = processors.AsyncProcessors(client=self)
|
451
|
+
self.deployments = deployments.AsyncDeployments(client=self)
|
448
452
|
self.secrets = secrets.AsyncSecrets(client=self)
|
449
453
|
self.usage = usage.AsyncUsage(client=self)
|
450
454
|
self.consensus = consensus.AsyncConsensus(client=self)
|
retab/generate_types.py
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
import collections.abc
|
2
|
+
import json
|
3
|
+
import os
|
4
|
+
import types
|
5
|
+
import typing
|
6
|
+
import enum
|
7
|
+
import sys
|
8
|
+
import inspect
|
9
|
+
from datetime import datetime, date
|
10
|
+
from typing import Any, Type, get_args, get_origin, Union, Literal, is_typeddict
|
11
|
+
from typing_extensions import is_typeddict as is_typeddict_ext
|
12
|
+
import typing_extensions
|
13
|
+
from pydantic_core import PydanticUndefined
|
14
|
+
from pydantic import BaseModel, EmailStr
|
15
|
+
import PIL.Image
|
16
|
+
|
17
|
+
to_compile: list[tuple[str, Type, bool]] = []
|
18
|
+
|
19
|
+
def is_base_model(field_type: Type) -> bool:
|
20
|
+
return getattr(field_type, "__name__", None) in ["BaseModel", "GenericModel", "ConfigDict", "Generic"]
|
21
|
+
|
22
|
+
def type_to_zod(field_type: Any, put_names: bool = True, ts: bool = False) -> str:
|
23
|
+
origin = get_origin(field_type) or field_type
|
24
|
+
optional = False
|
25
|
+
|
26
|
+
def make_union(args):
|
27
|
+
return args[0] if len(args) <= 1 else "z.union([" + ", ".join(args) + "])"
|
28
|
+
|
29
|
+
def make_ts_union(args):
|
30
|
+
return args[0] if len(args) <= 1 else " | ".join(args)
|
31
|
+
|
32
|
+
if isinstance(field_type, typing.ForwardRef):
|
33
|
+
return type_to_zod(typing._eval_type(field_type, globals(), locals(), []), ts=ts)
|
34
|
+
elif origin is typing.Annotated or origin is typing.Required or origin is typing_extensions.Required:
|
35
|
+
return type_to_zod(get_args(field_type)[0], put_names, ts=ts)
|
36
|
+
if origin is Union or origin is types.UnionType:
|
37
|
+
args = [x for x in get_args(field_type)]
|
38
|
+
if types.NoneType in args:
|
39
|
+
args.remove(types.NoneType)
|
40
|
+
optional = True
|
41
|
+
typename = make_union([type_to_zod(x) for x in args])
|
42
|
+
ts_typename = make_ts_union([type_to_zod(x, ts=True) for x in args])
|
43
|
+
elif issubclass(origin, BaseModel) or is_typeddict(origin) or is_typeddict_ext(origin):
|
44
|
+
if put_names:
|
45
|
+
typename = "Z" + origin.__name__
|
46
|
+
ts_typename = origin.__name__
|
47
|
+
to_compile.append((origin.__name__, field_type, True))
|
48
|
+
else:
|
49
|
+
excluded_fields = set()
|
50
|
+
typename = "z.object({\n"
|
51
|
+
ts_typename = "{\n"
|
52
|
+
props = [(n, f.annotation, f.default) for n, f in origin.model_fields.items()] if issubclass(origin, BaseModel) else \
|
53
|
+
[(n, f, PydanticUndefined) for n, f in origin.__annotations__.items()]
|
54
|
+
|
55
|
+
for field_name, field, default in props:
|
56
|
+
if field_name in excluded_fields:
|
57
|
+
continue
|
58
|
+
ts_compiled = type_to_zod(field, ts=True)
|
59
|
+
default_str = ""
|
60
|
+
if default is not PydanticUndefined and default is not None:
|
61
|
+
if isinstance(default, BaseModel):
|
62
|
+
default_str = f".default({json.dumps(default.model_dump(mode="json", exclude_unset=True))})"
|
63
|
+
else:
|
64
|
+
default_str = f".default({json.dumps(default)})"
|
65
|
+
typename += f" {field_name}: {type_to_zod(field)}{default_str},\n"
|
66
|
+
ts_typename += f" {field_name}{"?" if ts_compiled.endswith(" | undefined") or default is not PydanticUndefined else ""}: {ts_compiled},\n"
|
67
|
+
typename += "})"
|
68
|
+
ts_typename += "}"
|
69
|
+
|
70
|
+
based = origin.__bases__
|
71
|
+
for i in range(0, len(based)):
|
72
|
+
if is_base_model(based[i]) or based[i] is dict:
|
73
|
+
break
|
74
|
+
if issubclass(based[i], BaseModel):
|
75
|
+
excluded_fields.update(based[i].model_fields.keys())
|
76
|
+
typename += ".merge(Z" + based[i].__name__ + ".schema)"
|
77
|
+
ts_typename += " & " + based[i].__name__
|
78
|
+
elif origin is list or origin is typing.List or origin is collections.abc.Sequence or origin is collections.abc.Iterable:
|
79
|
+
typename = "z.array(" + type_to_zod(get_args(field_type)[0]) + ")"
|
80
|
+
ts_typename = "Array<" + type_to_zod(get_args(field_type)[0], ts=True) + ">"
|
81
|
+
elif origin is tuple:
|
82
|
+
args = get_args(field_type)
|
83
|
+
typename = "z.tuple([" + ", ".join([type_to_zod(x) for x in args]) + "])"
|
84
|
+
ts_typename = "[" + ", ".join([type_to_zod(x, ts=True) for x in args]) + "]"
|
85
|
+
elif origin is dict:
|
86
|
+
if len(get_args(field_type)) == 2:
|
87
|
+
typename = "z.record(" + type_to_zod(get_args(field_type)[0]) + ", " + type_to_zod(get_args(field_type)[1]) + ")"
|
88
|
+
ts_typename = "{[key: " + type_to_zod(get_args(field_type)[0], ts=True) + "]: " + type_to_zod(get_args(field_type)[1], ts=True) + "}"
|
89
|
+
else:
|
90
|
+
typename = "z.record(z.any())"
|
91
|
+
ts_typename = "{[key: string]: any}"
|
92
|
+
elif origin is Literal:
|
93
|
+
typename = make_union(["z.literal(" + json.dumps(x) + ")" for x in get_args(field_type)])
|
94
|
+
ts_typename = make_ts_union([json.dumps(x) for x in get_args(field_type)])
|
95
|
+
elif isinstance(field_type, typing.TypeVar):
|
96
|
+
typename = "z.any()"
|
97
|
+
ts_typename = "any"
|
98
|
+
elif isinstance(field_type, type) and issubclass(field_type, enum.Enum):
|
99
|
+
typename = "z.any()"
|
100
|
+
ts_typename = "any"
|
101
|
+
elif field_type is str or field_type is date or field_type is datetime:
|
102
|
+
typename = "z.string()"
|
103
|
+
ts_typename = "string"
|
104
|
+
elif field_type is int or field_type is float:
|
105
|
+
typename = "z.number()"
|
106
|
+
ts_typename = "number"
|
107
|
+
elif field_type is bool:
|
108
|
+
typename = "z.boolean()"
|
109
|
+
ts_typename = "boolean"
|
110
|
+
elif field_type is typing.Any:
|
111
|
+
typename = "z.any()"
|
112
|
+
ts_typename = "any"
|
113
|
+
elif field_type is bytes or field_type is PIL.Image.Image or field_type is typing.BinaryIO or origin is typing.IO or origin is typing_extensions.IO:
|
114
|
+
typename = "z.instanceof(Uint8Array)"
|
115
|
+
ts_typename = "Uint8Array"
|
116
|
+
elif field_type is EmailStr:
|
117
|
+
typename = "z.string().email()"
|
118
|
+
ts_typename = "string"
|
119
|
+
elif field_type is os.PathLike:
|
120
|
+
typename = "z.string()"
|
121
|
+
ts_typename = "string"
|
122
|
+
elif field_type is object:
|
123
|
+
typename = "z.object({}).passthrough()"
|
124
|
+
ts_typename = "object"
|
125
|
+
else:
|
126
|
+
raise ValueError(f"Unsupported type: {field_type} ({origin})")
|
127
|
+
if ts:
|
128
|
+
return ts_typename if not optional else ts_typename + " | null | undefined"
|
129
|
+
else:
|
130
|
+
return typename if not optional else typename + ".nullable().optional()"
|
131
|
+
|
132
|
+
|
133
|
+
# SET of names of python builtin types starting with a capital
|
134
|
+
builtin_types = {
|
135
|
+
"Any",
|
136
|
+
"BaseModel",
|
137
|
+
"NoneType",
|
138
|
+
"Literal",
|
139
|
+
"Union",
|
140
|
+
"List",
|
141
|
+
"Sequence",
|
142
|
+
"ConfigDict",
|
143
|
+
"Optional",
|
144
|
+
}
|
145
|
+
|
146
|
+
if __name__ == "__main__":
|
147
|
+
modules = []
|
148
|
+
for root, dirs, files in os.walk("retab/types"):
|
149
|
+
for module in files:
|
150
|
+
if module[-3:] != '.py':
|
151
|
+
continue
|
152
|
+
full_name = os.path.join(root, module[:-3]).replace(os.path.sep, '.')
|
153
|
+
__import__(full_name, locals(), globals())
|
154
|
+
modules.append(full_name)
|
155
|
+
|
156
|
+
|
157
|
+
for module_name in modules:
|
158
|
+
for name, obj in inspect.getmembers(sys.modules[module_name]):
|
159
|
+
if name[0] != name[0].lower() and name not in builtin_types:
|
160
|
+
to_compile.append((name, obj, False))
|
161
|
+
|
162
|
+
print("import * as z from 'zod';\n")
|
163
|
+
|
164
|
+
defined = {}
|
165
|
+
while len(to_compile) > 0:
|
166
|
+
name, model, necessary = to_compile.pop(0)
|
167
|
+
if name in defined: continue
|
168
|
+
defined[name] = True
|
169
|
+
try:
|
170
|
+
compiled = type_to_zod(model, False)
|
171
|
+
compiled_ts = type_to_zod(model, False, ts=True)
|
172
|
+
except Exception as e:
|
173
|
+
if not necessary:
|
174
|
+
print(f"Skipping {name} {model} due to error: {e}", file=sys.stderr)
|
175
|
+
continue
|
176
|
+
print(f"Error compiling {name} {model}", file=sys.stderr)
|
177
|
+
raise e
|
178
|
+
print("export const Z" + name + " = z.lazy(() => " + compiled + ");")
|
179
|
+
print("export type " + name + " = z.infer<typeof Z" + name + ">;\n")
|
180
|
+
|
@@ -0,0 +1,244 @@
|
|
1
|
+
import hashlib
|
2
|
+
import hmac
|
3
|
+
import json
|
4
|
+
from typing import Any, Literal, Optional, Union
|
5
|
+
|
6
|
+
from ...._resource import AsyncAPIResource, SyncAPIResource
|
7
|
+
from ....types.automations.endpoints import Endpoint, UpdateEndpointRequest
|
8
|
+
from ....types.automations.links import Link, UpdateLinkRequest
|
9
|
+
from ....types.automations.mailboxes import Mailbox, UpdateMailboxRequest
|
10
|
+
from ....types.automations.outlook import Outlook, UpdateOutlookRequest
|
11
|
+
from ....types.standards import PreparedRequest
|
12
|
+
from .endpoints import AsyncEndpoints, Endpoints
|
13
|
+
from .links import AsyncLinks, Links
|
14
|
+
from .logs import AsyncLogs, Logs
|
15
|
+
from .mailboxes import AsyncMailboxes, Mailboxes
|
16
|
+
from .outlook import AsyncOutlooks, Outlooks
|
17
|
+
from .tests import AsyncTests, Tests
|
18
|
+
|
19
|
+
|
20
|
+
class SignatureVerificationError(Exception):
|
21
|
+
"""Raised when webhook signature verification fails."""
|
22
|
+
|
23
|
+
pass
|
24
|
+
|
25
|
+
|
26
|
+
class AutomationsMixin:
|
27
|
+
def _verify_event(self, event_body: bytes, event_signature: str, secret: str) -> Any:
|
28
|
+
"""
|
29
|
+
Verify the signature of a webhook event.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
body: The raw request body
|
33
|
+
signature: The signature header
|
34
|
+
secret: The secret key used for signing
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
The parsed event payload
|
38
|
+
|
39
|
+
Raises:
|
40
|
+
SignatureVerificationError: If the signature verification fails
|
41
|
+
"""
|
42
|
+
expected_signature = hmac.new(secret.encode(), event_body, hashlib.sha256).hexdigest()
|
43
|
+
|
44
|
+
if not hmac.compare_digest(event_signature, expected_signature):
|
45
|
+
raise SignatureVerificationError("Invalid signature")
|
46
|
+
|
47
|
+
return json.loads(event_body.decode("utf-8"))
|
48
|
+
|
49
|
+
def prepare_list(
|
50
|
+
self,
|
51
|
+
processor_id: str,
|
52
|
+
before: Optional[str] = None,
|
53
|
+
after: Optional[str] = None,
|
54
|
+
limit: Optional[int] = 10,
|
55
|
+
order: Optional[Literal["asc", "desc"]] = "desc",
|
56
|
+
automation_id: Optional[str] = None,
|
57
|
+
webhook_url: Optional[str] = None,
|
58
|
+
name: Optional[str] = None,
|
59
|
+
) -> PreparedRequest:
|
60
|
+
params = {
|
61
|
+
"before": before,
|
62
|
+
"after": after,
|
63
|
+
"limit": limit,
|
64
|
+
"order": order,
|
65
|
+
"id": automation_id,
|
66
|
+
"webhook_url": webhook_url,
|
67
|
+
"name": name,
|
68
|
+
}
|
69
|
+
params = {k: v for k, v in params.items() if v is not None}
|
70
|
+
return PreparedRequest(method="GET", url=f"/v1/processors/{processor_id}/automations", params=params)
|
71
|
+
|
72
|
+
def prepare_get(self, processor_id: str, automation_id: str) -> PreparedRequest:
|
73
|
+
return PreparedRequest(method="GET", url=f"/v1/processors/{processor_id}/automations/{automation_id}")
|
74
|
+
|
75
|
+
def prepare_update(
|
76
|
+
self,
|
77
|
+
processor_id: str,
|
78
|
+
automation_id: str,
|
79
|
+
automation_data: Union[UpdateLinkRequest, UpdateMailboxRequest, UpdateEndpointRequest, UpdateOutlookRequest],
|
80
|
+
) -> PreparedRequest:
|
81
|
+
return PreparedRequest(method="PUT", url=f"/v1/processors/{processor_id}/automations/{automation_id}", data=automation_data.model_dump(mode="json"))
|
82
|
+
|
83
|
+
def prepare_delete(self, processor_id: str, automation_id: str) -> PreparedRequest:
|
84
|
+
return PreparedRequest(method="DELETE", url=f"/v1/processors/{processor_id}/automations/{automation_id}")
|
85
|
+
|
86
|
+
|
87
|
+
class Automations(SyncAPIResource, AutomationsMixin):
|
88
|
+
"""Automations API wrapper"""
|
89
|
+
|
90
|
+
def __init__(self, client: Any) -> None:
|
91
|
+
super().__init__(client=client)
|
92
|
+
self.mailboxes = Mailboxes(client=client)
|
93
|
+
self.links = Links(client=client)
|
94
|
+
self.outlook = Outlooks(client=client)
|
95
|
+
self.endpoints = Endpoints(client=client)
|
96
|
+
self.tests = Tests(client=client)
|
97
|
+
self.logs = Logs(client=client)
|
98
|
+
|
99
|
+
def verify_event(self, event_body: bytes, event_signature: str, secret: str) -> Any:
|
100
|
+
"""
|
101
|
+
Verify the signature of a webhook event.
|
102
|
+
"""
|
103
|
+
return self._verify_event(event_body, event_signature, secret)
|
104
|
+
|
105
|
+
def list_automations(
|
106
|
+
self,
|
107
|
+
processor_id: str,
|
108
|
+
before: Optional[str] = None,
|
109
|
+
after: Optional[str] = None,
|
110
|
+
limit: Optional[int] = 10,
|
111
|
+
order: Optional[Literal["asc", "desc"]] = "desc",
|
112
|
+
automation_id: Optional[str] = None,
|
113
|
+
webhook_url: Optional[str] = None,
|
114
|
+
name: Optional[str] = None,
|
115
|
+
):
|
116
|
+
"""List automations attached to this processor."""
|
117
|
+
request = self.prepare_list(processor_id, before, after, limit, order, automation_id, webhook_url, name)
|
118
|
+
response = self._client._prepared_request(request)
|
119
|
+
return response
|
120
|
+
|
121
|
+
def get_automation(self, processor_id: str, automation_id: str) -> Union[Link, Mailbox, Endpoint, Outlook]:
|
122
|
+
"""Get a specific automation attached to this processor."""
|
123
|
+
request = self.prepare_get(processor_id, automation_id)
|
124
|
+
response = self._client._prepared_request(request)
|
125
|
+
|
126
|
+
# Return the appropriate model based on the automation type
|
127
|
+
if response["object"] == "automation.link":
|
128
|
+
return Link.model_validate(response)
|
129
|
+
elif response["object"] == "automation.mailbox":
|
130
|
+
return Mailbox.model_validate(response)
|
131
|
+
elif response["object"] == "automation.endpoint":
|
132
|
+
return Endpoint.model_validate(response)
|
133
|
+
elif response["object"] == "automation.outlook":
|
134
|
+
return Outlook.model_validate(response)
|
135
|
+
else:
|
136
|
+
raise ValueError(f"Unknown automation type: {response.get('object')}")
|
137
|
+
|
138
|
+
def update_automation(
|
139
|
+
self,
|
140
|
+
processor_id: str,
|
141
|
+
automation_id: str,
|
142
|
+
automation_data: Union[UpdateLinkRequest, UpdateMailboxRequest, UpdateEndpointRequest, UpdateOutlookRequest],
|
143
|
+
) -> Union[Link, Mailbox, Endpoint, Outlook]:
|
144
|
+
"""Update an automation attached to this processor."""
|
145
|
+
request = self.prepare_update(processor_id, automation_id, automation_data)
|
146
|
+
response = self._client._prepared_request(request)
|
147
|
+
|
148
|
+
# Return the appropriate model based on the automation type
|
149
|
+
if response["object"] == "automation.link":
|
150
|
+
return Link.model_validate(response)
|
151
|
+
elif response["object"] == "automation.mailbox":
|
152
|
+
return Mailbox.model_validate(response)
|
153
|
+
elif response["object"] == "automation.endpoint":
|
154
|
+
return Endpoint.model_validate(response)
|
155
|
+
elif response["object"] == "automation.outlook":
|
156
|
+
return Outlook.model_validate(response)
|
157
|
+
else:
|
158
|
+
raise ValueError(f"Unknown automation type: {response.get('object')}")
|
159
|
+
|
160
|
+
def delete_automation(self, processor_id: str, automation_id: str) -> None:
|
161
|
+
"""Delete an automation attached to this processor."""
|
162
|
+
request = self.prepare_delete(processor_id, automation_id)
|
163
|
+
self._client._prepared_request(request)
|
164
|
+
print(f"Automation {automation_id} deleted from processor {processor_id}")
|
165
|
+
|
166
|
+
|
167
|
+
class AsyncAutomations(AsyncAPIResource, AutomationsMixin):
|
168
|
+
"""Async Automations API wrapper"""
|
169
|
+
|
170
|
+
def __init__(self, client: Any) -> None:
|
171
|
+
super().__init__(client=client)
|
172
|
+
self.mailboxes = AsyncMailboxes(client=client)
|
173
|
+
self.links = AsyncLinks(client=client)
|
174
|
+
self.outlook = AsyncOutlooks(client=client)
|
175
|
+
self.endpoints = AsyncEndpoints(client=client)
|
176
|
+
self.tests = AsyncTests(client=client)
|
177
|
+
self.logs = AsyncLogs(client=client)
|
178
|
+
|
179
|
+
async def verify_event(self, event_body: bytes, event_signature: str, secret: str) -> Any:
|
180
|
+
"""
|
181
|
+
Verify the signature of a webhook event.
|
182
|
+
"""
|
183
|
+
return self._verify_event(event_body, event_signature, secret)
|
184
|
+
|
185
|
+
async def list(
|
186
|
+
self,
|
187
|
+
processor_id: str,
|
188
|
+
before: Optional[str] = None,
|
189
|
+
after: Optional[str] = None,
|
190
|
+
limit: Optional[int] = 10,
|
191
|
+
order: Optional[Literal["asc", "desc"]] = "desc",
|
192
|
+
automation_id: Optional[str] = None,
|
193
|
+
webhook_url: Optional[str] = None,
|
194
|
+
name: Optional[str] = None,
|
195
|
+
):
|
196
|
+
"""List automations attached to this processor."""
|
197
|
+
request = self.prepare_list(processor_id, before, after, limit, order, automation_id, webhook_url, name)
|
198
|
+
response = await self._client._prepared_request(request)
|
199
|
+
return response
|
200
|
+
|
201
|
+
async def get(self, processor_id: str, automation_id: str) -> Union[Link, Mailbox, Endpoint, Outlook]:
|
202
|
+
"""Get a specific automation attached to this processor."""
|
203
|
+
request = self.prepare_get(processor_id, automation_id)
|
204
|
+
response = await self._client._prepared_request(request)
|
205
|
+
|
206
|
+
# Return the appropriate model based on the automation type
|
207
|
+
if response["object"] == "automation.link":
|
208
|
+
return Link.model_validate(response)
|
209
|
+
elif response["object"] == "automation.mailbox":
|
210
|
+
return Mailbox.model_validate(response)
|
211
|
+
elif response["object"] == "automation.endpoint":
|
212
|
+
return Endpoint.model_validate(response)
|
213
|
+
elif response["object"] == "automation.outlook":
|
214
|
+
return Outlook.model_validate(response)
|
215
|
+
else:
|
216
|
+
raise ValueError(f"Unknown automation type: {response.get('object')}")
|
217
|
+
|
218
|
+
async def update(
|
219
|
+
self,
|
220
|
+
processor_id: str,
|
221
|
+
automation_id: str,
|
222
|
+
automation_data: Union[UpdateLinkRequest, UpdateMailboxRequest, UpdateEndpointRequest, UpdateOutlookRequest],
|
223
|
+
) -> Union[Link, Mailbox, Endpoint, Outlook]:
|
224
|
+
"""Update an automation attached to this processor."""
|
225
|
+
request = self.prepare_update(processor_id, automation_id, automation_data)
|
226
|
+
response = await self._client._prepared_request(request)
|
227
|
+
|
228
|
+
# Return the appropriate model based on the automation type
|
229
|
+
if response["object"] == "automation.link":
|
230
|
+
return Link.model_validate(response)
|
231
|
+
elif response["object"] == "automation.mailbox":
|
232
|
+
return Mailbox.model_validate(response)
|
233
|
+
elif response["object"] == "automation.endpoint":
|
234
|
+
return Endpoint.model_validate(response)
|
235
|
+
elif response["object"] == "automation.outlook":
|
236
|
+
return Outlook.model_validate(response)
|
237
|
+
else:
|
238
|
+
raise ValueError(f"Unknown automation type: {response.get('object')}")
|
239
|
+
|
240
|
+
async def delete(self, processor_id: str, automation_id: str) -> None:
|
241
|
+
"""Delete an automation attached to this processor."""
|
242
|
+
request = self.prepare_delete(processor_id, automation_id)
|
243
|
+
await self._client._prepared_request(request)
|
244
|
+
print(f"Automation {automation_id} deleted from processor {processor_id}")
|