Skip to content

Commit

Permalink
refactor: omit some method parameters in storage (#148)
Browse files Browse the repository at this point in the history
* refactor: omit some method parameters

* test: separate integration from unit test
  • Loading branch information
grdsdev authored Nov 4, 2023
1 parent 4ae8d20 commit 75699fc
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 141 deletions.
10 changes: 5 additions & 5 deletions Sources/Storage/StorageBucketApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class StorageBucketApi: StorageApi {
/// Retrieves the details of an existing Storage bucket.
/// - Parameters:
/// - id: The unique identifier of the bucket you would like to retrieve.
public func getBucket(id: String) async throws -> Bucket {
public func getBucket(_ id: String) async throws -> Bucket {
try await execute(Request(path: "/bucket/\(id)", method: .get))
.decoded(decoder: configuration.decoder)
}
Expand All @@ -32,7 +32,7 @@ public class StorageBucketApi: StorageApi {
/// Creates a new Storage bucket.
/// - Parameters:
/// - id: A unique identifier for the bucket you are creating.
public func createBucket(id: String, options: BucketOptions = .init()) async throws {
public func createBucket(_ id: String, options: BucketOptions = .init()) async throws {
try await execute(
Request(
path: "/bucket",
Expand All @@ -53,7 +53,7 @@ public class StorageBucketApi: StorageApi {
/// Updates a Storage bucket
/// - Parameters:
/// - id: A unique identifier for the bucket you are updating.
public func updateBucket(id: String, options: BucketOptions) async throws {
public func updateBucket(_ id: String, options: BucketOptions) async throws {
try await execute(
Request(
path: "/bucket/\(id)",
Expand All @@ -74,15 +74,15 @@ public class StorageBucketApi: StorageApi {
/// Removes all objects inside a single bucket.
/// - Parameters:
/// - id: The unique identifier of the bucket you would like to empty.
public func emptyBucket(id: String) async throws {
public func emptyBucket(_ id: String) async throws {
try await execute(Request(path: "/bucket/\(id)/empty", method: .post))
}

/// Deletes an existing bucket. A bucket can't be deleted with existing objects inside it.
/// You must first `empty()` the bucket.
/// - Parameters:
/// - id: The unique identifier of the bucket you would like to delete.
public func deleteBucket(id: String) async throws {
public func deleteBucket(_ id: String) async throws {
try await execute(Request(path: "/bucket/\(id)", method: .delete))
}
}
47 changes: 41 additions & 6 deletions Sources/Storage/StorageFileApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public class StorageFileApi: StorageApi {
/// `folder/image.png`.
/// - expiresIn: The number of seconds until the signed URL expires. For example, `60` for a URL
/// which is valid for one minute.
/// - download: Trigger a download with the specified file name.
/// - transform: Transform the asset before serving it to the client.
public func createSignedURL(
path: String,
Expand Down Expand Up @@ -195,6 +196,29 @@ public class StorageFileApi: StorageApi {
return signedURL
}

/// Create signed url to download file without requiring permissions. This URL can be valid for a
/// set number of seconds.
/// - Parameters:
/// - path: The file path to be downloaded, including the current file name. For example
/// `folder/image.png`.
/// - expiresIn: The number of seconds until the signed URL expires. For example, `60` for a URL
/// which is valid for one minute.
/// - download: Trigger a download with the default file name.
/// - transform: Transform the asset before serving it to the client.
public func createSignedURL(
path: String,
expiresIn: Int,
download: Bool,
transform: TransformOptions? = nil
) async throws -> URL {
try await createSignedURL(
path: path,
expiresIn: expiresIn,
download: download ? "" : nil,
transform: transform
)
}

/// Deletes files within the same bucket
/// - Parameters:
/// - paths: An array of files to be deletes, including the path and file name. For example
Expand Down Expand Up @@ -252,13 +276,11 @@ public class StorageFileApi: StorageApi {
/// Returns a public url for an asset.
/// - Parameters:
/// - path: The file path to the asset. For example `folder/image.png`.
/// - download: Whether the asset should be downloaded.
/// - fileName: If specified, the file name for the asset that is downloaded.
/// - download: Trigger a download with the specified file name.
/// - options: Transform the asset before retrieving it on the client.
public func getPublicURL(
path: String,
download: Bool = false,
fileName: String = "",
download: String? = nil,
options: TransformOptions? = nil
) throws -> URL {
var queryItems: [URLQueryItem] = []
Expand All @@ -268,8 +290,8 @@ public class StorageFileApi: StorageApi {
throw URLError(.badURL)
}

if download {
queryItems.append(URLQueryItem(name: "download", value: fileName))
if let download {
queryItems.append(URLQueryItem(name: "download", value: download))
}

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

return generatedUrl
}

/// Returns a public url for an asset.
/// - Parameters:
/// - path: The file path to the asset. For example `folder/image.png`.
/// - download: Trigger a download with the default file name.
/// - options: Transform the asset before retrieving it on the client.
public func getPublicURL(
path: String,
download: Bool,
options: TransformOptions? = nil
) throws -> URL {
try getPublicURL(path: path, download: download ? "" : nil, options: options)
}
}

private func fileName(fromPath path: String) -> String {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Storage/SupabaseStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class SupabaseStorageClient: StorageBucketApi {
/// Perform file operation in a bucket.
/// - Parameter id: The bucket id to operate on.
/// - Returns: StorageFileApi object
public func from(id: String) -> StorageFileApi {
public func from(_ id: String) -> StorageFileApi {
StorageFileApi(bucketId: id, configuration: configuration)
}
}
159 changes: 159 additions & 0 deletions Tests/StorageTests/StorageClientIntegrationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//
// StorageClientIntegrationTests.swift
//
//
// Created by Guilherme Souza on 04/11/23.
//

@testable import Storage
import XCTest

final class StorageClientIntegrationTests: XCTestCase {
static var apiKey: String {
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0"
}

static var supabaseURL: String {
"http://localhost:54321/storage/v1"
}

let bucketId = "tests"

let storage = SupabaseStorageClient.test(supabaseURL: supabaseURL, apiKey: apiKey)

let uploadData = try? Data(
contentsOf: URL(
string: "https://raw.githubusercontent.com/supabase-community/storage-swift/main/README.md"
)!
)

override func setUp() async throws {
try await super.setUp()

try XCTSkipUnless(
ProcessInfo.processInfo.environment["INTEGRATION_TESTS"] != nil,
"INTEGRATION_TESTS not defined."
)

try? await storage.emptyBucket(bucketId)
try? await storage.deleteBucket(bucketId)

try await storage.createBucket(bucketId, options: BucketOptions(public: true))
}

func testUpdateBucket() async throws {
var bucket = try await storage.getBucket(bucketId)
XCTAssertTrue(bucket.isPublic)

try await storage.updateBucket(bucketId, options: BucketOptions(public: false))
bucket = try await storage.getBucket(bucketId)
XCTAssertFalse(bucket.isPublic)
}

func testListBuckets() async throws {
let buckets = try await storage.listBuckets()
XCTAssertTrue(buckets.contains { $0.id == bucketId })
}

func testFileIntegration() async throws {
var files = try await storage.from(bucketId).list()
XCTAssertTrue(files.isEmpty)

try await uploadTestData()

files = try await storage.from(bucketId).list()
XCTAssertEqual(files.map(\.name), ["README.md"])

let downloadedData = try await storage.from(bucketId).download(path: "README.md")
XCTAssertEqual(downloadedData, uploadData)

try await storage.from(bucketId).move(from: "README.md", to: "README_2.md")

var searchedFiles = try await storage.from(bucketId)
.list(options: .init(search: "README.md"))
XCTAssertTrue(searchedFiles.isEmpty)

try await storage.from(bucketId).copy(from: "README_2.md", to: "README.md")
searchedFiles = try await storage.from(bucketId).list(options: .init(search: "README.md"))
XCTAssertEqual(searchedFiles.map(\.name), ["README.md"])

let removedFiles = try await storage.from(bucketId).remove(paths: ["README_2.md"])
XCTAssertEqual(removedFiles.map(\.name), ["README_2.md"])
}

func testGetPublicURL() async throws {
try await uploadTestData()

let path = "README.md"

let baseUrl = try storage.from(bucketId).getPublicURL(path: path)
XCTAssertEqual(baseUrl.absoluteString, "\(Self.supabaseURL)/object/public/\(bucketId)/\(path)")

let baseUrlWithDownload = try storage.from(bucketId).getPublicURL(
path: path,
download: true
)
XCTAssertEqual(
baseUrlWithDownload.absoluteString,
"\(Self.supabaseURL)/object/public/\(bucketId)/\(path)?download="
)

let baseUrlWithDownloadAndFileName = try storage.from(bucketId).getPublicURL(
path: path, download: "test"
)
XCTAssertEqual(
baseUrlWithDownloadAndFileName.absoluteString,
"\(Self.supabaseURL)/object/public/\(bucketId)/\(path)?download=test"
)

let baseUrlWithAllOptions = try storage.from(bucketId).getPublicURL(
path: path, download: "test",
options: TransformOptions(width: 300, height: 300)
)
XCTAssertEqual(
baseUrlWithAllOptions.absoluteString,
"\(Self.supabaseURL)/render/image/public/\(bucketId)/\(path)?download=test&width=300&height=300&resize=cover&quality=80&format=origin"
)
}

func testCreateSignedURL() async throws {
try await uploadTestData()

let path = "README.md"

let url = try await storage.from(bucketId).createSignedURL(
path: path,
expiresIn: 60,
download: "README_local.md"
)
let components = try XCTUnwrap(URLComponents(url: url, resolvingAgainstBaseURL: true))

let downloadQuery = components.queryItems?.first(where: { $0.name == "download" })
XCTAssertEqual(downloadQuery?.value, "README_local.md")
XCTAssertEqual(components.path, "/storage/v1/object/sign/\(bucketId)/\(path)")
}

func testUpdate() async throws {
try await uploadTestData()

let dataToUpdate = try? Data(
contentsOf: URL(
string: "https://raw.githubusercontent.com/supabase-community/supabase-swift/master/README.md"
)!
)

try await storage.from(bucketId).update(
path: "README.md",
file: File(name: "README.md", data: dataToUpdate ?? Data(), fileName: nil, contentType: nil)
)
}

private func uploadTestData() async throws {
let file = File(
name: "README.md", data: uploadData ?? Data(), fileName: "README.md", contentType: "text/html"
)
_ = try await storage.from(bucketId).upload(
path: "README.md", file: file, fileOptions: FileOptions(cacheControl: "3600")
)
}
}
23 changes: 23 additions & 0 deletions Tests/StorageTests/SupabaseStorageClient+Test.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// SupabaseStorageClient+Test.swift
//
//
// Created by Guilherme Souza on 04/11/23.
//

import Foundation
import Storage

extension SupabaseStorageClient {
static func test(supabaseURL: String, apiKey: String) -> SupabaseStorageClient {
SupabaseStorageClient(
configuration: StorageClientConfiguration(
url: URL(string: supabaseURL)!,
headers: [
"Authorization": "Bearer \(apiKey)",
"apikey": apiKey,
]
)
)
}
}
Loading

0 comments on commit 75699fc

Please sign in to comment.