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 +0 -0
- riffsdk/_internal/__init__.py +0 -0
- riffsdk/_internal/auth.py +97 -0
- riffsdk/_internal/config.py +10 -0
- riffsdk/storage/__init__.py +59 -0
- riffsdk/storage/_async_client.py +555 -0
- riffsdk/storage/_client.py +586 -0
- riffsdk/storage/_http.py +351 -0
- riffsdk/storage/_models.py +68 -0
- riffsdk/storage/_session.py +33 -0
- riffsdk/storage/_streaming.py +177 -0
- riffsdk/storage/_upload.py +229 -0
- riffsdk/storage/exceptions.py +87 -0
- riffsdk/storage/py.typed +0 -0
- riffsdk-0.1.0.dist-info/METADATA +9 -0
- riffsdk-0.1.0.dist-info/RECORD +18 -0
- riffsdk-0.1.0.dist-info/WHEEL +4 -0
- riffsdk-0.1.0.dist-info/licenses/LICENCE +7 -0
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
|
+
]
|