Skip to content

Commit 75699fc

Browse files
authored
refactor: omit some method parameters in storage (#148)
* refactor: omit some method parameters * test: separate integration from unit test
1 parent 4ae8d20 commit 75699fc

File tree

6 files changed

+243
-141
lines changed

6 files changed

+243
-141
lines changed

Sources/Storage/StorageBucketApi.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class StorageBucketApi: StorageApi {
1616
/// Retrieves the details of an existing Storage bucket.
1717
/// - Parameters:
1818
/// - id: The unique identifier of the bucket you would like to retrieve.
19-
public func getBucket(id: String) async throws -> Bucket {
19+
public func getBucket(_ id: String) async throws -> Bucket {
2020
try await execute(Request(path: "/bucket/\(id)", method: .get))
2121
.decoded(decoder: configuration.decoder)
2222
}
@@ -32,7 +32,7 @@ public class StorageBucketApi: StorageApi {
3232
/// Creates a new Storage bucket.
3333
/// - Parameters:
3434
/// - id: A unique identifier for the bucket you are creating.
35-
public func createBucket(id: String, options: BucketOptions = .init()) async throws {
35+
public func createBucket(_ id: String, options: BucketOptions = .init()) async throws {
3636
try await execute(
3737
Request(
3838
path: "/bucket",
@@ -53,7 +53,7 @@ public class StorageBucketApi: StorageApi {
5353
/// Updates a Storage bucket
5454
/// - Parameters:
5555
/// - id: A unique identifier for the bucket you are updating.
56-
public func updateBucket(id: String, options: BucketOptions) async throws {
56+
public func updateBucket(_ id: String, options: BucketOptions) async throws {
5757
try await execute(
5858
Request(
5959
path: "/bucket/\(id)",
@@ -74,15 +74,15 @@ public class StorageBucketApi: StorageApi {
7474
/// Removes all objects inside a single bucket.
7575
/// - Parameters:
7676
/// - id: The unique identifier of the bucket you would like to empty.
77-
public func emptyBucket(id: String) async throws {
77+
public func emptyBucket(_ id: String) async throws {
7878
try await execute(Request(path: "/bucket/\(id)/empty", method: .post))
7979
}
8080

8181
/// Deletes an existing bucket. A bucket can't be deleted with existing objects inside it.
8282
/// You must first `empty()` the bucket.
8383
/// - Parameters:
8484
/// - id: The unique identifier of the bucket you would like to delete.
85-
public func deleteBucket(id: String) async throws {
85+
public func deleteBucket(_ id: String) async throws {
8686
try await execute(Request(path: "/bucket/\(id)", method: .delete))
8787
}
8888
}

Sources/Storage/StorageFileApi.swift

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ public class StorageFileApi: StorageApi {
142142
/// `folder/image.png`.
143143
/// - expiresIn: The number of seconds until the signed URL expires. For example, `60` for a URL
144144
/// which is valid for one minute.
145+
/// - download: Trigger a download with the specified file name.
145146
/// - transform: Transform the asset before serving it to the client.
146147
public func createSignedURL(
147148
path: String,
@@ -195,6 +196,29 @@ public class StorageFileApi: StorageApi {
195196
return signedURL
196197
}
197198

199+
/// Create signed url to download file without requiring permissions. This URL can be valid for a
200+
/// set number of seconds.
201+
/// - Parameters:
202+
/// - path: The file path to be downloaded, including the current file name. For example
203+
/// `folder/image.png`.
204+
/// - expiresIn: The number of seconds until the signed URL expires. For example, `60` for a URL
205+
/// which is valid for one minute.
206+
/// - download: Trigger a download with the default file name.
207+
/// - transform: Transform the asset before serving it to the client.
208+
public func createSignedURL(
209+
path: String,
210+
expiresIn: Int,
211+
download: Bool,
212+
transform: TransformOptions? = nil
213+
) async throws -> URL {
214+
try await createSignedURL(
215+
path: path,
216+
expiresIn: expiresIn,
217+
download: download ? "" : nil,
218+
transform: transform
219+
)
220+
}
221+
198222
/// Deletes files within the same bucket
199223
/// - Parameters:
200224
/// - paths: An array of files to be deletes, including the path and file name. For example
@@ -252,13 +276,11 @@ public class StorageFileApi: StorageApi {
252276
/// Returns a public url for an asset.
253277
/// - Parameters:
254278
/// - path: The file path to the asset. For example `folder/image.png`.
255-
/// - download: Whether the asset should be downloaded.
256-
/// - fileName: If specified, the file name for the asset that is downloaded.
279+
/// - download: Trigger a download with the specified file name.
257280
/// - options: Transform the asset before retrieving it on the client.
258281
public func getPublicURL(
259282
path: String,
260-
download: Bool = false,
261-
fileName: String = "",
283+
download: String? = nil,
262284
options: TransformOptions? = nil
263285
) throws -> URL {
264286
var queryItems: [URLQueryItem] = []
@@ -268,8 +290,8 @@ public class StorageFileApi: StorageApi {
268290
throw URLError(.badURL)
269291
}
270292

271-
if download {
272-
queryItems.append(URLQueryItem(name: "download", value: fileName))
293+
if let download {
294+
queryItems.append(URLQueryItem(name: "download", value: download))
273295
}
274296

275297
if let optionsQueryItems = options?.queryItems {
@@ -287,6 +309,19 @@ public class StorageFileApi: StorageApi {
287309

288310
return generatedUrl
289311
}
312+
313+
/// Returns a public url for an asset.
314+
/// - Parameters:
315+
/// - path: The file path to the asset. For example `folder/image.png`.
316+
/// - download: Trigger a download with the default file name.
317+
/// - options: Transform the asset before retrieving it on the client.
318+
public func getPublicURL(
319+
path: String,
320+
download: Bool,
321+
options: TransformOptions? = nil
322+
) throws -> URL {
323+
try getPublicURL(path: path, download: download ? "" : nil, options: options)
324+
}
290325
}
291326

292327
private func fileName(fromPath path: String) -> String {

Sources/Storage/SupabaseStorage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class SupabaseStorageClient: StorageBucketApi {
2626
/// Perform file operation in a bucket.
2727
/// - Parameter id: The bucket id to operate on.
2828
/// - Returns: StorageFileApi object
29-
public func from(id: String) -> StorageFileApi {
29+
public func from(_ id: String) -> StorageFileApi {
3030
StorageFileApi(bucketId: id, configuration: configuration)
3131
}
3232
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
//
2+
// StorageClientIntegrationTests.swift
3+
//
4+
//
5+
// Created by Guilherme Souza on 04/11/23.
6+
//
7+
8+
@testable import Storage
9+
import XCTest
10+
11+
final class StorageClientIntegrationTests: XCTestCase {
12+
static var apiKey: String {
13+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0"
14+
}
15+
16+
static var supabaseURL: String {
17+
"http://localhost:54321/storage/v1"
18+
}
19+
20+
let bucketId = "tests"
21+
22+
let storage = SupabaseStorageClient.test(supabaseURL: supabaseURL, apiKey: apiKey)
23+
24+
let uploadData = try? Data(
25+
contentsOf: URL(
26+
string: "https://raw.githubusercontent.com/supabase-community/storage-swift/main/README.md"
27+
)!
28+
)
29+
30+
override func setUp() async throws {
31+
try await super.setUp()
32+
33+
try XCTSkipUnless(
34+
ProcessInfo.processInfo.environment["INTEGRATION_TESTS"] != nil,
35+
"INTEGRATION_TESTS not defined."
36+
)
37+
38+
try? await storage.emptyBucket(bucketId)
39+
try? await storage.deleteBucket(bucketId)
40+
41+
try await storage.createBucket(bucketId, options: BucketOptions(public: true))
42+
}
43+
44+
func testUpdateBucket() async throws {
45+
var bucket = try await storage.getBucket(bucketId)
46+
XCTAssertTrue(bucket.isPublic)
47+
48+
try await storage.updateBucket(bucketId, options: BucketOptions(public: false))
49+
bucket = try await storage.getBucket(bucketId)
50+
XCTAssertFalse(bucket.isPublic)
51+
}
52+
53+
func testListBuckets() async throws {
54+
let buckets = try await storage.listBuckets()
55+
XCTAssertTrue(buckets.contains { $0.id == bucketId })
56+
}
57+
58+
func testFileIntegration() async throws {
59+
var files = try await storage.from(bucketId).list()
60+
XCTAssertTrue(files.isEmpty)
61+
62+
try await uploadTestData()
63+
64+
files = try await storage.from(bucketId).list()
65+
XCTAssertEqual(files.map(\.name), ["README.md"])
66+
67+
let downloadedData = try await storage.from(bucketId).download(path: "README.md")
68+
XCTAssertEqual(downloadedData, uploadData)
69+
70+
try await storage.from(bucketId).move(from: "README.md", to: "README_2.md")
71+
72+
var searchedFiles = try await storage.from(bucketId)
73+
.list(options: .init(search: "README.md"))
74+
XCTAssertTrue(searchedFiles.isEmpty)
75+
76+
try await storage.from(bucketId).copy(from: "README_2.md", to: "README.md")
77+
searchedFiles = try await storage.from(bucketId).list(options: .init(search: "README.md"))
78+
XCTAssertEqual(searchedFiles.map(\.name), ["README.md"])
79+
80+
let removedFiles = try await storage.from(bucketId).remove(paths: ["README_2.md"])
81+
XCTAssertEqual(removedFiles.map(\.name), ["README_2.md"])
82+
}
83+
84+
func testGetPublicURL() async throws {
85+
try await uploadTestData()
86+
87+
let path = "README.md"
88+
89+
let baseUrl = try storage.from(bucketId).getPublicURL(path: path)
90+
XCTAssertEqual(baseUrl.absoluteString, "\(Self.supabaseURL)/object/public/\(bucketId)/\(path)")
91+
92+
let baseUrlWithDownload = try storage.from(bucketId).getPublicURL(
93+
path: path,
94+
download: true
95+
)
96+
XCTAssertEqual(
97+
baseUrlWithDownload.absoluteString,
98+
"\(Self.supabaseURL)/object/public/\(bucketId)/\(path)?download="
99+
)
100+
101+
let baseUrlWithDownloadAndFileName = try storage.from(bucketId).getPublicURL(
102+
path: path, download: "test"
103+
)
104+
XCTAssertEqual(
105+
baseUrlWithDownloadAndFileName.absoluteString,
106+
"\(Self.supabaseURL)/object/public/\(bucketId)/\(path)?download=test"
107+
)
108+
109+
let baseUrlWithAllOptions = try storage.from(bucketId).getPublicURL(
110+
path: path, download: "test",
111+
options: TransformOptions(width: 300, height: 300)
112+
)
113+
XCTAssertEqual(
114+
baseUrlWithAllOptions.absoluteString,
115+
"\(Self.supabaseURL)/render/image/public/\(bucketId)/\(path)?download=test&width=300&height=300&resize=cover&quality=80&format=origin"
116+
)
117+
}
118+
119+
func testCreateSignedURL() async throws {
120+
try await uploadTestData()
121+
122+
let path = "README.md"
123+
124+
let url = try await storage.from(bucketId).createSignedURL(
125+
path: path,
126+
expiresIn: 60,
127+
download: "README_local.md"
128+
)
129+
let components = try XCTUnwrap(URLComponents(url: url, resolvingAgainstBaseURL: true))
130+
131+
let downloadQuery = components.queryItems?.first(where: { $0.name == "download" })
132+
XCTAssertEqual(downloadQuery?.value, "README_local.md")
133+
XCTAssertEqual(components.path, "/storage/v1/object/sign/\(bucketId)/\(path)")
134+
}
135+
136+
func testUpdate() async throws {
137+
try await uploadTestData()
138+
139+
let dataToUpdate = try? Data(
140+
contentsOf: URL(
141+
string: "https://raw.githubusercontent.com/supabase-community/supabase-swift/master/README.md"
142+
)!
143+
)
144+
145+
try await storage.from(bucketId).update(
146+
path: "README.md",
147+
file: File(name: "README.md", data: dataToUpdate ?? Data(), fileName: nil, contentType: nil)
148+
)
149+
}
150+
151+
private func uploadTestData() async throws {
152+
let file = File(
153+
name: "README.md", data: uploadData ?? Data(), fileName: "README.md", contentType: "text/html"
154+
)
155+
_ = try await storage.from(bucketId).upload(
156+
path: "README.md", file: file, fileOptions: FileOptions(cacheControl: "3600")
157+
)
158+
}
159+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// SupabaseStorageClient+Test.swift
3+
//
4+
//
5+
// Created by Guilherme Souza on 04/11/23.
6+
//
7+
8+
import Foundation
9+
import Storage
10+
11+
extension SupabaseStorageClient {
12+
static func test(supabaseURL: String, apiKey: String) -> SupabaseStorageClient {
13+
SupabaseStorageClient(
14+
configuration: StorageClientConfiguration(
15+
url: URL(string: supabaseURL)!,
16+
headers: [
17+
"Authorization": "Bearer \(apiKey)",
18+
"apikey": apiKey,
19+
]
20+
)
21+
)
22+
}
23+
}

0 commit comments

Comments
 (0)