-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c292c9f
Showing
45 changed files
with
2,915 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.build | ||
.swiftpm/** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
5.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// | ||
// Database.swift | ||
// Meridian | ||
// | ||
// Created by Soroush Khanlou on 8/29/20. | ||
// | ||
|
||
import Foundation | ||
import Meridian | ||
|
||
struct Todo: Codable { | ||
var id: UUID | ||
var title: String | ||
var completed: Bool = false | ||
var order: Int | ||
|
||
var url: String { | ||
return "https://meridian-demo.herokuapp.com/todos/\(id)" | ||
} | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case id, title, completed, url, order | ||
} | ||
|
||
init(from decoder: Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
id = UUID() | ||
title = try container.decode(String.self, forKey: .title) | ||
self.order = try container.decodeIfPresent(Int.self, forKey: .order) ?? -1 | ||
} | ||
|
||
init(title: String, completed: Bool) { | ||
self.id = UUID() | ||
self.title = title | ||
self.completed = completed | ||
self.order = -1 | ||
} | ||
|
||
func encode(to encoder: Encoder) throws { | ||
var container = encoder.container(keyedBy: CodingKeys.self) | ||
try container.encode(id, forKey: .id) | ||
try container.encode(title, forKey: .title) | ||
try container.encode(completed, forKey: .completed) | ||
try container.encode(url, forKey: .url) | ||
try container.encode(order, forKey: .order) | ||
} | ||
} | ||
|
||
final class Database { | ||
var todos: [Todo] | ||
|
||
init() { | ||
todos = [] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// | ||
// Todos.swift | ||
// | ||
// | ||
// Created by Soroush Khanlou on 9/2/20. | ||
// | ||
|
||
import Foundation | ||
import Meridian | ||
|
||
// Specs | ||
// https://www.todobackend.com/specs/index.html?https://meridian-demo.herokuapp.com/todos | ||
|
||
extension Response { | ||
func allowCORS() -> Response { | ||
return self.additionalHeaders([ | ||
"Access-Control-Allow-Origin": "*", | ||
"Access-Control-Allow-Headers": "X-Requested-With, Origin, Content-Type, Accept", | ||
"Access-Control-Allow-Methods": "POST, GET, PUT, OPTIONS, DELETE, PATCH", | ||
]) | ||
} | ||
} | ||
|
||
struct ListTodos: Route { | ||
|
||
static let route: RouteMatcher = "/todos" | ||
|
||
@EnvironmentObject var database: Database | ||
|
||
func execute() throws -> Response { | ||
JSON(database.todos) | ||
.allowCORS() | ||
} | ||
|
||
} | ||
|
||
struct ClearTodos: Route { | ||
static let route: RouteMatcher = .delete("/todos") | ||
|
||
@EnvironmentObject var database: Database | ||
|
||
func execute() throws -> Response { | ||
self.database.todos = [] | ||
return JSON(database.todos) | ||
.allowCORS() | ||
} | ||
} | ||
|
||
struct CreateTodo: Route { | ||
static let route: RouteMatcher = .post("/todos") | ||
|
||
@JSONBody var todo: Todo | ||
|
||
@EnvironmentObject var database: Database | ||
|
||
func execute() throws -> Response { | ||
self.database.todos.append(todo) | ||
return JSON(todo) | ||
.statusCode(.created) | ||
.allowCORS() | ||
} | ||
} | ||
|
||
struct ShowTodo: Route { | ||
static let route: RouteMatcher = "/todos/\(.id)" | ||
|
||
@URLParameter(key: .id) var id: String | ||
|
||
@EnvironmentObject var database: Database | ||
|
||
func execute() throws -> Response { | ||
guard let todo = database.todos.first(where: { $0.id.uuidString == id }) else { | ||
throw NoRouteFound() | ||
} | ||
return JSON(todo).allowCORS() | ||
} | ||
|
||
} | ||
|
||
struct TodoPatch: Codable { | ||
var title: String? | ||
var completed: Bool? | ||
var order: Int? | ||
} | ||
|
||
struct EditTodo: Route { | ||
static let route: RouteMatcher = .patch("/todos/\(.id)") | ||
|
||
@URLParameter(key: .id) var id: String | ||
|
||
@JSONBody var patch: TodoPatch | ||
|
||
@EnvironmentObject var database: Database | ||
|
||
func execute() throws -> Response { | ||
guard let index = database.todos.firstIndex(where: { $0.id.uuidString == id }) else { | ||
throw NoRouteFound() | ||
} | ||
if let newTitle = patch.title { | ||
database.todos[index].title = newTitle | ||
} | ||
if let newCompleted = patch.completed { | ||
database.todos[index].completed = newCompleted | ||
} | ||
if let newOrder = patch.order { | ||
database.todos[index].order = newOrder | ||
} | ||
return JSON(database.todos[index]).allowCORS() | ||
} | ||
} | ||
|
||
struct DeleteTodo: Route { | ||
static let route: RouteMatcher = .delete("/todos/\(.id)") | ||
|
||
@URLParameter(key: .id) var id: String | ||
|
||
@EnvironmentObject var database: Database | ||
|
||
func execute() throws -> Response { | ||
database.todos.removeAll(where: { $0.id.uuidString == id }) | ||
return EmptyResponse().allowCORS() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// | ||
// main.swift | ||
// MeridianDemo | ||
// | ||
// Created by Soroush Khanlou on 8/26/20. | ||
// | ||
|
||
import Foundation | ||
import Backtrace | ||
import Meridian | ||
|
||
Backtrace.install() | ||
|
||
let app = Server( | ||
routes: [ | ||
DeleteTodo.self, | ||
EditTodo.self, | ||
ShowTodo.self, | ||
ClearTodos.self, | ||
CreateTodo.self, | ||
ListTodos.self, | ||
], | ||
errorRenderer: JSONErrorRenderer.self | ||
) | ||
.environmentObject(Database()) | ||
|
||
app.listen() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import XCTest | ||
|
||
import MeridianTests | ||
|
||
var tests = [XCTestCaseEntry]() | ||
tests += MeridianTests.__allTests() | ||
|
||
XCTMain(tests) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// EnvironmentStorage.swift | ||
// Meridian | ||
// | ||
// Created by Soroush Khanlou on 8/29/20. | ||
// | ||
|
||
import Foundation | ||
|
||
public struct EnvironmentKey: Hashable { | ||
private let id = UUID() | ||
|
||
public init() { | ||
|
||
} | ||
} | ||
|
||
extension EnvironmentKey { | ||
public static let dateFormatter = EnvironmentKey() | ||
} | ||
|
||
final class EnvironmentStorage { | ||
|
||
static let shared = EnvironmentStorage() | ||
|
||
var objects: [AnyObject] = [] | ||
|
||
var keyedObjects: [EnvironmentKey: AnyObject] = [:] | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// | ||
// ErrorRenderer.swift | ||
// Meridian | ||
// | ||
// Created by Soroush Khanlou on 8/30/20. | ||
// | ||
|
||
import Foundation | ||
|
||
public protocol ErrorRenderer { | ||
|
||
init(error: Error) | ||
|
||
func render() throws -> Response | ||
|
||
} | ||
|
||
struct ErrorContainer: Codable { | ||
let message: String | ||
} | ||
|
||
public struct JSONErrorRenderer: ErrorRenderer { | ||
|
||
public let error: Error | ||
|
||
public init(error: Error) { | ||
self.error = error | ||
} | ||
|
||
public func render() throws -> Response { | ||
JSON(ErrorContainer(message: (error as? ErrorWithMessage)?.message ?? "An error occurred")) | ||
.statusCode( (error as? ErrorWithStatusCode)?.statusCode ?? .badRequest) | ||
} | ||
} |
Oops, something went wrong.