Skip to content
This repository has been archived by the owner on Apr 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #46 from nodes-vapor/vapor-3
Browse files Browse the repository at this point in the history
Prepare for Vapor 3
  • Loading branch information
BrettRToomey authored Nov 6, 2018
2 parents c82be03 + 0b41ef9 commit 7690f3c
Show file tree
Hide file tree
Showing 21 changed files with 4,219 additions and 437 deletions.
8 changes: 5 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2
jobs:
MacOS:
macos:
xcode: "9.0"
xcode: "10.0.0"
steps:
- checkout
- restore_cache:
Expand All @@ -11,9 +11,11 @@ jobs:
- run:
name: Install CMySQL and CTLS
command: |
export HOMEBREW_NO_AUTO_UPDATE=1
brew tap vapor/homebrew-tap
brew install cmysql
brew install ctls
brew install libressl
- run:
name: Build and Run Tests
no_output_timeout: 1800
Expand All @@ -25,12 +27,12 @@ jobs:
command: |
bash <(curl -s https://codecov.io/bash)
- save_cache:
key: v1-spm-deps-{{ checksum "Package.swift" }}
key: v2-spm-deps-{{ checksum "Package.swift" }}
paths:
- .build
Linux:
docker:
- image: brettrtoomey/vapor-ci:0.0.1
- image: nodesvapor/vapor-ci:swift-4.2
steps:
- checkout
- restore_cache:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

Package.resolved
*.xcodeproj
Packages/

Expand Down
2 changes: 1 addition & 1 deletion .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ function_body_length:
variable_name:
min_length:
warning: 2
line_length: 80
line_length: 100
disabled_rules:
- opening_brace
colon:
Expand Down
24 changes: 20 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
// swift-tools-version:4.2
import PackageDescription

let package = Package(
name: "Storage",
products: [
.library(
name: "Storage",
targets: ["Storage"]
)
],
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 2),
.Package(url: "https://github.com/nodes-vapor/data-uri.git", majorVersion: 1),
.Package(url: "https://github.com/nodes-vapor/aws.git", majorVersion: 1),
.Package(url: "https://github.com/manGoweb/MimeLib.git", majorVersion: 1)
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
],
targets: [
.target(
name: "Storage",
dependencies: [
"Vapor"
]
),
.testTarget(
name: "StorageTests",
dependencies: ["Storage"]
)
]
)
69 changes: 26 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Storage 🗄
[![Swift Version](https://img.shields.io/badge/Swift-3-brightgreen.svg)](http://swift.org)
[![Vapor Version](https://img.shields.io/badge/Vapor-2-F6CBCA.svg)](http://vapor.codes)
[![Swift Version](https://img.shields.io/badge/Swift-4.2-brightgreen.svg)](http://swift.org)
[![Vapor Version](https://img.shields.io/badge/Vapor-3-30B6FC.svg)](http://vapor.codes)
[![Circle CI](https://circleci.com/gh/nodes-vapor/storage/tree/master.svg?style=shield)](https://circleci.com/gh/nodes-vapor/storage)
[![codebeat badge](https://codebeat.co/badges/58eeca2c-7b58-4aea-9b09-d80e3b79de19)](https://codebeat.co/projects/github-com-nodes-vapor-storage-master)
[![codecov](https://codecov.io/gh/nodes-vapor/storage/branch/master/graph/badge.svg)](https://codecov.io/gh/nodes-vapor/storage)
Expand All @@ -15,7 +15,7 @@ A package to ease the use of multiple storage and CDN services.
* [Upload a file](#upload-a-file-)
* [Base 64 and data URI](#base-64-and-data-uri-)
* [Download a file](#download-a-file-)
* [Get CDN path](#get-cdn-path)
* [Get CDN path](#get-cdn-path-)
* [Delete a file](#delete-a-file-)
* [Configuration](#configuration-)
* [Network driver](#network-driver-)
Expand All @@ -31,28 +31,7 @@ Update your `Package.swift` file.


## Getting started 🚀

`Storage` offers a [Provider](https://vapor.github.io/documentation/guide/provider.html) and does all configuration through JSON files.

```swift
import Storage
try drop.addProvider(StorageProvider.self)
```

Now, create a JSON file named `Config/storage.json` with the following contents:

```json
{
"driver": "s3",
"bucket": "$AWS_S3_BUCKET",
"accessKey": "$AWS_ACCESS_KEY",
"secretKey": "$AWS_SECRET_KEY",
"host": "s3.amazonaws.com",
"cdnUrl": "$CDN_BASE_URL"
}
```
Learn about [these fields and more](#configuration-).

Storage makes it easy to start uploading and downloading files. Just register a [network driver](#network-driver) and get going.

## Upload a file 🌐

Expand All @@ -63,28 +42,32 @@ Storage.upload(
fileName: String?,
fileExtension: String?,
mime: String?,
folder: String
folder: String,
on container: Container
) throws -> String
```
The aforementioned function will attempt to upload the file using your [selected driver and template](#configuration-) and will return a `String` representing the location of the file.

If you want to upload an image named `profile.png` your call site would look like:
```swift
let path = try Storage.upload(bytes: bytes, fileName: "profile.png")
print(path) //prints `/profile.png`
try Storage.upload(
bytes: bytes,
fileName: "profile.png",
on: req
)
```

#### Base64 and data URI 📡
Is your file a base64 or data URI? No problem!
```swift
Storage.upload(base64: "SGVsbG8sIFdvcmxkIQ==", fileName: "base64.txt")
Storage.upload(dataURI: "data:,Hello%2C%20World!", fileName: "data-uri.txt")
Storage.upload(base64: "SGVsbG8sIFdvcmxkIQ==", fileName: "base64.txt", on: req)
Storage.upload(dataURI: "data:,Hello%2C%20World!", fileName: "data-uri.txt", on: req)
```

#### Remote resources
Download an asset from a URL and then reupload it to your storage server.
```swift
Storage.upload(url: "http://mysite.com/myimage.png", fileName: "profile.png")
Storage.upload(url: "http://mysite.com/myimage.png", fileName: "profile.png", on: req)
```


Expand All @@ -93,7 +76,7 @@ Storage.upload(url: "http://mysite.com/myimage.png", fileName: "profile.png")
To download a file that was previously uploaded you simply use the generated path.
```swift
//download image as `Foundation.Data`
let data = try Storage.get("/images/profile.png")
let data = try Storage.get("/images/profile.png", on: req)
```


Expand Down Expand Up @@ -128,18 +111,18 @@ try Storage.delete("/images/profile.png")

#### Network driver 🔨
The network driver is the module responsible for interacting with your 3rd party service. The default, and currently the only, driver is `s3`.
```json
{
"driver": "s3",
"bucket": "$AWS_S3_BUCKET",
"accessKey": "$AWS_ACCESS_KEY",
"secretKey": "$AWS_SECRET_KEY",
"host": "s3.amazonaws.com",
"cdnUrl": "$CDN_BASE_URL",
"region": "eu-west-1"
}
```swift
import Storage

let driver = S3Driver(
bucket: "bucket",
accessKey: "access",
secretKey: "secret"
)

services.register(driver)
```
The `driver` key is optional and will default to `s3`. `accessKey` and `secretKey` are both required by the S3 driver, while `host`, `bucket` and `region` are all optional. `region` will default to `eu-west-1` and `host` will default to `s3.amazonaws.com` if not provided. The above example uses the environment variables as provided by [Vapor Cloud](https://vapor.cloud/), but you can change this to use hardcoded values although this is not recommended. Another option is to use "fallback values" which can be achieved by using the `:` notion. For example `$AWS_S3_BUCKET:my-bucket` will fallback to `my-bucket` when the `AWS_S3_BUCKET` environment variable is not present.
`bucket`, `accessKey`and `secretKey` are required by the S3 driver, while `template`, `host` and `region` are optional. `region` will default to `eu-west-1` and `host` will default to `s3.amazonaws.com`.

#### Upload path 🛣
A times, you may need to upload files to a different scheme than `/file.ext`. You can achieve this by adding the `"template"` field to your `Config/storage.json`. If the field is omitted it will default to `/#file`.
Expand Down
61 changes: 28 additions & 33 deletions Sources/Storage/FileEntity.swift
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
import Core
import MimeLib

/// Representation of a to-be-uploaded file.
public struct FileEntity {
public enum Error : Swift.Error {
public enum Error: Swift.Error {
case missingFilename
case missingFileExtension
case malformedFileName
}

//TODO(Brett): considering changing all `String` fields to `Bytes`.


/// The raw bytes of the file.
var bytes: Bytes?


var bytes: Data?

// The file's name with the extension.
var fullFileName: String? {
guard let fileName = fileName, let fileExtension = fileExtension else {
return nil
}

return [fileName, fileExtension].joined(separator: ".")
}

/// The file's name without the extension.
var fileName: String?

/// The file's extension.
var fileExtension: String?

/// The folder the file was uploaded from.
var folder: String?

/// The type of the file.
var mime: String?

/**
FileEntity's default initializer.

Expand All @@ -47,7 +43,7 @@ public struct FileEntity {
- mime: The type of the file.
*/
public init(
bytes: Bytes? = nil,
bytes: Data? = nil,
fileName: String? = nil,
fileExtension: String? = nil,
folder: String? = nil,
Expand All @@ -58,7 +54,7 @@ public struct FileEntity {
self.fileExtension = fileExtension
self.folder = folder
self.mime = mime

sanitize()
}
}
Expand All @@ -68,7 +64,7 @@ extension FileEntity {
guard fileName != nil else {
throw Error.missingFilename
}

guard fileExtension != nil else {
throw Error.missingFileExtension
}
Expand All @@ -80,60 +76,59 @@ extension FileEntity {
guard let fileName = fullFileName else {
throw Error.malformedFileName
}

var path = [
fileName
]

if let folder = folder {
path.insert(folder, at: 0)
}

return path.joined(separator: "/")
}
}

extension FileEntity {
mutating func sanitize() {
guard let fileName = fileName, fileName.contains(".") else { return }

let components = fileName.components(separatedBy: ".")

// don't override if a programmer provided an extension
if fileExtension == nil {
fileExtension = components.last
}

self.fileName = components.dropLast().joined(separator: ".")
}



@discardableResult
mutating func loadMimeFromFileExtension() -> Bool {
guard let fileExtension = fileExtension?.lowercased() else { return false }

// MimeLib doesn't support `jpg` so do a check here first
guard fileExtension != "jpg" else {
self.mime = "image/jpeg"
return true
}
guard let mime = Mime.get(fileExtension: fileExtension)?.rawValue else {

guard let mime = getMime(for: fileExtension) else {
return false
}

self.mime = mime
return true
}

@discardableResult
mutating func loadFileExtensionFromMime() -> Bool {
guard let mime = mime else { return false }
guard let fileExtension = Mime.fileExtension(forMime: mime) else {

guard let fileExtension = getExtension(for: mime) else {
return false
}

self.fileExtension = fileExtension
return true
}
Expand Down
Loading

0 comments on commit 7690f3c

Please sign in to comment.