volcano-sdk-python 0.9.1__py3-none-any.whl → 0.9.3__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.
volcano_sdk/_transport.py CHANGED
@@ -99,10 +99,12 @@ from ._generated.api.database_queries.query_database_update import (
99
99
  from ._generated.api.database_queries.query_database_update import (
100
100
  _get_kwargs as database_update_kwargs,
101
101
  )
102
- from ._generated.api.functions import resolve_function_for_invocation
103
102
  from ._generated.api.functions.invoke_function import (
104
103
  _get_kwargs as invoke_function_kwargs,
105
104
  )
105
+ from ._generated.api.functions.resolve_function_for_invocation import (
106
+ _get_kwargs as resolve_function_kwargs,
107
+ )
106
108
  from ._generated.api.locks import (
107
109
  force_release_project_lock,
108
110
  get_project_lock,
@@ -1643,11 +1645,10 @@ class GeneratedTransport:
1643
1645
  name: str,
1644
1646
  ) -> TransportResponse:
1645
1647
  with self._client(authorization) as client:
1646
- response = resolve_function_for_invocation.sync_detailed(
1647
- client=client,
1648
- name=name,
1648
+ response = client.get_httpx_client().request(
1649
+ **resolve_function_kwargs(name=name)
1649
1650
  )
1650
- return self._response(response)
1651
+ return self._raw_response(response)
1651
1652
 
1652
1653
  def invoke_function(
1653
1654
  self,
volcano_sdk/functions.py CHANGED
@@ -4,14 +4,19 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import re
7
- from collections.abc import Mapping
8
- from typing import TYPE_CHECKING, Protocol, cast
7
+ from collections.abc import Callable, Mapping
8
+ from typing import TYPE_CHECKING, Protocol, TypeVar, cast
9
9
 
10
10
  from . import _function_resolution
11
11
  from ._function_resolution import FunctionResolution
12
12
  from ._transport import TransportResponse, invoke, response_payload
13
- from .errors import NotFoundError
14
- from .models import FunctionResponse, JSONValue
13
+ from .errors import (
14
+ AuthenticationError,
15
+ NotFoundError,
16
+ SessionChangedError,
17
+ VolcanoError,
18
+ )
19
+ from .models import FunctionResponse, JSONValue, _freeze_json
15
20
 
16
21
  if TYPE_CHECKING:
17
22
  from .client import VolcanoClient
@@ -27,10 +32,57 @@ _UNKNOWN_FUNCTION = "Function was not found"
27
32
  _HTTP_SUCCESS_MIN = 200
28
33
  _HTTP_SUCCESS_MAX = 300
29
34
  _HTTP_NOT_FOUND = 404
35
+ _HTTP_UNAUTHORIZED = 401
30
36
  # Present only once the platform has dispatched to the function.
31
37
  _FUNCTION_INVOKED_HEADER = "X-Volcano-Function-Invoked"
32
38
 
33
39
 
40
+ _Result = TypeVar("_Result")
41
+
42
+
43
+ class _FunctionAuth:
44
+ def __init__(self, client: VolcanoClient) -> None:
45
+ self._client = client
46
+ self._binding = client._capture_session_binding()
47
+ self._fallback_token = client._function_token()
48
+
49
+ def run(self, operation: Callable[[str], _Result]) -> _Result:
50
+ if self._binding[2] is not None:
51
+ self._binding = self._client.auth._owned_refresh_session(self._binding)
52
+ try:
53
+ return self._run(operation)
54
+ finally:
55
+ if self._binding[2] is not None:
56
+ self._client.auth._validate_read_failure(self._binding)
57
+ elif self._client._capture_session_binding()[1] != self._binding[1]:
58
+ raise SessionChangedError
59
+
60
+ def _run(self, operation: Callable[[str], _Result]) -> _Result:
61
+ try:
62
+ return operation(self._token())
63
+ except AuthenticationError as original:
64
+ if self._binding[2] is None or original.status != _HTTP_UNAUTHORIZED:
65
+ raise
66
+ try:
67
+ # Resolve has released its cache lock before refresh callbacks run.
68
+ self._client.auth._refresh_session_for_binding(self._binding)
69
+ except SessionChangedError:
70
+ raise
71
+ except VolcanoError:
72
+ raise original from None
73
+ return operation(self._token())
74
+
75
+ def _token(self) -> str:
76
+ if self._binding[2] is None:
77
+ if self._client._capture_session_binding()[1] != self._binding[1]:
78
+ raise SessionChangedError
79
+ return self._fallback_token
80
+ session = self._client.auth._owned_refresh_session(self._binding)[2]
81
+ if session is None:
82
+ raise SessionChangedError
83
+ return session.access_token
84
+
85
+
34
86
  class FunctionsTransport(Protocol):
35
87
  """Transport operations required by the functions facade."""
36
88
 
@@ -77,13 +129,17 @@ class Functions:
77
129
  payload: Mapping[str, JSONValue] | None = None,
78
130
  ) -> FunctionResponse:
79
131
  """Resolve and invoke a function with an optional JSON object payload."""
132
+ auth = _FunctionAuth(self._client)
80
133
  name = _function_name(name)
81
134
  request_payload = _function_payload(payload)
82
- authorization = self._client._function_token()
83
135
  transport = cast("FunctionsTransport", self._client._transport)
84
- resolution = self._resolve(transport, authorization, name)
85
- response = self._invoke_resolved(
86
- transport, authorization, resolution, request_payload
136
+ authorization, resolution = auth.run(
137
+ lambda token: (token, self._resolve(transport, token, name))
138
+ )
139
+ response = auth.run(
140
+ lambda token: self._invoke_resolved(
141
+ transport, token, resolution, request_payload
142
+ )
87
143
  )
88
144
  if _stale_mapping(response):
89
145
  # The function was deleted and recreated, so the cached identity no
@@ -91,9 +147,13 @@ class Functions:
91
147
  _function_resolution.forget(
92
148
  self._client._api_base_url(), authorization, name
93
149
  )
94
- resolution = self._resolve(transport, authorization, name)
95
- response = self._invoke_resolved(
96
- transport, authorization, resolution, request_payload
150
+ _, resolution = auth.run(
151
+ lambda token: (token, self._resolve(transport, token, name))
152
+ )
153
+ response = auth.run(
154
+ lambda token: self._invoke_resolved(
155
+ transport, token, resolution, request_payload
156
+ )
97
157
  )
98
158
  return self._response(response)
99
159
 
@@ -118,7 +178,13 @@ class Functions:
118
178
  invoke_url=resolution.invoke_url,
119
179
  payload=payload,
120
180
  )
121
- return cast("TransportResponse", response)
181
+ response = cast("TransportResponse", response)
182
+ if (
183
+ response.status_code == _HTTP_UNAUTHORIZED
184
+ and _header(response.headers, _FUNCTION_INVOKED_HEADER) is None
185
+ ):
186
+ response_payload(response, _HTTP_SUCCESS_MIN)
187
+ return response
122
188
 
123
189
  def _resolve(
124
190
  self,
@@ -269,9 +335,9 @@ def _function_name(value: object) -> str:
269
335
  return value
270
336
 
271
337
 
272
- def _function_payload(value: object) -> dict[str, JSONValue]:
338
+ def _function_payload(value: object) -> Mapping[str, JSONValue]:
273
339
  if value is None:
274
340
  return {}
275
341
  if not isinstance(value, Mapping):
276
342
  raise TypeError(_INVALID_FUNCTION_PAYLOAD)
277
- return dict(cast("Mapping[str, JSONValue]", value))
343
+ return cast("Mapping[str, JSONValue]", _freeze_json(cast("JSONValue", value)))
volcano_sdk/realtime.py CHANGED
@@ -768,7 +768,6 @@ class Channel:
768
768
  not self._fetch_config.enabled
769
769
  or change.mode != "lightweight"
770
770
  or change.type == "DELETE"
771
- or change.schema != "public"
772
771
  or change.id is None
773
772
  or database_name is None
774
773
  ):
@@ -776,7 +775,11 @@ class Channel:
776
775
  return _PostgresFetchRequest(
777
776
  database_name=database_name,
778
777
  access_token=self._realtime._connection_token(),
779
- table=change.table,
778
+ table=(
779
+ change.table
780
+ if change.schema == "public"
781
+ else f"{change.schema}.{change.table}"
782
+ ),
780
783
  row_id=change.id,
781
784
  )
782
785
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: volcano-sdk-python
3
- Version: 0.9.1
3
+ Version: 0.9.3
4
4
  Summary: Official Python SDK for Volcano
5
5
  Project-URL: Homepage, https://volcano.dev
6
6
  Project-URL: Repository, https://github.com/Kong/volcano-sdk-python
@@ -79,14 +79,18 @@ function = client.functions.invoke(
79
79
  )
80
80
  print(function.status, function.version, function.data)
81
81
 
82
- logs = client.logs.search(
82
+ # Project logs use a control-plane project token, not this end-user session.
83
+ logs_client = VolcanoClient(
84
+ anon_key="ak_your_anon_key", access_token="vpat_your_project_token"
85
+ )
86
+ logs = logs_client.logs.search(
83
87
  "00000000-0000-4000-8000-000000000001",
84
88
  {"resource": {"type": "function"}, "limit": 100},
85
89
  )
86
90
  for event in logs.data:
87
91
  print(event["timestamp"], event["body"])
88
92
 
89
- activity = client.logs.activity(
93
+ activity = logs_client.logs.activity(
90
94
  "00000000-0000-4000-8000-000000000001",
91
95
  {"resource": {"type": "function"}, "bucket_count": 24},
92
96
  )
@@ -220,13 +224,23 @@ user session; the function receives no user identity. The immutable result
220
224
  includes the response body, status, headers, and `X-Volcano-Version`. The body
221
225
  can be a JSON object, array, scalar, or text; an empty body returns `None`.
222
226
  JSON arrays become immutable tuples. Invalid JSON is returned as text. A
223
- function's own non-2xx response is returned when the version header proves it
224
- ran; platform failures raise typed SDK errors.
227
+ function's own non-2xx response is returned when Volcano confirms it ran;
228
+ non-success platform HTTP responses raise typed SDK errors.
229
+
230
+
231
+ Function resolution and invocation recover from a platform HTTP 401 before dispatch:
232
+ the SDK refreshes the captured session and retries the rejected request once.
233
+ Concurrent calls share successful recovery. Replacing or signing out that session
234
+ prevents replay under another identity. The call preserves its original payload values.
235
+ A function's own response, HTTP 403, or a network failure never triggers this retry.
236
+ Anonymous and service keys do not refresh.
225
237
 
226
238
  `logs.search()` returns an immutable page of retained runtime or deployment log
227
239
  events. Pass `next_cursor` back as `cursor` to continue a search. `logs.activity()`
228
240
  returns immutable time buckets using the same resource selector and query syntax.
229
- Both methods require an active user session.
241
+ Both methods require a platform user token or a project access token.
242
+ A `read_only` project token is sufficient; end-user sessions cannot read project logs.
243
+ See the [logs guide](https://github.com/Kong/volcano-sdk-python/blob/main/docs/logs.md).
230
244
 
231
245
  Database selects, inserts, updates, deletes, log reads, and authenticated storage
232
246
  requests (including upload sessions and parts) refresh the captured
@@ -784,12 +798,12 @@ stop_changes()
784
798
  ```
785
799
 
786
800
  Binding a database automatically fetches the matching row for lightweight
787
- `INSERT` and `UPDATE` notifications in the `public` schema. The fetch uses the
801
+ `INSERT` and `UPDATE` notifications in public or custom schemas. The fetch uses the
788
802
  realtime connection's RLS-scoped access token. Compatible row lookups are
789
803
  batched while callback delivery preserves publication order.
790
804
  If the row is absent or the query fails, the callback receives the lightweight
791
- notification with its `id` and `mode` intact. Non-public schemas also retain
792
- that lightweight form. Lightweight deletes never query the database; they
805
+ notification with its `id` and `mode` intact. Custom-schema row lookups preserve
806
+ the schema from the notification. Lightweight deletes never query the database; they
793
807
  preserve `old_record`, or provide `{"id": change.id}` when no old row was
794
808
  included. Tune a channel's batching with `fetch_batch_window_ms` and
795
809
  `fetch_max_batch_size`; the defaults are 20 milliseconds and 50 rows. Set
@@ -6,18 +6,18 @@ volcano_sdk/_lock_worker.py,sha256=QhSmwR0WcwgT-7ueJMlbCogLqOakQ5eRAtyWV74fgV0,3
6
6
  volcano_sdk/_realtime_fetch_worker.py,sha256=zQTgFF4cIUBeAL4J2OpWAlz_sIynv2BLfu588EogAdo,9269
7
7
  volcano_sdk/_session.py,sha256=LYCv2NCDlL5Slh2YLEG8VQ8z-kDaKDYWW4vGdMvUf1U,2474
8
8
  volcano_sdk/_session_operations.py,sha256=1vI9gtwjxG2eUGT-nIYnMEgRldJqgIj2lRLukBbG64E,5518
9
- volcano_sdk/_transport.py,sha256=AFd47C3X1WAHwq3f9V2ux0WNzwGIn4NBxrwgaIN-xl0,58296
9
+ volcano_sdk/_transport.py,sha256=ox_wKKEW42gm2307uK9vIiQFB5l8pzb0ifWXxkzPiHQ,58331
10
10
  volcano_sdk/auth.py,sha256=iKq8o0K8geVKXrnLzhvOjAQoX_Nd0yQDJqDH4bemWaM,47366
11
11
  volcano_sdk/client.py,sha256=CJJnf_iTcXnyY2Y57LXJVAn39mTUsepblUg9op-Thzo,12283
12
12
  volcano_sdk/connection_string.py,sha256=LlTZigsrCF4IfAqQ8SDTWJv35hnnzNu1MfeVv5JW-Kk,2331
13
13
  volcano_sdk/database.py,sha256=p_5OaqlBjKAtIUfKQCflXM_CGqokrVmpOMsp6KHiJ8A,9493
14
14
  volcano_sdk/errors.py,sha256=ffs66FIN_jhyh5SMj7KdXb-zVV3DSgVK6xJEPNA5h9Y,1613
15
- volcano_sdk/functions.py,sha256=zDeAKyCieNnI6wzb9iZMlg9_cbjaYWXYbXHb7jzCle4,10126
15
+ volcano_sdk/functions.py,sha256=bk523Ss80nREwMD1wk6_pO2XKqOIXqDKylq0fQvMmb0,12540
16
16
  volcano_sdk/locks.py,sha256=eSkidfHQ_wml5-1LT2hBtHxeALP6E1cGYK0FzHjbrNk,9241
17
17
  volcano_sdk/logs.py,sha256=lxORRQBvsnJ0BdMau19MXrIeig5_mEucQSyJ4F1CZxU,4666
18
18
  volcano_sdk/models.py,sha256=n5EdzkWzmOstXsuFlvfGOILRemQAqG2P4VCLMelW8SA,8690
19
19
  volcano_sdk/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
20
- volcano_sdk/realtime.py,sha256=3p5ueLCeveaR4_ItqlLv-exkMCODqxDQNHj1aYB_YE4,59506
20
+ volcano_sdk/realtime.py,sha256=KJbEMEpCYqlbE4YYtu6Wt5YM379LQmWtXU-wRhni1T0,59597
21
21
  volcano_sdk/storage.py,sha256=p16OV5ADI6JjMAJ7gx2gcmwk9t8--YiIZcOSLQLVlDY,26238
22
22
  volcano_sdk/_generated/__init__.py,sha256=ZlB8wMYdN6EjpUS5W_lX3jsxq9G1kOcmUr-j3Fccfn8,164
23
23
  volcano_sdk/_generated/client.py,sha256=GDzw6T2uRmPlUb1DImpBGxwy0dNbhmBknfvvIQW0GuU,12387
@@ -775,7 +775,7 @@ volcano_sdk/_generated/models/usage_data_point.py,sha256=d4U08JbVTiIMM_l2BmRILL1
775
775
  volcano_sdk/_generated/models/variable.py,sha256=tvGDOMv77gW6j5skufnfrlNAhzCzA1F7nuPCQWx79yY,5926
776
776
  volcano_sdk/_generated/models/variable_deploy_source.py,sha256=G_aCiFHzQx8WhJFs0GvYZEFclfTrfkDvd3kT1e3vty8,463
777
777
  volcano_sdk/_generated/models/variable_status.py,sha256=THSsKBSiAYG4kAtBzGA4TZqxjgx8hdL0Q4qCdZfOj00,391
778
- volcano_sdk_python-0.9.1.dist-info/METADATA,sha256=zPhzqaYqvRyeIoZlgMAlmRbvskDedIxy6kclHDME__4,35125
779
- volcano_sdk_python-0.9.1.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
780
- volcano_sdk_python-0.9.1.dist-info/licenses/LICENSE,sha256=AIHJy-5MFqM8fHfuGFTTb9a8rvJGypcVgN4aZ-7XTWw,11387
781
- volcano_sdk_python-0.9.1.dist-info/RECORD,,
778
+ volcano_sdk_python-0.9.3.dist-info/METADATA,sha256=AmqFIPDJDOmZgEWKZUIn0jW8EK4ITcvAG92gKMDl1Mk,36008
779
+ volcano_sdk_python-0.9.3.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
780
+ volcano_sdk_python-0.9.3.dist-info/licenses/LICENSE,sha256=AIHJy-5MFqM8fHfuGFTTb9a8rvJGypcVgN4aZ-7XTWw,11387
781
+ volcano_sdk_python-0.9.3.dist-info/RECORD,,