Skip to content

refactor: omit some method parameters in storage #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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