Skip to content

Commit d85f874

Browse files
committed
refactor storage for using new http layer
1 parent 4af940a commit d85f874

File tree

10 files changed

+202
-112
lines changed

10 files changed

+202
-112
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import Foundation
2+
import HTTPTypes
3+
4+
extension HTTPBody {
5+
/// Decodes the body into a decodable object.
6+
///
7+
/// - Parameters:
8+
/// - type: The type to decode the body into.
9+
/// - decoder: The decoder to use to decode the body.
10+
/// - Returns: The decoded object.
11+
/// - Throws: An error if the body cannot be decoded.
12+
package func decoded<T: Decodable>(
13+
as _: T.Type = T.self,
14+
decoder: JSONDecoder = JSONDecoder()
15+
) async throws -> T {
16+
try await decoder.decode(T.self, from: data)
17+
}
18+
19+
/// The data of the body.
20+
///
21+
/// This is a lazy property that will collect the data from the body.
22+
///
23+
/// - Returns: The data of the body.
24+
/// - Throws: An error if the data cannot be collected.
25+
package var data: Data {
26+
get async throws {
27+
try await Data(collecting: self, upTo: .max)
28+
}
29+
}
30+
}

Sources/Helpers/HTTP/LoggingMiddleware.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import HTTPTypesFoundation
12
import Logging
23
import OpenAPIRuntime
3-
import HTTPTypesFoundation
44

55
#if canImport(Darwin)
66
import struct Foundation.URL
@@ -54,5 +54,3 @@ extension HTTPTypes.HTTPRequest {
5454
extension HTTPTypes.HTTPResponse {
5555
fileprivate var prettyDescription: String { "\(status.code) [\(headerFields.prettyDescription)]" }
5656
}
57-
58-
extension HTTPBody { fileprivate var prettyDescription: String { String(describing: self) } }

Sources/Storage/Logger.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// Logger.swift
3+
// Supabase
4+
//
5+
// Created by Guilherme Souza on 14/08/25.
6+
//
7+
8+
import Logging
9+
10+
extension Logger {
11+
static let storage = Logger(label: "storage")
12+
}

Sources/Storage/StorageApi.swift

Lines changed: 24 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Foundation
22
import HTTPTypes
3+
import OpenAPIURLSession
34

45
#if canImport(FoundationNetworking)
56
import FoundationNetworking
@@ -8,7 +9,7 @@ import HTTPTypes
89
public class StorageApi: @unchecked Sendable {
910
public let configuration: StorageClientConfiguration
1011

11-
private let http: any HTTPClientType
12+
private let client: Client
1213

1314
public init(configuration: StorageClientConfiguration) {
1415
var configuration = configuration
@@ -40,61 +41,42 @@ public class StorageApi: @unchecked Sendable {
4041

4142
self.configuration = configuration
4243

43-
var interceptors: [any HTTPClientInterceptor] = []
44-
if let logger = configuration.logger {
45-
interceptors.append(LoggerInterceptor(logger: logger))
46-
}
47-
48-
http = HTTPClient(
49-
fetch: configuration.session.fetch,
50-
interceptors: interceptors
44+
client = Client(
45+
serverURL: configuration.url,
46+
transport: configuration.transport
47+
?? FetchTransportAdapter(fetch: configuration.session.fetch),
48+
middlewares: [LoggingMiddleware(logger: .storage)]
5149
)
5250
}
5351

5452
@discardableResult
55-
func execute(_ request: Helpers.HTTPRequest) async throws -> Helpers.HTTPResponse {
53+
func execute(
54+
_ request: HTTPTypes.HTTPRequest,
55+
requestBody: HTTPBody? = nil
56+
) async throws -> (response: HTTPTypes.HTTPResponse, responseBody: HTTPBody) {
5657
var request = request
57-
request.headers = HTTPFields(configuration.headers).merging(with: request.headers)
58+
request.headerFields.merge(with: HTTPFields(configuration.headers))
5859

59-
let response = try await http.send(request)
60+
let (response, responseBody) = try await client.send(request, body: requestBody)
6061

61-
guard (200..<300).contains(response.statusCode) else {
62+
guard response.status.kind == .successful else {
63+
let data = try await Data(collecting: responseBody, upTo: .max)
6264
if let error = try? configuration.decoder.decode(
6365
StorageError.self,
64-
from: response.data
66+
from: data
6567
) {
6668
throw error
6769
}
6870

69-
throw HTTPError(data: response.data, response: response.underlyingResponse)
71+
throw HTTPError(
72+
data: data,
73+
response: HTTPURLResponse(
74+
httpResponse: response,
75+
url: request.url!
76+
)!
77+
)
7078
}
7179

72-
return response
73-
}
74-
}
75-
76-
extension Helpers.HTTPRequest {
77-
init(
78-
url: URL,
79-
method: HTTPTypes.HTTPRequest.Method,
80-
query: [URLQueryItem],
81-
formData: MultipartFormData,
82-
options: FileOptions,
83-
headers: HTTPFields = [:]
84-
) throws {
85-
var headers = headers
86-
if headers[.contentType] == nil {
87-
headers[.contentType] = formData.contentType
88-
}
89-
if headers[.cacheControl] == nil {
90-
headers[.cacheControl] = "max-age=\(options.cacheControl)"
91-
}
92-
try self.init(
93-
url: url,
94-
method: method,
95-
query: query,
96-
headers: headers,
97-
body: formData.encode()
98-
)
80+
return (response, responseBody)
9981
}
10082
}

Sources/Storage/StorageBucketApi.swift

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import Foundation
2+
import HTTPTypes
3+
import HTTPTypesFoundation
4+
import Helpers
25

36
#if canImport(FoundationNetworking)
47
import FoundationNetworking
@@ -9,25 +12,23 @@ public class StorageBucketApi: StorageApi, @unchecked Sendable {
912
/// Retrieves the details of all Storage buckets within an existing project.
1013
public func listBuckets() async throws -> [Bucket] {
1114
try await execute(
12-
HTTPRequest(
13-
url: configuration.url.appendingPathComponent("bucket"),
14-
method: .get
15+
HTTPTypes.HTTPRequest(
16+
method: .get,
17+
url: configuration.url.appendingPathComponent("bucket")
1518
)
16-
)
17-
.decoded(decoder: configuration.decoder)
19+
).responseBody.decoded(decoder: configuration.decoder)
1820
}
1921

2022
/// Retrieves the details of an existing Storage bucket.
2123
/// - Parameters:
2224
/// - id: The unique identifier of the bucket you would like to retrieve.
2325
public func getBucket(_ id: String) async throws -> Bucket {
2426
try await execute(
25-
HTTPRequest(
26-
url: configuration.url.appendingPathComponent("bucket/\(id)"),
27-
method: .get
27+
HTTPTypes.HTTPRequest(
28+
method: .get,
29+
url: configuration.url.appendingPathComponent("bucket/\(id)")
2830
)
29-
)
30-
.decoded(decoder: configuration.decoder)
31+
).responseBody.decoded(decoder: configuration.decoder)
3132
}
3233

3334
struct BucketParameters: Encodable {
@@ -44,10 +45,15 @@ public class StorageBucketApi: StorageApi, @unchecked Sendable {
4445
/// - options: Options for creating the bucket.
4546
public func createBucket(_ id: String, options: BucketOptions = .init()) async throws {
4647
try await execute(
47-
HTTPRequest(
48-
url: configuration.url.appendingPathComponent("bucket"),
48+
HTTPTypes.HTTPRequest(
4949
method: .post,
50-
body: configuration.encoder.encode(
50+
url: configuration.url.appendingPathComponent("bucket"),
51+
headerFields: [
52+
.contentType: "application/json"
53+
]
54+
),
55+
requestBody: HTTPBody(
56+
configuration.encoder.encode(
5157
BucketParameters(
5258
id: id,
5359
name: id,
@@ -66,10 +72,15 @@ public class StorageBucketApi: StorageApi, @unchecked Sendable {
6672
/// - options: Options for updating the bucket.
6773
public func updateBucket(_ id: String, options: BucketOptions) async throws {
6874
try await execute(
69-
HTTPRequest(
70-
url: configuration.url.appendingPathComponent("bucket/\(id)"),
75+
HTTPTypes.HTTPRequest(
7176
method: .put,
72-
body: configuration.encoder.encode(
77+
url: configuration.url.appendingPathComponent("bucket/\(id)"),
78+
headerFields: [
79+
.contentType: "application/json"
80+
]
81+
),
82+
requestBody: HTTPBody(
83+
configuration.encoder.encode(
7384
BucketParameters(
7485
id: id,
7586
name: id,
@@ -87,9 +98,9 @@ public class StorageBucketApi: StorageApi, @unchecked Sendable {
8798
/// - id: The unique identifier of the bucket you would like to empty.
8899
public func emptyBucket(_ id: String) async throws {
89100
try await execute(
90-
HTTPRequest(
91-
url: configuration.url.appendingPathComponent("bucket/\(id)/empty"),
92-
method: .post
101+
HTTPTypes.HTTPRequest(
102+
method: .post,
103+
url: configuration.url.appendingPathComponent("bucket/\(id)/empty")
93104
)
94105
)
95106
}
@@ -100,9 +111,9 @@ public class StorageBucketApi: StorageApi, @unchecked Sendable {
100111
/// - id: The unique identifier of the bucket you would like to delete.
101112
public func deleteBucket(_ id: String) async throws {
102113
try await execute(
103-
HTTPRequest(
104-
url: configuration.url.appendingPathComponent("bucket/\(id)"),
105-
method: .delete
114+
HTTPTypes.HTTPRequest(
115+
method: .delete,
116+
url: configuration.url.appendingPathComponent("bucket/\(id)")
106117
)
107118
)
108119
}

0 commit comments

Comments
 (0)