Created basic Respondable protocol structure

This commit is contained in:
Caleb Kleveter 2019-01-30 14:05:46 -06:00
commit a1c5960fad
No known key found for this signature in database
GPG Key ID: B38DBD5CF2C98D69
8 changed files with 356 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.DS_Store
/.build
/Packages
/*.xcodeproj

160
Package.resolved Normal file
View File

@ -0,0 +1,160 @@
{
"object": {
"pins": [
{
"package": "Console",
"repositoryURL": "https://github.com/vapor/console.git",
"state": {
"branch": null,
"revision": "d6cf07af59ae63cd95c4b5f98cf1f25627750fd1",
"version": "3.1.0"
}
},
{
"package": "Core",
"repositoryURL": "https://github.com/vapor/core.git",
"state": {
"branch": null,
"revision": "1794ff138bd669175a2528d27695028d7cb30471",
"version": "3.5.0"
}
},
{
"package": "Crypto",
"repositoryURL": "https://github.com/vapor/crypto.git",
"state": {
"branch": null,
"revision": "bce9ac891c9b33fc045deda713e0d976d13b749a",
"version": "3.3.1"
}
},
{
"package": "DatabaseKit",
"repositoryURL": "https://github.com/vapor/database-kit.git",
"state": {
"branch": null,
"revision": "3557894af50914e134803a684b42a9ea6eefaea2",
"version": "1.3.2"
}
},
{
"package": "HTTP",
"repositoryURL": "https://github.com/vapor/http.git",
"state": {
"branch": null,
"revision": "4c8c655932fdb409c11549eadabf27d3d61bc24c",
"version": "3.1.7"
}
},
{
"package": "Multipart",
"repositoryURL": "https://github.com/vapor/multipart.git",
"state": {
"branch": null,
"revision": "bd7736c5f28e48ed8b683dcc9df3dcd346064c2b",
"version": "3.0.3"
}
},
{
"package": "Routing",
"repositoryURL": "https://github.com/vapor/routing.git",
"state": {
"branch": null,
"revision": "3219e328491b0853b8554c5a694add344d2c6cfb",
"version": "3.0.1"
}
},
{
"package": "Service",
"repositoryURL": "https://github.com/vapor/service.git",
"state": {
"branch": null,
"revision": "281a70b69783891900be31a9e70051b6fe19e146",
"version": "1.0.0"
}
},
{
"package": "swift-nio",
"repositoryURL": "https://github.com/apple/swift-nio.git",
"state": {
"branch": null,
"revision": "03c541a24dd0558c942b15d8464eb75d70a921c4",
"version": "1.12.1"
}
},
{
"package": "swift-nio-ssl",
"repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
"state": {
"branch": null,
"revision": "0f3999f3e3c359cc74480c292644c3419e44a12f",
"version": "1.4.0"
}
},
{
"package": "swift-nio-ssl-support",
"repositoryURL": "https://github.com/apple/swift-nio-ssl-support.git",
"state": {
"branch": null,
"revision": "c02eec4e0e6d351cd092938cf44195a8e669f555",
"version": "1.0.0"
}
},
{
"package": "swift-nio-zlib-support",
"repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git",
"state": {
"branch": null,
"revision": "37760e9a52030bb9011972c5213c3350fa9d41fd",
"version": "1.0.0"
}
},
{
"package": "TemplateKit",
"repositoryURL": "https://github.com/vapor/template-kit.git",
"state": {
"branch": null,
"revision": "aff2d6fc65bfd04579b0201b31a8d6720239c1cf",
"version": "1.1.1"
}
},
{
"package": "URLEncodedForm",
"repositoryURL": "https://github.com/vapor/url-encoded-form.git",
"state": {
"branch": null,
"revision": "932024f363ee5ff59059cf7d67194a1c271d3d0c",
"version": "1.0.5"
}
},
{
"package": "Validation",
"repositoryURL": "https://github.com/vapor/validation.git",
"state": {
"branch": null,
"revision": "4de213cf319b694e4ce19e5339592601d4dd3ff6",
"version": "2.1.1"
}
},
{
"package": "Vapor",
"repositoryURL": "https://github.com/vapor/vapor.git",
"state": {
"branch": null,
"revision": "54cbf396147aba77ecb32e7fc75a3d13251551b1",
"version": "3.1.1"
}
},
{
"package": "WebSocket",
"repositoryURL": "https://github.com/vapor/websocket.git",
"state": {
"branch": null,
"revision": "21eb4773e25a8ff96fe347a31fe106900a69fa6a",
"version": "1.1.1"
}
}
]
},
"version": 1
}

17
Package.swift Normal file
View File

@ -0,0 +1,17 @@
// swift-tools-version:4.2
import PackageDescription
let package = Package(
name: "ModelResponse",
products: [
.library(name: "ModelResponse", targets: ["ModelResponse"]),
],
dependencies: [
.package(url: "https://github.com/vapor/vapor.git", from: "3.1.1")
],
targets: [
.target(name: "ModelResponse", dependencies: ["Vapor"]),
.testTarget(name: "ModelResponseTests", dependencies: ["ModelResponse"]),
]
)

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# ModelResponse
A description of this package.

View File

@ -0,0 +1,41 @@
import Vapor
public protocol Respondable {
associatedtype Result: ResponseEncodable
func response(on container: Container) -> Future<Self.Result>
}
extension Respondable where Self: ResponseEncodable {
public func response(on container: Container) -> Future<Self> {
return container.future(self)
}
}
extension Array: Respondable where Element: Respondable, Element.Result: Content {
public typealias Result = [Element.Result]
public func response(on container: Container) -> Future<[Element.Result]> {
return self.map { $0.response(on: container) }.flatten(on: container)
}
}
extension Dictionary: Respondable where Key == String, Value: Respondable, Value.Result: Content {
public typealias Result = [String: Value.Result]
public func response(on container: Container) -> Future<[String: Value.Result]> {
return self.map { key, value in value.response(on: container).and(result: key) }.flatten(on: container).map { list in
return list.reduce(into: [:]) { result, pair in
result[pair.1] = pair.0
}
}
}
}
extension Future: Respondable where T: Respondable {
public typealias Result = T.Result
public func response(on container: Container) -> EventLoopFuture<T.Result> {
return self.flatMap { t in t.response(on: container) }
}
}

7
Tests/LinuxMain.swift Normal file
View File

@ -0,0 +1,7 @@
import XCTest
import ModelResponseTests
var tests = [XCTestCaseEntry]()
tests += ModelResponseTests.allTests()
XCTMain(tests)

View File

@ -0,0 +1,115 @@
import XCTest
import Vapor
@testable import ModelResponse
final class ModelResponseTests: XCTestCase {
var app: Application!
override func setUp() {
super.setUp()
self.app = try! Application(config: Config.default(), environment: Environment.detect(), services: Services.default())
}
override func tearDown() {
self.app = nil
super.tearDown()
}
func testUser()throws {
let user = User(name: "Caleb", password: "bad-password", age: 42)
let response = try user.response(on: self.app).wait()
XCTAssertEqual(response.age, 42)
XCTAssertEqual(response.name, "Caleb")
}
func testArray()throws {
let users = [User(name: "Caleb", password: "bad-password", age: 42), User(name: "Tanner", password: "0101", age: 31)]
let response = try users.response(on: self.app).wait()
XCTAssertEqual(response[0].age, 42)
XCTAssertEqual(response[0].name, "Caleb")
XCTAssertEqual(response[1].age, 31)
XCTAssertEqual(response[1].name, "Tanner")
}
func testDictionary()throws {
let users = [
"Caleb": User(name: "Caleb", password: "bad-password", age: 42),
"Tanner": User(name: "Tanner", password: "0101", age: 31)
]
let response = try users.response(on: self.app).wait()
XCTAssertEqual(response["Caleb"]?.age, 42)
XCTAssertEqual(response["Caleb"]?.name, "Caleb")
XCTAssertEqual(response["Tanner"]?.age, 31)
XCTAssertEqual(response["Tanner"]?.name, "Tanner")
}
func testFuture()throws {
let user = self.app.future(User(name: "Caleb", password: "bad-password", age: 42))
let response = try user.response(on: self.app).wait()
XCTAssertEqual(response.age, 42)
XCTAssertEqual(response.name, "Caleb")
}
func testFutureArrayDictionary()throws {
let user = self.app.future(["users": [User(name: "Caleb", password: "bad-password", age: 42)]])
let response = try user.response(on: self.app).wait()
XCTAssertEqual(response["users"]?[0].age, 42)
XCTAssertEqual(response["users"]?[0].name, "Caleb")
}
func testDefault()throws {
let token = Token(hash: "#AF0091")
let response = try token.response(on: self.app).wait()
XCTAssertEqual(response.hash, "#AF0091")
}
static var allTests = [
("testUser", testUser),
("testArray", testArray),
("testDictionary", testDictionary),
("testFuture", testFuture),
("testFutureArrayDictionary", testFutureArrayDictionary),
("testDefault", testDefault)
]
}
final class User {
let password: String
let name: String
let age: Int
init(name: String, password: String, age: Int) {
self.password = password
self.name = name
self.age = age
}
}
extension User: Respondable {
struct Result: Content {
let name: String
let age: Int
}
func response(on container: Container) -> EventLoopFuture<User.Result> {
return container.future(User.Result(name: self.name, age: self.age))
}
}
final class Token: Content, Respondable {
typealias Result = Token
let hash: String
init(hash: String) {
self.hash = hash
}
}

View File

@ -0,0 +1,9 @@
import XCTest
#if !os(macOS)
public func allTests() -> [XCTestCaseEntry] {
return [
testCase(ModelResponseTests.allTests),
]
}
#endif