Skip to content
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
27 changes: 23 additions & 4 deletions Sources/GoogleCloud/Storage/Bucket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public struct Bucket: Sendable {
self.authorizedClient = authorizedClient
}

// INFO: https://cloud.google.com/storage/docs/json_api/v1/objects/list
// INFO: https://docs.cloud.google.com/storage/docs/json_api/v1/objects/list
public func files(
prefix: String,
logger: Logger? = nil
Expand All @@ -30,15 +30,32 @@ public struct Bucket: Sendable {
).items ?? []
}

// INFO: https://cloud.google.com/storage/docs/json_api/v1/objects/delete
// INFO: https://docs.cloud.google.com/storage/docs/json_api/v1/objects/move
@discardableResult
public func move(
src: String,
dst: String,
logger: Logger? = nil
) async throws -> StorageFile {
let src = Self.nameOnPath(name: src)
let dst = Self.nameOnPath(name: dst)
return try await authorizedClient.execute(
method: .POST,
path: "storage/v1/b/\(bucketName)/o/\(src)/moveTo/o/\(dst)",
logger: logger,
responseType: StorageFile.self
)
}

// INFO: https://docs.cloud.google.com/storage/docs/json_api/v1/objects/delete
public func delete(
name: String,
logger: Logger? = nil
) async throws {
let name = Self.nameOnPath(name: name)
_ = try await authorizedClient.execute(
method: .DELETE,
path: "storage/v1/b/\(bucketName)/o" + (name.hasPrefix("/") ? name : "/" + name),
path: "storage/v1/b/\(bucketName)/o/\(name)",
logger: logger
)
}
Expand Down Expand Up @@ -75,7 +92,9 @@ public struct Bucket: Sendable {
// INFO: https://cloud.google.com/storage/docs/request-endpoints#encoding
static func nameOnPath(name: String) -> String {
let targets = CharacterSet(charactersIn: " !#$&'()*+,/:;=?@[]")
return name.addingPercentEncoding(withAllowedCharacters: targets.inverted)!
return name
.choppingSlashPrefix
.addingPercentEncoding(withAllowedCharacters: targets.inverted)!
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/GoogleCloud/Storage/Entity/StorageFile.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

/// INFO: https://cloud.google.com/storage/docs/json_api/v1/objects
/// INFO: https://docs.cloud.google.com/storage/docs/json_api/v1/objects#resource
/// varとletは↑のwritableかどうかに対応してるけど、意味はわかっていない
public struct StorageFile: Decodable {
public init(name: String, bucket: String) {
Expand Down
5 changes: 3 additions & 2 deletions Sources/GoogleCloudBase/AuthorizedClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ package struct AuthorizedClient: Sendable {
if let data = resBody.getData(at: resBody.readerIndex, length: resBody.readableBytes),
let string = String(data: data, encoding: .utf8) {
// 404のときなど、JSONでないレスポンスが返ることがある
throw ErrorResponse(error: .init(code: Int(res.status.code), message: string))
errorResponse = ErrorResponse(error: .init(code: Int(res.status.code), message: string))
} else {
throw error
}
throw error
}
throw errorResponse
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/GoogleCloudBase/GCPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public struct GCPClient: Sendable {

public init(
requestLogLevel: Logger.Level = .debug,
errorLogLevel: Logger.Level = .debug
errorLogLevel: Logger.Level = .error
) {
self.requestLogLevel = requestLogLevel
self.errorLogLevel = errorLogLevel
Expand Down
20 changes: 20 additions & 0 deletions Tests/SharedTests/Storage/BucketTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@ import Testing
#expect(Set(response.map(\.name)) == ["testFiles/bar.png", "testFiles/baz.jpg"])
}

@Test(.disabled("fake-gcs-server doesn't support 'move'."))
func move() async throws {
let bucket = makeBucket()

let srcName = "testMove/src/Foo.txt"
let dstName = "testMove/dst/Foo_renamed.txt"
_ = try await bucket.uploadSimple(name: srcName, media: Data(#function.utf8))

let filesBefore = try await bucket.files(prefix: srcName)
#expect(!filesBefore.isEmpty)

let movedFile = try await bucket.move(src: srcName, dst: dstName)
#expect(movedFile.name == dstName)

let filesAfterSrc = try await bucket.files(prefix: srcName)
#expect(filesAfterSrc.isEmpty)
let filesAfterDst = try await bucket.files(prefix: dstName)
#expect(!filesAfterDst.isEmpty)
}

@Test func upload() async throws {
let bucket = makeBucket()

Expand Down