Skip to content

Commit

Permalink
loads of changes
Browse files Browse the repository at this point in the history
- locating bucket region works
- new url builder
- loads of cleaning & small improvements
  • Loading branch information
rafiki270 committed May 16, 2018
1 parent 5364051 commit 4fe4eeb
Show file tree
Hide file tree
Showing 19 changed files with 375 additions and 145 deletions.
105 changes: 105 additions & 0 deletions Other/Postman/Vapor S3.postman_collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"info": {
"_postman_id": "da87b927-b230-431d-91bf-57200936e59f",
"name": "Vapor S3",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Buckets list",
"request": {
"method": "GET",
"header": [],
"body": {},
"url": {
"raw": "{{SERVER}}buckets",
"host": [
"{{SERVER}}buckets"
]
}
},
"response": []
},
{
"name": "Bucket (create)",
"request": {
"method": "PUT",
"header": [],
"body": {},
"url": {
"raw": "{{SERVER}}bucket",
"host": [
"{{SERVER}}bucket"
]
}
},
"response": []
},
{
"name": "Bucket (delete)",
"request": {
"method": "DELETE",
"header": [],
"body": {},
"url": {
"raw": "{{SERVER}}bucket",
"host": [
"{{SERVER}}bucket"
]
}
},
"response": []
},
{
"name": "Bucket (location)",
"request": {
"method": "GET",
"header": [],
"body": {},
"url": {
"raw": "{{SERVER}}bucket/location",
"host": [
"{{SERVER}}bucket"
],
"path": [
"location"
]
}
},
"response": []
},
{
"name": "Files (list)",
"request": {
"method": "GET",
"header": [],
"body": {},
"url": {
"raw": "{{SERVER}}files",
"host": [
"{{SERVER}}files"
]
}
},
"response": []
},
{
"name": "Files (test)",
"request": {
"method": "GET",
"header": [],
"body": {},
"url": {
"raw": "{{SERVER}}files/test",
"host": [
"{{SERVER}}files"
],
"path": [
"test"
]
}
},
"response": []
}
]
}
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ let package = Package(
.library(name: "S3TestTools", targets: ["S3TestTools"])
],
dependencies: [
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0-rc.2"),
.package(url: "https://github.com/LiveUI/XMLCoding.git", .branch("master")),
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
.package(url: "https://github.com/LiveUI/XMLCoding.git", from: "0.1.0"),
.package(url: "https://github.com/LiveUI/VaporTestTools.git", .branch("master"))
],
targets: [
Expand Down
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [x] Listing buckets
- [x] Create bucket
- [x] Delete bucket
- [x] Locate bucket region
- [x] List objects
- [x] Upload file
- [x] Get file
Expand Down Expand Up @@ -54,14 +55,11 @@ public protocol S3Client: Service {
/// Create a bucket
func create(bucket: String, region: Region?, on container: Container) throws -> Future<Void>

/// Delete a bucket wherever it is (WIP)
// func delete(bucket: String, on container: Container) throws -> Future<Void>

/// Delete a bucket
func delete(bucket: String, region: Region?, on container: Container) throws -> Future<Void>

/// Get bucket location (WIP)
// func location(bucket: String, on container: Container) throws -> Future<Bucket.Location>
/// Get bucket location
func location(bucket: String, on container: Container) throws -> Future<Region>

/// Get list of objects
func list(bucket: String, region: Region?, on container: Container) throws -> Future<BucketResults>
Expand Down Expand Up @@ -144,6 +142,24 @@ public func routes(_ router: Router) throws {
)
}

// Locate bucket (get region)
router.get("bucket/location") { req -> Future<String> in
let s3 = try req.makeS3Client()
return try s3.location(bucket: "bucket-name", on: req).map(to: String.self) { region in
return region.hostUrlString()
}.catchMap({ (error) -> (String) in
if let error = error as? S3.Error {
switch error {
case .errorResponse(_, let error):
return error.message
default:
return "S3 :("
}
}
return ":("
}
)
}
// Delete bucket
router.delete("bucket") { req -> Future<String> in
let s3 = try req.makeS3Client()
Expand Down
87 changes: 45 additions & 42 deletions Sources/S3/Extensions/S3+Bucket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,51 @@ public extension S3 {

// MARK: Buckets

// /// Get bucket location
// public func location(bucket: String, on container: Container) throws -> Future<Bucket.Location> {
// let region = region ?? signer.config.region
// guard let url = URL(string: "https://\(bucket).s3.amazonaws.com/?location"), let host = url.host else {
// throw Error.invalidUrl
// }
//
// let headers = [
// "host": host
// ]
// let awsHeaders = try signer.headers(for: .GET, urlString: url.absoluteString, region: region, headers: headers, payload: .none)
// return try make(request: url, method: .GET, headers: awsHeaders, data: "".convertToData(), on: container).map(to: Bucket.Location.self) { response in
// if response.http.status == .notFound {
// throw Error.notFound
// }
// guard response.http.status == .ok || response.http.status == .noContent else {
// if let error = try? response.decode(to: ErrorMessage.self) {
// throw Error.errorResponse(response.http.status, error)
// } else {
// throw Error.badResponse(response)
// }
// }
// return try response.decode(to: Bucket.Location.self)
// }
// }
/// Get bucket location
public func location(bucket: String, on container: Container) throws -> Future<Region> {
let builder = urlBuilder(for: container)
let region = Region.euWest2
let url = try builder.url(region: region, bucket: bucket)

let awsHeaders = try signer.headers(for: .GET, urlString: url.absoluteString, region: region, payload: .none)
return try make(request: url, method: .GET, headers: awsHeaders, data: emptyData(), on: container).map(to: Region.self) { response in
if response.http.status == .notFound {
throw Error.notFound
}
if response.http.status == .ok {
return region
} else {
if let error = try? response.decode(to: ErrorMessage.self), error.code == "PermanentRedirect", let endpoint = error.endpoint {
if endpoint == "s3.amazonaws.com" {
return Region.usEast1
} else {
// Split bucket.s3.region.amazonaws.com into parts
var parts = endpoint.split(separator: ".")
// Remove .com
parts.removeLast()
// Remove .amazonaws
parts.removeLast()
// Get region (lat part)
let regionString = String(parts.removeLast()).lowercased()
guard let region = Region(rawValue: regionString) else {
throw Error.badResponse(response)
}
return region
}
} else {
throw Error.badResponse(response)
}
}
}
}

/// Delete bucket
public func delete(bucket: String, region: Region? = nil, on container: Container) throws -> Future<Void> {
let region = region ?? signer.config.region
guard let url = URL(string: "https://\(bucket).s3.\(region.rawValue).amazonaws.com/"), let host = url.host else {
throw Error.invalidUrl
}
let builder = urlBuilder(for: container)
let url = try builder.url(region: region, bucket: bucket)

let headers = [
"host": host
]
let awsHeaders = try signer.headers(for: .DELETE, urlString: url.absoluteString, region: region, headers: headers, payload: .none)
return try make(request: url, method: .DELETE, headers: awsHeaders, data: "".convertToData(), on: container).map(to: Void.self) { response in
let awsHeaders = try signer.headers(for: .DELETE, urlString: url.absoluteString, region: region, payload: .none)
return try make(request: url, method: .DELETE, headers: awsHeaders, data: emptyData(), on: container).map(to: Void.self) { response in
try self.check(response)
return Void()
}
Expand All @@ -61,9 +68,9 @@ public extension S3 {
/// Create a bucket
public func create(bucket: String, region: Region? = nil, on container: Container) throws -> Future<Void> {
let region = region ?? signer.config.region
guard let url = URL(string: "https://\(bucket).s3.\(region.rawValue).amazonaws.com/"), let host = url.host else {
throw Error.invalidUrl
}

let builder = urlBuilder(for: container)
let url = try builder.url(region: region, bucket: bucket)

let content = """
<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
Expand All @@ -72,11 +79,7 @@ public extension S3 {
"""

let data = content.convertToData()

let headers = [
"host": host
]
let awsHeaders = try signer.headers(for: .PUT, urlString: url.absoluteString, region: region, headers: headers, payload: .bytes(data))
let awsHeaders = try signer.headers(for: .PUT, urlString: url.absoluteString, region: region, payload: .bytes(data))
return try make(request: url, method: .PUT, headers: awsHeaders, data: data, on: container).map(to: Void.self) { response in
try self.check(response)
return Void()
Expand Down
6 changes: 4 additions & 2 deletions Sources/S3/Extensions/S3+Delete.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ public extension S3 {

/// Delete file from S3
public func delete(file: LocationConvertible, headers: [String: String], on container: Container) throws -> Future<Void> {
let url = try self.url(file: file, on: container)
let builder = urlBuilder(for: container)
let url = try builder.url(file: file)

let headers = try signer.headers(for: .DELETE, urlString: url.absoluteString, headers: headers, payload: .none)
return try make(request: url, method: .DELETE, headers: headers, data: "".convertToData(), on: container).map(to: Void.self) { response in
return try make(request: url, method: .DELETE, headers: headers, data: emptyData(), on: container).map(to: Void.self) { response in
try self.check(response)

return Void()
Expand Down
4 changes: 3 additions & 1 deletion Sources/S3/Extensions/S3+Get.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ public extension S3 {

/// Retrieve file data from S3
public func get(file: LocationConvertible, headers: [String: String], on container: Container) throws -> Future<File.Response> {
let url = try self.url(file: file, on: container)
let builder = urlBuilder(for: container)
let url = try builder.url(file: file)

let headers = try signer.headers(for: .GET, urlString: url.absoluteString, headers: headers, payload: .none)
return try make(request: url, method: .GET, headers: headers, on: container).map(to: File.Response.self) { response in
try self.check(response)
Expand Down
2 changes: 1 addition & 1 deletion Sources/S3/Extensions/S3+List.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extension S3 {
var headers = headers
headers["host"] = host
let awsHeaders = try signer.headers(for: .GET, urlString: url.absoluteString, region: region, headers: headers, payload: .none)
return try make(request: url, method: .GET, headers: awsHeaders, data: "".convertToData(), on: container).map(to: BucketResults.self) { response in
return try make(request: url, method: .GET, headers: awsHeaders, data: emptyData(), on: container).map(to: BucketResults.self) { response in
try self.check(response)
return try response.decode(to: BucketResults.self)
}
Expand Down
6 changes: 4 additions & 2 deletions Sources/S3/Extensions/S3+ObjectInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ public extension S3 {
/// Get file information (HEAD)
/// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html
public func get(fileInfo file: LocationConvertible, headers: [String: String], on container: Container) throws -> Future<File.Info> {
let url = try self.url(file: file, on: container)
let builder = urlBuilder(for: container)
let url = try builder.url(file: file)

let headers = try signer.headers(for: .HEAD, urlString: url.absoluteString, headers: headers, payload: .none)
return try make(request: url, method: .HEAD, headers: headers, data: "".convertToData(), on: container).map(to: File.Info.self) { response in
return try make(request: url, method: .HEAD, headers: headers, data: emptyData(), on: container).map(to: File.Info.self) { response in
try self.check(response)

let bucket = file.bucket ?? self.defaultBucket
Expand Down
5 changes: 4 additions & 1 deletion Sources/S3/Extensions/S3+Put.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ public extension S3 {

/// Upload file to S3
public func put(file: File.Upload, headers: [String: String], on container: Container) throws -> EventLoopFuture<File.Response> {
let url = try self.url(file: file, on: container)
let builder = urlBuilder(for: container)
let url = try builder.url(file: file)

// let url = URL(string: "https://s3.eu-west-2.amazonaws.com/s3-liveui-test/file-hu.txt")!

var awsHeaders: [String: String] = headers
awsHeaders["content-type"] = file.mime.description
Expand Down
5 changes: 3 additions & 2 deletions Sources/S3/Extensions/S3+Service.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ public extension S3 {

/// Get list of buckets
public func buckets(on container: Container) throws -> Future<BucketsInfo> {
let url = try self.url(on: container)
let builder = urlBuilder(for: container)
let url = try builder.plain()
let headers = try signer.headers(for: .GET, urlString: url.absoluteString, payload: .none)
return try make(request: url, method: .GET, headers: headers, data: "".convertToData(), on: container).map(to: BucketsInfo.self) { response in
return try make(request: url, method: .GET, headers: headers, data: emptyData(), on: container).map(to: BucketsInfo.self) { response in
try self.check(response)
return try response.decode(to: BucketsInfo.self)
}
Expand Down
Loading

0 comments on commit 4fe4eeb

Please sign in to comment.