Skip to content

Commit

Permalink
Add grouping of routes by path prefix (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
slashmo authored and joshuawright11 committed Jan 25, 2021
1 parent 19bf5d5 commit 48beccc
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 1 deletion.
20 changes: 20 additions & 0 deletions Sources/Alchemy/Routing/Application+GroupedRoutes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
extension Application {
/// Groups a set of endpoints by a path prefix.
/// All endpoints added in the `configure` closure will
/// be prefixed, but none in the handler chain that continues
/// after the `.grouped`.
///
/// - Parameters:
/// - pathPrefix: The path prefix for all routes
/// defined in the `configure` closure.
/// - configure: A closure for adding routes that will be
/// prefixed by the given path prefix.
/// - Returns: This application for chaining handlers.
@discardableResult
public func grouped(_ pathPrefix: String, configure: (Application) -> Void) -> Self {
Services.router.pathPrefixes.append(pathPrefix)
configure(self)
_ = Services.router.pathPrefixes.popLast()
return self
}
}
5 changes: 4 additions & 1 deletion Sources/Alchemy/Routing/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public final class Router {

/// Current middleware of this router.
var middlewares: [Middleware] = []

var pathPrefixes: [String] = []

/// A trie that holds all the handlers.
private let trie = RouterTrieNode<HTTPMethod, RouterHandler>()
Expand All @@ -42,7 +44,8 @@ public final class Router {
for method: HTTPMethod,
path: String
) {
let splitPath = path.split(separator: "/").map(String.init)
let pathPrefixes = self.pathPrefixes.map { $0.hasPrefix("/") ? String($0.dropFirst()) : $0 }
let splitPath = pathPrefixes + path.split(separator: "/").map(String.init)
let middlewareClosures = self.middlewares.reversed().map(Middleware.intercept)
self.trie.insert(path: splitPath, storageKey: method) {
var next = { request in
Expand Down
47 changes: 47 additions & 0 deletions Tests/AlchemyTests/Routing/RouterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,53 @@ final class RouterTests: XCTestCase {
// Could update the router to automatically add "/" if URI strings are missing them,
// automatically add/remove trailing "/", etc.
}

func testGroupedPathPrefix() throws {
self.app
.grouped("group") { app in
app
.register(.get1)
.register(.get2)
.grouped("nested") { app in
app.register(.post1)
}
.register(.post2)
}
.register(.get3)

XCTAssertEqual(try self.app.request(TestRequest(
method: .GET,
path: "/group\(TestRequest.get1.path)",
response: TestRequest.get1.path
)), TestRequest.get1.response)

XCTAssertEqual(try self.app.request(TestRequest(
method: .GET,
path: "/group\(TestRequest.get2.path)",
response: TestRequest.get2.path
)), TestRequest.get2.response)

XCTAssertEqual(try self.app.request(TestRequest(
method: .POST,
path: "/group/nested\(TestRequest.post1.path)",
response: TestRequest.post1.path
)), TestRequest.post1.response)

XCTAssertEqual(try self.app.request(TestRequest(
method: .POST,
path: "/group\(TestRequest.post2.path)",
response: TestRequest.post2.path
)), TestRequest.post2.response)

// only available under group prefix
XCTAssertNil(try self.app.request(TestRequest.get1))
XCTAssertNil(try self.app.request(TestRequest.get2))
XCTAssertNil(try self.app.request(TestRequest.post1))
XCTAssertNil(try self.app.request(TestRequest.post2))

// defined outside group --> still available without group prefix
XCTAssertEqual(try self.app.request(TestRequest.get3), TestRequest.get3.response)
}
}

/// Runs the specified callback on a request / response.
Expand Down

0 comments on commit 48beccc

Please sign in to comment.