supabase-rb 2.0.0
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.
- checksums.yaml +7 -0
- data/lib/supabase/README.md +90 -0
- data/lib/supabase/auth/README.md +172 -0
- data/lib/supabase/auth/admin_api.rb +218 -0
- data/lib/supabase/auth/admin_oauth_api.rb +51 -0
- data/lib/supabase/auth/api.rb +125 -0
- data/lib/supabase/auth/async/admin_api.rb +36 -0
- data/lib/supabase/auth/async/admin_oauth_api.rb +15 -0
- data/lib/supabase/auth/async/api.rb +32 -0
- data/lib/supabase/auth/async/client.rb +33 -0
- data/lib/supabase/auth/async.rb +14 -0
- data/lib/supabase/auth/client.rb +1217 -0
- data/lib/supabase/auth/constants.rb +32 -0
- data/lib/supabase/auth/errors.rb +207 -0
- data/lib/supabase/auth/helpers.rb +222 -0
- data/lib/supabase/auth/memory_storage.rb +25 -0
- data/lib/supabase/auth/storage.rb +19 -0
- data/lib/supabase/auth/timer.rb +40 -0
- data/lib/supabase/auth/types.rb +517 -0
- data/lib/supabase/auth/version.rb +7 -0
- data/lib/supabase/auth.rb +19 -0
- data/lib/supabase/client.rb +200 -0
- data/lib/supabase/client_options.rb +82 -0
- data/lib/supabase/functions/README.md +71 -0
- data/lib/supabase/functions/async/client.rb +45 -0
- data/lib/supabase/functions/async.rb +8 -0
- data/lib/supabase/functions/client.rb +174 -0
- data/lib/supabase/functions/errors.rb +38 -0
- data/lib/supabase/functions/types.rb +37 -0
- data/lib/supabase/functions/version.rb +7 -0
- data/lib/supabase/functions.rb +11 -0
- data/lib/supabase/postgrest/README.md +84 -0
- data/lib/supabase/postgrest/async/client.rb +50 -0
- data/lib/supabase/postgrest/async.rb +8 -0
- data/lib/supabase/postgrest/client.rb +136 -0
- data/lib/supabase/postgrest/errors.rb +49 -0
- data/lib/supabase/postgrest/request_builder.rb +657 -0
- data/lib/supabase/postgrest/types.rb +60 -0
- data/lib/supabase/postgrest/utils.rb +24 -0
- data/lib/supabase/postgrest/version.rb +7 -0
- data/lib/supabase/postgrest.rb +13 -0
- data/lib/supabase/realtime/README.md +90 -0
- data/lib/supabase/realtime/channel.rb +274 -0
- data/lib/supabase/realtime/client.rb +182 -0
- data/lib/supabase/realtime/errors.rb +19 -0
- data/lib/supabase/realtime/message.rb +38 -0
- data/lib/supabase/realtime/presence.rb +136 -0
- data/lib/supabase/realtime/push.rb +48 -0
- data/lib/supabase/realtime/socket.rb +40 -0
- data/lib/supabase/realtime/sockets/async_websocket.rb +175 -0
- data/lib/supabase/realtime/sockets/websocket_client_simple.rb +94 -0
- data/lib/supabase/realtime/test_socket.rb +65 -0
- data/lib/supabase/realtime/transformers.rb +26 -0
- data/lib/supabase/realtime/types.rb +70 -0
- data/lib/supabase/realtime/version.rb +7 -0
- data/lib/supabase/realtime.rb +18 -0
- data/lib/supabase/storage/README.md +108 -0
- data/lib/supabase/storage/analytics.rb +69 -0
- data/lib/supabase/storage/async/client.rb +52 -0
- data/lib/supabase/storage/async.rb +8 -0
- data/lib/supabase/storage/bucket_api.rb +65 -0
- data/lib/supabase/storage/client.rb +80 -0
- data/lib/supabase/storage/errors.rb +32 -0
- data/lib/supabase/storage/file_api.rb +281 -0
- data/lib/supabase/storage/request.rb +63 -0
- data/lib/supabase/storage/types.rb +236 -0
- data/lib/supabase/storage/utils.rb +35 -0
- data/lib/supabase/storage/vectors.rb +189 -0
- data/lib/supabase/storage/version.rb +7 -0
- data/lib/supabase/storage.rb +17 -0
- data/lib/supabase/version.rb +5 -0
- data/lib/supabase-auth.rb +3 -0
- data/lib/supabase.rb +63 -0
- metadata +272 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "realtime/version"
|
|
4
|
+
require_relative "realtime/errors"
|
|
5
|
+
require_relative "realtime/types"
|
|
6
|
+
require_relative "realtime/transformers"
|
|
7
|
+
require_relative "realtime/message"
|
|
8
|
+
require_relative "realtime/presence"
|
|
9
|
+
require_relative "realtime/push"
|
|
10
|
+
require_relative "realtime/socket"
|
|
11
|
+
require_relative "realtime/channel"
|
|
12
|
+
require_relative "realtime/client"
|
|
13
|
+
require_relative "realtime/test_socket"
|
|
14
|
+
|
|
15
|
+
module Supabase
|
|
16
|
+
module Realtime
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# `supabase-storage`
|
|
2
|
+
|
|
3
|
+
Ruby client for [Supabase Storage](https://supabase.com/docs/guides/storage).
|
|
4
|
+
Bucket management, file upload/download, signed URLs, plus the Iceberg
|
|
5
|
+
(`analytics`) and vector bucket APIs. Mirrors the public surface of
|
|
6
|
+
[`storage3`](https://github.com/supabase/supabase-py/tree/main/src/storage)
|
|
7
|
+
in Python.
|
|
8
|
+
|
|
9
|
+
- Source: [github.com/supabase-rb/client](https://github.com/supabase-rb/client)
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem "supabase-storage"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then `bundle install`. (Requires Ruby >= 3.0.)
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require "supabase/storage"
|
|
23
|
+
|
|
24
|
+
storage = Supabase::Storage::Client.new(
|
|
25
|
+
base_url: "https://your-project.supabase.co/storage/v1",
|
|
26
|
+
headers: { "apikey" => key, "Authorization" => "Bearer #{token}" }
|
|
27
|
+
)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Bucket management
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
storage.create_bucket("avatars", public: true)
|
|
34
|
+
storage.list_buckets
|
|
35
|
+
storage.get_bucket("avatars")
|
|
36
|
+
storage.update_bucket("avatars", public: false)
|
|
37
|
+
storage.empty_bucket("avatars")
|
|
38
|
+
storage.delete_bucket("avatars")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### File operations
|
|
42
|
+
|
|
43
|
+
Scoped to one bucket via `.from`:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
bucket = storage.from("avatars")
|
|
47
|
+
bucket.upload("user1.png", File.binread("user1.png"), content_type: "image/png")
|
|
48
|
+
bucket.download("user1.png") # => bytes
|
|
49
|
+
bucket.list("folder/")
|
|
50
|
+
bucket.list_v2(prefix: "folder/", limit: 50, cursor: "abc", with_delimiter: true)
|
|
51
|
+
bucket.remove(["user1.png"])
|
|
52
|
+
bucket.move("user1.png", "archive/user1.png")
|
|
53
|
+
bucket.copy("user1.png", "backups/user1.png")
|
|
54
|
+
bucket.exists?("user1.png")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Upload accepts `String` (raw bytes), any `IO`, `StringIO`, or `Pathname`.
|
|
58
|
+
Multipart encoding is handled by `faraday-multipart`. Metadata Hashes are
|
|
59
|
+
base64-encoded into the `x-metadata` header automatically.
|
|
60
|
+
|
|
61
|
+
### Signed URLs
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
bucket.create_signed_url("user1.png", expires_in: 3600)
|
|
65
|
+
bucket.create_signed_urls(["user1.png", "user2.png"], expires_in: 3600)
|
|
66
|
+
bucket.get_public_url("user1.png")
|
|
67
|
+
|
|
68
|
+
# Signed upload URL — so a browser can upload directly to Storage
|
|
69
|
+
signed = bucket.create_signed_upload_url("user1.png")
|
|
70
|
+
bucket.upload_to_signed_url("user1.png", token: signed.token, file: bytes)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Analytics (Iceberg) buckets
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
storage.analytics.create("warehouse")
|
|
77
|
+
storage.analytics.list
|
|
78
|
+
storage.analytics.delete("warehouse")
|
|
79
|
+
cfg = storage.analytics.catalog("warehouse",
|
|
80
|
+
access_key_id: "AKIA", secret_access_key: "...")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Vector buckets
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
storage.vectors.create_bucket("embeddings")
|
|
87
|
+
storage.vectors.bucket("embeddings").create_index(
|
|
88
|
+
index_name: "docs", dimension: 1536, distance_metric: "cosine"
|
|
89
|
+
)
|
|
90
|
+
storage.vectors.bucket("embeddings").index("docs").put(records)
|
|
91
|
+
storage.vectors.bucket("embeddings").index("docs").query(vector, top_k: 10)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Async variant
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
require "supabase/storage/async"
|
|
98
|
+
|
|
99
|
+
async = Supabase::Storage::Async::Client.new(
|
|
100
|
+
base_url: ENV["SUPABASE_URL"] + "/storage/v1",
|
|
101
|
+
headers: { "apikey" => key, "Authorization" => "Bearer #{token}" }
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
Async do
|
|
105
|
+
bucket = async.from("avatars")
|
|
106
|
+
data = bucket.download("user1.png")
|
|
107
|
+
end
|
|
108
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "request"
|
|
4
|
+
require_relative "types"
|
|
5
|
+
|
|
6
|
+
module Supabase
|
|
7
|
+
module Storage
|
|
8
|
+
# Analytics (iceberg) bucket management. Mirrors storage3's
|
|
9
|
+
# `SyncStorageAnalyticsClient` — talks to `{storage_url}/iceberg/bucket`.
|
|
10
|
+
#
|
|
11
|
+
# client.analytics.create("my-iceberg")
|
|
12
|
+
# client.analytics.list(limit: 50, sort_column: "name", sort_order: "asc")
|
|
13
|
+
# client.analytics.delete("my-iceberg")
|
|
14
|
+
class AnalyticsClient
|
|
15
|
+
include Request
|
|
16
|
+
|
|
17
|
+
# @param session [Faraday::Connection]
|
|
18
|
+
# @param base_url [String] storage REST root (".../storage/v1/")
|
|
19
|
+
# @param headers [Hash]
|
|
20
|
+
def initialize(session, base_url, headers)
|
|
21
|
+
@session = session
|
|
22
|
+
normalized = base_url.end_with?("/") ? base_url : "#{base_url}/"
|
|
23
|
+
@base_url = "#{normalized}iceberg/"
|
|
24
|
+
@headers = headers
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def create(bucket_name)
|
|
28
|
+
body = _request(:post, ["bucket"], json: { "name" => bucket_name })
|
|
29
|
+
Types::AnalyticsBucket.from_hash(body)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Mirrors py's optional sort/search/pagination params. Nil values are
|
|
33
|
+
# dropped server-side; we also drop them client-side so the URL stays clean.
|
|
34
|
+
def list(limit: nil, offset: nil, sort_column: nil, sort_order: nil, search: nil)
|
|
35
|
+
params = { "limit" => limit, "offset" => offset,
|
|
36
|
+
"sort_column" => sort_column, "sort_order" => sort_order,
|
|
37
|
+
"search" => search }.compact
|
|
38
|
+
body = _request(:get, ["bucket"], query: params.empty? ? nil : params)
|
|
39
|
+
Array(body).map { |b| Types::AnalyticsBucket.from_hash(b) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def delete(bucket_name)
|
|
43
|
+
body = _request(:delete, ["bucket", bucket_name])
|
|
44
|
+
Types::AnalyticsBucketDeleteResponse.from_hash(body)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Python returns a `pyiceberg.RestCatalog`. There is no equivalent Iceberg
|
|
48
|
+
# client in the Ruby ecosystem, so we return the catalog configuration as
|
|
49
|
+
# a plain Hash that a downstream iceberg-ruby (when one exists) can consume.
|
|
50
|
+
# Keeping the public method present mirrors the API surface.
|
|
51
|
+
def catalog(catalog_name, access_key_id:, secret_access_key:)
|
|
52
|
+
service_key = @headers["apiKey"]
|
|
53
|
+
raise Errors::StorageApiError.new("apiKey must be passed in the headers.") if service_key.to_s.empty?
|
|
54
|
+
|
|
55
|
+
s3_endpoint = @base_url.sub(%r{iceberg/?\z}, "s3")
|
|
56
|
+
{
|
|
57
|
+
"name" => catalog_name,
|
|
58
|
+
"warehouse" => catalog_name,
|
|
59
|
+
"uri" => @base_url,
|
|
60
|
+
"token" => service_key,
|
|
61
|
+
"s3.endpoint" => s3_endpoint,
|
|
62
|
+
"s3.access-key-id" => access_key_id,
|
|
63
|
+
"s3.secret-access-key" => secret_access_key,
|
|
64
|
+
"s3.force-virtual-addressing" => "False"
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "async/http/faraday"
|
|
4
|
+
require_relative "../client"
|
|
5
|
+
|
|
6
|
+
module Supabase
|
|
7
|
+
module Storage
|
|
8
|
+
module Async
|
|
9
|
+
# Async counterpart to {Supabase::Storage::Client}.
|
|
10
|
+
#
|
|
11
|
+
# Inherits the full public surface (list_buckets, get_bucket, create_bucket,
|
|
12
|
+
# update_bucket, empty_bucket, delete_bucket, from/bucket → FileApi) and
|
|
13
|
+
# rewires only the Faraday adapter to async-http-faraday so HTTP I/O yields
|
|
14
|
+
# back to the {::Async} reactor instead of blocking the thread.
|
|
15
|
+
#
|
|
16
|
+
# BucketApi and FileApi are transport-agnostic — they speak to whatever
|
|
17
|
+
# Faraday connection the client hands them, so neither has an async twin.
|
|
18
|
+
#
|
|
19
|
+
# Call sites must run inside an `Async do ... end` block; outside one, the
|
|
20
|
+
# adapter still works but loses the concurrency win.
|
|
21
|
+
#
|
|
22
|
+
# require "supabase/storage/async"
|
|
23
|
+
# require "async"
|
|
24
|
+
#
|
|
25
|
+
# storage = Supabase::Storage::Async::Client.new(
|
|
26
|
+
# base_url: "https://project.supabase.co/storage/v1",
|
|
27
|
+
# headers: { "apikey" => key }
|
|
28
|
+
# )
|
|
29
|
+
#
|
|
30
|
+
# Async do |task|
|
|
31
|
+
# uploads = files.map do |f|
|
|
32
|
+
# task.async { storage.from("avatars").upload(f.name, f.bytes) }
|
|
33
|
+
# end
|
|
34
|
+
# uploads.map(&:wait)
|
|
35
|
+
# end
|
|
36
|
+
class Client < Supabase::Storage::Client
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def build_session(base_url)
|
|
40
|
+
Faraday.new(url: base_url, ssl: { verify: @verify }, proxy: @proxy) do |f|
|
|
41
|
+
f.request :multipart
|
|
42
|
+
if @timeout
|
|
43
|
+
f.options.timeout = @timeout
|
|
44
|
+
f.options.open_timeout = @timeout
|
|
45
|
+
end
|
|
46
|
+
f.adapter :async_http
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Convenience requirer for the async tree, mirroring lib/supabase/postgrest/async.rb.
|
|
4
|
+
# Plain `require "supabase/storage"` stays free of async-http-faraday so sync-only
|
|
5
|
+
# consumers don't pay for the fiber stack.
|
|
6
|
+
|
|
7
|
+
require_relative "../storage"
|
|
8
|
+
require_relative "async/client"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "request"
|
|
4
|
+
require_relative "types"
|
|
5
|
+
require_relative "utils"
|
|
6
|
+
|
|
7
|
+
module Supabase
|
|
8
|
+
module Storage
|
|
9
|
+
# The bucket-management half of the storage client — list / get / create / update /
|
|
10
|
+
# empty / delete on bucket records. Mirrors storage3's SyncStorageBucketAPI.
|
|
11
|
+
#
|
|
12
|
+
# {Client} inherits from this; callers don't construct BucketApi directly.
|
|
13
|
+
class BucketApi
|
|
14
|
+
include Request
|
|
15
|
+
|
|
16
|
+
# @param session [Faraday::Connection]
|
|
17
|
+
# @param base_url [String] storage REST endpoint, e.g. "https://x.supabase.co/storage/v1/"
|
|
18
|
+
# @param headers [Hash] static request headers (apikey, Authorization, etc.)
|
|
19
|
+
def initialize(session, base_url, headers)
|
|
20
|
+
@session = session
|
|
21
|
+
@base_url = base_url.end_with?("/") ? base_url : "#{base_url}/"
|
|
22
|
+
@headers = headers
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def list_buckets
|
|
26
|
+
body = _request(:get, ["bucket"])
|
|
27
|
+
Array(body).map { |b| Types::Bucket.from_hash(b) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def get_bucket(id)
|
|
31
|
+
Types::Bucket.from_hash(_request(:get, ["bucket", id]))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param id [String]
|
|
35
|
+
# @param name [String, nil] defaults to id
|
|
36
|
+
# @param public [Boolean, nil]
|
|
37
|
+
# @param file_size_limit [Integer, nil]
|
|
38
|
+
# @param allowed_mime_types [Array<String>, nil]
|
|
39
|
+
# @return [Hash] the raw response body
|
|
40
|
+
def create_bucket(id, name: nil, public: nil, file_size_limit: nil, allowed_mime_types: nil)
|
|
41
|
+
json = { "id" => id, "name" => name || id }
|
|
42
|
+
json["public"] = public unless public.nil?
|
|
43
|
+
json["file_size_limit"] = file_size_limit unless file_size_limit.nil?
|
|
44
|
+
json["allowed_mime_types"] = allowed_mime_types unless allowed_mime_types.nil?
|
|
45
|
+
_request(:post, ["bucket"], json: json)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def update_bucket(id, public: nil, file_size_limit: nil, allowed_mime_types: nil)
|
|
49
|
+
json = { "id" => id, "name" => id }
|
|
50
|
+
json["public"] = public unless public.nil?
|
|
51
|
+
json["file_size_limit"] = file_size_limit unless file_size_limit.nil?
|
|
52
|
+
json["allowed_mime_types"] = allowed_mime_types unless allowed_mime_types.nil?
|
|
53
|
+
_request(:put, ["bucket", id], json: json)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def empty_bucket(id)
|
|
57
|
+
_request(:post, ["bucket", id, "empty"], json: {})
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def delete_bucket(id)
|
|
61
|
+
_request(:delete, ["bucket", id], json: {})
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
require "faraday/multipart"
|
|
5
|
+
|
|
6
|
+
require_relative "bucket_api"
|
|
7
|
+
require_relative "file_api"
|
|
8
|
+
require_relative "analytics"
|
|
9
|
+
require_relative "vectors"
|
|
10
|
+
require_relative "version"
|
|
11
|
+
|
|
12
|
+
module Supabase
|
|
13
|
+
module Storage
|
|
14
|
+
# Sync Storage client. Constructed once per project; reused across requests.
|
|
15
|
+
#
|
|
16
|
+
# client = Supabase::Storage::Client.new(
|
|
17
|
+
# base_url: "https://project.supabase.co/storage/v1",
|
|
18
|
+
# headers: { "apikey" => key, "Authorization" => "Bearer #{token}" }
|
|
19
|
+
# )
|
|
20
|
+
#
|
|
21
|
+
# client.list_buckets # bucket-management surface
|
|
22
|
+
# client.from("avatars").upload(...) # file-level surface, scoped to one bucket
|
|
23
|
+
class Client < BucketApi
|
|
24
|
+
attr_reader :base_url, :headers
|
|
25
|
+
|
|
26
|
+
# @param base_url [String] storage REST endpoint (e.g. ".../storage/v1")
|
|
27
|
+
# @param headers [Hash] static request headers (apikey, Authorization)
|
|
28
|
+
# @param http_client [Faraday::Connection, nil] inject a pre-built Faraday for tests
|
|
29
|
+
# @param verify [Boolean] TLS cert verification
|
|
30
|
+
# @param proxy [String, nil]
|
|
31
|
+
# @param timeout [Numeric, nil]
|
|
32
|
+
def initialize(base_url:, headers: {}, http_client: nil, verify: true, proxy: nil, timeout: nil)
|
|
33
|
+
@verify = verify
|
|
34
|
+
@proxy = proxy
|
|
35
|
+
@timeout = timeout
|
|
36
|
+
@http_client = http_client
|
|
37
|
+
normalized = base_url.to_s
|
|
38
|
+
normalized = "#{normalized}/" unless normalized.end_with?("/")
|
|
39
|
+
|
|
40
|
+
default_headers = {
|
|
41
|
+
"X-Client-Info" => "supabase-rb/storage-rb v#{VERSION}"
|
|
42
|
+
}.merge(headers)
|
|
43
|
+
|
|
44
|
+
super(http_client || build_session(normalized), normalized, default_headers)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Return a {FileApi} scoped to the given bucket.
|
|
48
|
+
def from(bucket_id)
|
|
49
|
+
FileApi.new(bucket_id, @base_url, @headers, @session)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
alias bucket from
|
|
53
|
+
|
|
54
|
+
# Iceberg / analytics bucket management. Mirrors storage3's
|
|
55
|
+
# `SyncStorageClient#analytics`.
|
|
56
|
+
def analytics
|
|
57
|
+
@analytics ||= AnalyticsClient.new(@session, @base_url, @headers)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Vector bucket / index / record management. Mirrors storage3's
|
|
61
|
+
# `SyncStorageClient#vectors`.
|
|
62
|
+
def vectors
|
|
63
|
+
@vectors ||= VectorsClient.new(@session, @base_url, @headers)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def build_session(base_url)
|
|
69
|
+
Faraday.new(url: base_url, ssl: { verify: @verify }, proxy: @proxy) do |f|
|
|
70
|
+
f.request :multipart
|
|
71
|
+
if @timeout
|
|
72
|
+
f.options.timeout = @timeout
|
|
73
|
+
f.options.open_timeout = @timeout
|
|
74
|
+
end
|
|
75
|
+
f.adapter Faraday.default_adapter
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Supabase
|
|
4
|
+
module Storage
|
|
5
|
+
module Errors
|
|
6
|
+
# Base class for any failure raised by the storage gem.
|
|
7
|
+
class StorageError < StandardError; end
|
|
8
|
+
|
|
9
|
+
# Raised when the Storage REST API returns a non-2xx response.
|
|
10
|
+
# Mirrors supabase-py's StorageApiError contract — exposes :message, :code (the
|
|
11
|
+
# API's `error` field, e.g. "InvalidKey"), and :status (HTTP status).
|
|
12
|
+
class StorageApiError < StorageError
|
|
13
|
+
attr_reader :code, :status
|
|
14
|
+
|
|
15
|
+
def initialize(message, code: nil, status: nil)
|
|
16
|
+
@code = code
|
|
17
|
+
@status = status
|
|
18
|
+
super(message.to_s)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_h
|
|
22
|
+
{ "name" => "StorageApiError", "message" => message, "code" => @code, "status" => @status }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Raised by the vectors client for client-side validation (e.g. batch size
|
|
27
|
+
# bounds). Mirrors storage3's VectorBucketException, which intentionally
|
|
28
|
+
# does NOT inherit from StorageError.
|
|
29
|
+
class VectorBucketException < StandardError; end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|