synapseone-sdk 0.7.0__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.
@@ -0,0 +1,11 @@
1
+ # Copyright 2026 SynapseOne Contributors
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ """Compatibility shim for the SynapseDX -> SynapseOne rebrand.
4
+
5
+ ``synapsedx_sdk`` is deprecated; import ``synapseone_sdk`` instead. Everything
6
+ here is re-exported from the canonical package, including the legacy
7
+ ``SynapseDXClient`` / ``SynapseDXError`` aliases.
8
+ """
9
+
10
+ from synapseone_sdk import * # noqa: F401,F403
11
+ from synapseone_sdk import __all__ as __all__ # noqa: F401
@@ -0,0 +1,291 @@
1
+ # Copyright 2026 SynapseOne Contributors
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ """synapseone Python SDK — gateway client for the SynapseOne platform.
4
+
5
+ The SDK is split per service; use the namespace that matches the service you
6
+ talk to:
7
+
8
+ ====================== =============================================
9
+ ``client.iam`` auth, orgs/accounts/users, roles, policies,
10
+ API keys, credentials, service accounts
11
+ ``client.workflow`` workflow templates, CRUD/lifecycle, executions,
12
+ test runs
13
+ ``client.lds`` LDS datasources, attributes, records, NGSI-LD
14
+ entities, licenses
15
+ ``client.base`` SynapseBase collections/documents, queries,
16
+ batches, counters, change feed, object mapping
17
+ ``client.faststore`` Synapse Faststore cache/KV, hashes, lists, sets,
18
+ sorted sets, locks
19
+ ``client.queue`` SynapseQueue publish/consume/ack, dead letters
20
+ ====================== =============================================
21
+
22
+ The old flat methods on :class:`SynapseOneClient` still work but are deprecated:
23
+ each one emits a ``DeprecationWarning`` and forwards to the namespaced call
24
+ (e.g. ``client.list_templates()`` -> ``client.workflow.list_templates()``).
25
+
26
+ Example::
27
+
28
+ from synapseone_sdk import SynapseOneClient
29
+
30
+ client = SynapseOneClient("http://localhost:8080", token=jwt)
31
+ parks = client.base.collection("parks", model=Park, mapping={"name": "profile.name"})
32
+ parks.create(Park(name="Central"))
33
+ result = client.queue.publish("emails", {"to": "a@b.c"})
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import warnings
39
+ from typing import Any
40
+
41
+ from ._http import UNSET, HttpClient, SynapseOneError
42
+ from .ai import AiService
43
+ from .base import (
44
+ BaseCollection,
45
+ BaseService,
46
+ QueryResult,
47
+ VectorSearchResult,
48
+ data_to_object,
49
+ normalize_mapping,
50
+ object_to_data,
51
+ )
52
+ from .faststore import FaststoreService
53
+ from .iam import IamService
54
+ from .inference import InferenceService
55
+ from .lds import LdsService
56
+ from .queue import QueueService
57
+ from .stream import StreamService
58
+ from .workflow import WorkflowService, trigger_webhook, unwrap_executions
59
+
60
+ __all__ = [
61
+ "UNSET",
62
+ "AiService",
63
+ "BaseCollection",
64
+ "BaseService",
65
+ "FaststoreService",
66
+ "IamService",
67
+ "InferenceService",
68
+ "LdsService",
69
+ "QueryResult",
70
+ "VectorSearchResult",
71
+ "QueueService",
72
+ "StreamService",
73
+ "SynapseDXClient",
74
+ "SynapseDXError",
75
+ "SynapseOneClient",
76
+ "SynapseOneError",
77
+ "WorkflowService",
78
+ "data_to_object",
79
+ "normalize_mapping",
80
+ "object_to_data",
81
+ "trigger_webhook",
82
+ "unwrap_executions",
83
+ ]
84
+
85
+
86
+ class SynapseOneClient:
87
+ """One client, one HTTP layer, seven service namespaces.
88
+
89
+ ``base_url`` points at the gateway (``http://localhost:8080``) or a single
90
+ service origin. ``tenant`` is sent as ``x-synapseone-tenant`` and is only
91
+ required by platform callers of faststore/queue.
92
+ """
93
+
94
+ def __init__(
95
+ self,
96
+ base_url: str,
97
+ token: str | None = None,
98
+ api_key: str | None = None,
99
+ timeout: int = 15,
100
+ tenant: str | None = None,
101
+ ) -> None:
102
+ self._http = HttpClient(
103
+ base_url, token=token, api_key=api_key, tenant=tenant, timeout=timeout
104
+ )
105
+ self.iam = IamService(self._http)
106
+ self.workflow = WorkflowService(self._http)
107
+ self.ai = AiService(self._http)
108
+ self.lds = LdsService(self._http)
109
+ self.base = BaseService(self._http)
110
+ self.faststore = FaststoreService(self._http)
111
+ self.queue = QueueService(self._http)
112
+ self.stream = StreamService(self._http)
113
+ self.inference = InferenceService(self._http)
114
+
115
+ # ---- transport knobs (also mirrored by JS setToken/setApiKey and Go) ----
116
+ @property
117
+ def base_url(self) -> str:
118
+ return self._http.base_url
119
+
120
+ @base_url.setter
121
+ def base_url(self, value: str) -> None:
122
+ self._http.base_url = value.rstrip("/")
123
+
124
+ @property
125
+ def token(self) -> str | None:
126
+ return self._http.token
127
+
128
+ @token.setter
129
+ def token(self, value: str | None) -> None:
130
+ self._http.token = value
131
+
132
+ @property
133
+ def api_key(self) -> str | None:
134
+ return self._http.api_key
135
+
136
+ @api_key.setter
137
+ def api_key(self, value: str | None) -> None:
138
+ self._http.api_key = value
139
+
140
+ @property
141
+ def tenant(self) -> str | None:
142
+ return self._http.tenant
143
+
144
+ @tenant.setter
145
+ def tenant(self, value: str | None) -> None:
146
+ self._http.tenant = value
147
+
148
+ @property
149
+ def timeout(self) -> int:
150
+ return self._http.timeout
151
+
152
+ @timeout.setter
153
+ def timeout(self, value: int) -> None:
154
+ self._http.timeout = value
155
+
156
+ def set_token(self, token: str) -> None:
157
+ """Set/replace the Bearer JWT (mirrors JS setToken / Go SetToken)."""
158
+ self.token = token
159
+
160
+ def set_api_key(self, api_key: str) -> None:
161
+ """Set/replace the X-API-Key (mirrors JS setApiKey / Go SetAPIKey)."""
162
+ self.api_key = api_key
163
+
164
+ def call(self, method: str, path: str, body: Any | None = None) -> Any:
165
+ """Generic escape hatch (forward-compat with endpoints added after this SDK).
166
+
167
+ method is e.g. 'GET'/'POST'/'PUT'/'DELETE'; path must start with '/'.
168
+ """
169
+ verb = (method or "GET").upper()
170
+ if not path.startswith("/"):
171
+ path = "/" + path
172
+ if verb in ("GET", "HEAD"):
173
+ return self._http.request(verb, path)
174
+ return self._http.request(verb, path, body)
175
+
176
+
177
+ # ---------------------------------------------------------------------------
178
+ # deprecated flat wrappers (legacy merged API)
179
+ # ---------------------------------------------------------------------------
180
+
181
+ _IAM_METHODS = (
182
+ "login",
183
+ "refresh",
184
+ "logout",
185
+ "logout_all",
186
+ "verify",
187
+ "me",
188
+ "oidc_config",
189
+ "oidc_start",
190
+ "oidc_callback",
191
+ "list_orgs",
192
+ "create_org",
193
+ "create_account",
194
+ "list_accounts",
195
+ "create_user",
196
+ "list_users",
197
+ "user_policies",
198
+ "authorize",
199
+ "attach_policy",
200
+ "detach_policy",
201
+ "role_policies",
202
+ "assign_role",
203
+ "unassign_role",
204
+ "user_roles",
205
+ "simulate",
206
+ "list_audit",
207
+ "create_api_key",
208
+ "revoke_api_key",
209
+ "list_api_keys",
210
+ "verify_api_key",
211
+ "create_service_account",
212
+ "list_service_accounts",
213
+ "list_roles",
214
+ "create_role",
215
+ "delete_role",
216
+ "list_policies",
217
+ "create_policy",
218
+ "delete_policy",
219
+ "service_account_token",
220
+ )
221
+
222
+ _WORKFLOW_METHODS = (
223
+ "list_templates",
224
+ "get_template",
225
+ "get_workflow",
226
+ "activate_workflow",
227
+ "deactivate_workflow",
228
+ "execute_workflow",
229
+ "list_workflows",
230
+ "create_workflow_from_template",
231
+ "create_workflow",
232
+ "update_workflow",
233
+ "delete_workflow",
234
+ "list_executions",
235
+ "get_execution",
236
+ "test_run",
237
+ "create_credential",
238
+ "list_credentials",
239
+ "get_credential",
240
+ "delete_credential",
241
+ "rotate_credential",
242
+ )
243
+
244
+ _LDS_METHODS = {
245
+ "list_lds_licenses": "list_licenses",
246
+ "list_lds_datasources": "list_datasources",
247
+ "create_lds_datasource": "create_datasource",
248
+ "get_lds_datasource": "get_datasource",
249
+ "update_lds_datasource": "update_datasource",
250
+ "delete_lds_datasource": "delete_datasource",
251
+ "get_lds_datasource_attributes": "get_datasource_attributes",
252
+ "set_lds_datasource_attributes": "set_datasource_attributes",
253
+ "ingest_lds_records": "ingest_records",
254
+ "list_lds_entities": "list_entities",
255
+ "get_lds_entity": "get_entity",
256
+ "get_lds_datasource_stats": "get_datasource_stats",
257
+ }
258
+
259
+
260
+ def _deprecated(service: str, legacy: str, target: str):
261
+ replacement = f"client.{service}.{target}()"
262
+
263
+ def wrapper(self: SynapseOneClient, *args: Any, **kwargs: Any) -> Any:
264
+ warnings.warn(
265
+ f"SynapseOneClient.{legacy}() is deprecated; use {replacement} instead",
266
+ DeprecationWarning,
267
+ stacklevel=2,
268
+ )
269
+ return getattr(getattr(self, service), target)(*args, **kwargs)
270
+
271
+ wrapper.__name__ = legacy
272
+ wrapper.__qualname__ = f"SynapseOneClient.{legacy}"
273
+ wrapper.__doc__ = f"Deprecated alias for ``{replacement}``."
274
+ return wrapper
275
+
276
+
277
+ for _name in _IAM_METHODS:
278
+ setattr(SynapseOneClient, _name, _deprecated("iam", _name, _name))
279
+ for _name in _WORKFLOW_METHODS:
280
+ setattr(SynapseOneClient, _name, _deprecated("workflow", _name, _name))
281
+ for _legacy, _target in _LDS_METHODS.items():
282
+ setattr(SynapseOneClient, _legacy, _deprecated("lds", _legacy, _target))
283
+ setattr(SynapseOneClient, "ai_agent", _deprecated("ai", "ai_agent", "agent"))
284
+
285
+
286
+ # ---------------------------------------------------------------------------
287
+ # SynapseDX aliases (rebrand window: same classes, old import names keep working)
288
+ # ---------------------------------------------------------------------------
289
+
290
+ SynapseDXClient = SynapseOneClient
291
+ SynapseDXError = SynapseOneError
@@ -0,0 +1,105 @@
1
+ # Copyright 2026 SynapseOne Contributors
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ """Shared HTTP plumbing for the synapseone SDK.
4
+
5
+ Every service module (iam, workflow, lds, base, faststore, queue) talks to the
6
+ platform through one :class:`HttpClient`, which adds auth headers, the optional
7
+ platform tenant header and maps every failure (network or HTTP) to
8
+ :class:`SynapseOneError`.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any
14
+
15
+ import requests
16
+
17
+ # Sentinel for "argument was not provided", distinct from None (an explicit
18
+ # null must be distinguishable from "leave untouched" in partial updates).
19
+ UNSET = object()
20
+
21
+
22
+ class SynapseOneError(Exception):
23
+ """All SDK errors surface as SynapseOneError (network or HTTP).
24
+
25
+ ``status`` carries the HTTP status code when the failure came from a JSON
26
+ response (None for network errors) and ``body`` carries the parsed error
27
+ body when one was returned, so callers can branch on codes such as
28
+ ``not_found`` or ``version_conflict``.
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ message: str,
34
+ *,
35
+ status: int | None = None,
36
+ body: Any | None = None,
37
+ ) -> None:
38
+ super().__init__(message)
39
+ self.status = status
40
+ self.body = body
41
+
42
+
43
+ class HttpClient:
44
+ """Thin request layer shared by all SynapseOne services.
45
+
46
+ ``base_url`` points at the gateway (``http://localhost:8080``) or directly
47
+ at a service origin. ``tenant`` is only needed by platform callers of the
48
+ tenant-scoped services (faststore/queue) and is sent as
49
+ ``x-synapseone-tenant``.
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ base_url: str,
55
+ token: str | None = None,
56
+ api_key: str | None = None,
57
+ tenant: str | None = None,
58
+ timeout: int = 15,
59
+ ) -> None:
60
+ self.base_url = base_url.rstrip("/")
61
+ self.token = token
62
+ self.api_key = api_key
63
+ self.tenant = tenant
64
+ self.timeout = timeout
65
+
66
+ def headers(self) -> dict[str, str]:
67
+ h = {"Content-Type": "application/json"}
68
+ if self.token:
69
+ h["Authorization"] = f"Bearer {self.token}"
70
+ if self.api_key:
71
+ h["X-API-Key"] = self.api_key
72
+ if self.tenant:
73
+ h["x-synapseone-tenant"] = self.tenant
74
+ return h
75
+
76
+ def request(self, method: str, path: str, body: Any | None = None) -> Any:
77
+ try:
78
+ r = requests.request(
79
+ (method or "GET").upper(),
80
+ self.base_url + path,
81
+ json=body,
82
+ headers=self.headers(),
83
+ timeout=self.timeout,
84
+ )
85
+ except Exception as e: # network, DNS, timeout...
86
+ raise SynapseOneError(f"synapseone network {path}: {e}") from e
87
+ if r.status_code >= 400:
88
+ raise SynapseOneError(
89
+ f"synapseone {r.status_code} {path}: {r.text[:500]}",
90
+ status=r.status_code,
91
+ body=_try_json(r),
92
+ )
93
+ if not r.text:
94
+ return None
95
+ try:
96
+ return r.json()
97
+ except ValueError:
98
+ return r.text # non-JSON body (parity with JS SDK text fallback)
99
+
100
+
101
+ def _try_json(r: Any) -> Any:
102
+ try:
103
+ return r.json()
104
+ except ValueError:
105
+ return None
synapseone_sdk/ai.py ADDED
@@ -0,0 +1,44 @@
1
+ # Copyright 2026 SynapseOne Contributors
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ """Synapse AI client: the guidance-only API assistant (``POST /v1/ai/agent``).
4
+
5
+ The agent maps a natural-language question to one catalogued SynapseOne
6
+ endpoint and returns the method/path plus a ready-to-try body. It never
7
+ executes the call itself — callers decide whether to run it.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any
13
+
14
+ from ._http import HttpClient
15
+
16
+
17
+ class AiService:
18
+ """Methods for the SynapseOne AI Assistant service (``/v1/ai``)."""
19
+
20
+ def __init__(self, http: HttpClient) -> None:
21
+ self._http = http
22
+
23
+ def agent(
24
+ self, question: str, history: list[dict[str, Any]] | None = None
25
+ ) -> dict[str, Any]:
26
+ """Ask the API assistant a question (1..2000 chars).
27
+
28
+ Parameters
29
+ ----------
30
+ question:
31
+ Natural language question about SynapseOne APIs.
32
+ history:
33
+ Optional list of prior turns (``[{"role": "user"|"ai", "text": "..."}]``).
34
+
35
+ Returns
36
+ -------
37
+ dict
38
+ Result containing ``ok``, ``method``, ``path``, ``auth``, ``pathValues``,
39
+ ``body``, ``explanation``, ``reason``, and optional ``guidance``.
40
+ """
41
+ body: dict[str, Any] = {"question": question}
42
+ if history:
43
+ body["history"] = history
44
+ return self._http.request("POST", "/v1/ai/agent", body)