riffsdk 0.1.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.
riffsdk/__init__.py ADDED
File without changes
File without changes
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ import http
4
+ import os
5
+ from collections.abc import Generator
6
+ from time import time
7
+ from typing import ClassVar
8
+
9
+ import httpx
10
+
11
+ FIREBASE_API_KEY = "AIzaSyAdgR9BGfQrV2fzndXZLZYgiRtpydlq8ug"
12
+
13
+
14
+ class _StaticTokenAuth(httpx.Auth):
15
+ """Bearer auth with a fixed token string."""
16
+
17
+ def __init__(self, token: str) -> None:
18
+ self._token = token
19
+
20
+ def auth_flow(
21
+ self, request: httpx.Request
22
+ ) -> Generator[httpx.Request, httpx.Response, None]:
23
+ request.headers["Authorization"] = f"Bearer {self._token}"
24
+ yield request
25
+
26
+
27
+ class RiffAuth(httpx.Auth):
28
+ """Firebase-based auth with automatic token refresh.
29
+
30
+ Uses a long-lived refresh token to obtain short-lived ID tokens
31
+ from Firebase, refreshing proactively before expiry.
32
+ """
33
+
34
+ requires_response_body = True
35
+ refresh_interval = 15 * 60
36
+ _default: ClassVar[RiffAuth | None] = None
37
+
38
+ def __init__(
39
+ self,
40
+ refresh_token: str,
41
+ *,
42
+ firebase_api_key: str = FIREBASE_API_KEY,
43
+ ) -> None:
44
+ if not refresh_token:
45
+ raise ValueError("refresh_token must be non-empty")
46
+ self._firebase_api_key = firebase_api_key
47
+ self._refresh_token = refresh_token
48
+ self._access_token = ""
49
+ self._last_refresh = time() - 2 * self.refresh_interval
50
+
51
+ @classmethod
52
+ def default(cls) -> RiffAuth:
53
+ if cls._default is None:
54
+ cls._default = cls.from_env()
55
+ return cls._default
56
+
57
+ @classmethod
58
+ def from_env(
59
+ cls,
60
+ *,
61
+ env_var: str = "RIFF_TOKEN",
62
+ firebase_api_key: str = FIREBASE_API_KEY,
63
+ ) -> RiffAuth:
64
+ token = os.environ.get(env_var)
65
+ if token is None:
66
+ raise RuntimeError(f"Missing environment variable: {env_var}")
67
+ return cls(token, firebase_api_key=firebase_api_key)
68
+
69
+ def _token_expires_soon(self) -> bool:
70
+ return time() - self._last_refresh > self.refresh_interval
71
+
72
+ def _build_refresh_request(self) -> httpx.Request:
73
+ return httpx.Request(
74
+ method="POST",
75
+ url=f"https://securetoken.googleapis.com/v1/token?key={self._firebase_api_key}",
76
+ data={
77
+ "grant_type": "refresh_token",
78
+ "refresh_token": self._refresh_token,
79
+ },
80
+ )
81
+
82
+ def _handle_refresh_response(self, response: httpx.Response) -> None:
83
+ if response.status_code == http.HTTPStatus.OK:
84
+ self._access_token = response.json()["id_token"]
85
+ self._last_refresh = time()
86
+ else:
87
+ raise RuntimeError("Token refresh failed")
88
+
89
+ def auth_flow(
90
+ self, request: httpx.Request
91
+ ) -> Generator[httpx.Request, httpx.Response, None]:
92
+ if self._token_expires_soon():
93
+ refresh_response = yield self._build_refresh_request()
94
+ self._handle_refresh_response(refresh_response)
95
+
96
+ request.headers["Authorization"] = f"Bearer {self._access_token}"
97
+ yield request
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+
6
+ def default_base_url() -> str:
7
+ project_id = os.environ.get("RIFF_PROJECT_ID")
8
+ if project_id is None:
9
+ raise RuntimeError("Missing environment variable: RIFF_PROJECT_ID")
10
+ return f"https://api.riff.new/_projects/{project_id}/apis/riff-api"
@@ -0,0 +1,59 @@
1
+ from riffsdk._internal.auth import RiffAuth
2
+ from riffsdk.storage._async_client import AsyncLease, AsyncStorageClient
3
+ from riffsdk.storage._client import Lease, StorageClient
4
+ from riffsdk.storage._models import (
5
+ DownloadResult,
6
+ ListPage,
7
+ ObjectMeta,
8
+ Scope,
9
+ UploadPart,
10
+ UploadResult,
11
+ account_scope,
12
+ project_scope,
13
+ session_scope,
14
+ )
15
+ from riffsdk.storage._streaming import StorageReader, StorageWriter
16
+ from riffsdk.storage._upload import AsyncResumableUpload, ResumableUpload
17
+ from riffsdk.storage.exceptions import (
18
+ AlreadyExistsError,
19
+ AuthorisationError,
20
+ LeaseConflictError,
21
+ ObjectNotFoundError,
22
+ PartMismatchError,
23
+ QuotaExceededError,
24
+ StorageError,
25
+ StorageTransportError,
26
+ UploadNotFoundError,
27
+ VersionConflictError,
28
+ )
29
+
30
+ __all__ = [
31
+ "AsyncLease",
32
+ "AsyncResumableUpload",
33
+ "RiffAuth",
34
+ "AsyncStorageClient",
35
+ "DownloadResult",
36
+ "Lease",
37
+ "ResumableUpload",
38
+ "Scope",
39
+ "StorageClient",
40
+ "ObjectMeta",
41
+ "UploadPart",
42
+ "ListPage",
43
+ "UploadResult",
44
+ "StorageReader",
45
+ "StorageWriter",
46
+ "StorageError",
47
+ "AuthorisationError",
48
+ "ObjectNotFoundError",
49
+ "VersionConflictError",
50
+ "AlreadyExistsError",
51
+ "LeaseConflictError",
52
+ "UploadNotFoundError",
53
+ "QuotaExceededError",
54
+ "PartMismatchError",
55
+ "StorageTransportError",
56
+ "account_scope",
57
+ "project_scope",
58
+ "session_scope",
59
+ ]