-
Notifications
You must be signed in to change notification settings - Fork 962
Support nested context paths. #5846
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
I would like to receive feedback on whether this PR is heading in the right direction, as it is currently a draft. After fix direction, I will write unit tests and Java docs based on the BaseContextPathsTest code. When you have time, please take a look 🙇♂️ |
I didn't see that a fluent style is good for nested context paths in terms of API design. Users may find it difficult to know the current depth and parent trees and children trees. How about taking a lambda expression as a second parameter to leverage Kotlin trailing lamdas? I imagined the following style API. We used Server
.builder()
.contextPath("/rest") {
contextPath("/catalog") {
service("/product", new GetProductService())
service("/products", new ProductsHandler())
}
}
.contextPath("/cart") {
contextPath("/foo") {
contextPath("/bar") {
service("/checkout", new CheckoutService())
}
}
}
.contextPath("/gql") {
service("/catalog", new GraphQLService())
} Related work: https://ktor.io/docs/server-routing.html#nested_routes |
@ikhoon nim, Thanks for your guidelines. |
@ikhoon nim, i change code style from fluent style to nested lambda expression. // Without virtual host
sb.baseContextPath("/api")
.contextPath("/context-path/a1", "/context-path/a2")
.nestedContext()
.contextPaths(Set.of("/b1", "/b2"), ctx1 -> ctx1 // ctx1 scope start (lambda function)
.annotatedService(new Object() { ... })
.service("/my-service", new HttpService() { ... })
.contextPaths(Set.of("/c1", "/c2"), ctx2 -> ctx2 // ctx2 scope start (lambda function)
.service("/my-service1", new HttpService() { ... })
.contextPaths(Set.of(...), ctx3 -> ctx3 // ctx3 scope start (lambda function)
.service("/...", new HttpService() { ... })
) // ctx3 scope end.
) // ctx2 scope end.
) // ctx1 scope end.
.contextPaths(Set.of("/b3", "/b4"), ctx11 -> ctx11
.service("/my-service", new HttpService() { ... });
// With virtual host.
sb.virtualHost("foo.com")
.contextPath("/virtual-foo")
.nestedContext()
.contextPaths(Set.of("/a1", "/a2"), ctx -> ctx
.service("/my-service1", new HttpService() { ... })); To get detail code, you can refer to this java code.
It may not be exactly the same as the API you imagined, but since we can represent the scope within a lambda function, I believe it will be more user-friendly than my initial commit. also, java cannot perfectly match to When you have time, please take another look. 🙇♂️ |
@ikhoon nim, gently ping. |
Is it possible to build nested context paths without using |
0d8adb5
to
fd182c8
Compare
@ikhoon nim, sure. I updated example codes and API test codes.. Style1It starts with sb.baseContextPath("/api")
.contextPath("/context-path/a1", "/context-path/a2")
.contextPaths(Set.of("/b1", "/b2"), ctx1 -> ctx1 // ctx1 scope start (lambda function)
.annotatedService(new Object() { ... })
.service("/my-service", new HttpService() { ... })
.contextPaths(Set.of("/c1", "/c2"), ctx2 -> ctx2 // ctx2 scope start (lambda function)
.service("/my-service1", new HttpService() { ... })
.contextPaths(Set.of(...), ctx3 -> ctx3 // ctx3 scope start (lambda function)
.service("/...", new HttpService() { ... })
) // ctx3 scope end.
) // ctx2 scope end.
) // ctx1 scope end.
.contextPaths(Set.of("/b3", "/b4"), ctx11 -> ctx11
.service("/my-service", new HttpService() { ... }); Style2It starts with sb.baseContextPath("/api")
.contextPath(Set.of("/b1", "/b2"), ctx1 -> ctx1 // ctx1 scope start (lambda function)
.annotatedService(new Object() { ... })
.service("/my-service", new HttpService() { ... })
.contextPaths(Set.of("/c1", "/c2"), ctx2 -> ctx2 // ctx2 scope start (lambda function)
.service("/my-service1", new HttpService() { ... })
.contextPaths(Set.of(...), ctx3 -> ctx3 // ctx3 scope start (lambda function)
.service("/...", new HttpService() { ... })
) // ctx3 scope end.
) // ctx2 scope end.
) // ctx1 scope end.
.contextPaths(Set.of("/b3", "/b4"), ctx11 -> ctx11
.service("/my-service", new HttpService() { ... }); When you have time, PTAL 🙇♂️ |
Could you explain the difference between |
8cb306d
to
6a1b4ee
Compare
@ikhoon nim, sorry to late. For
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5846 +/- ##
============================================
- Coverage 74.78% 0 -74.79%
============================================
Files 1877 0 -1877
Lines 79199 0 -79199
Branches 10212 0 -10212
============================================
- Hits 59228 0 -59228
+ Misses 15168 0 -15168
+ Partials 4803 0 -4803 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Motivation:
armeria
supports only1-depth
context paths. Sometimes, user want deeper context paths than 1-depth when they usecontextPaths()
.Modifications:
ContextPathServiceBuilder
tree to support nested context paths.before()
is for going back previous node.contextPath()
is for adding context paths and making childContextPathServiceBuilder
parent()
: To give ChildcontextPathServiceBuilder
parent object. because of this, child can return parent object whenand()
is called.virtualHostbuilder()
: to relay their context viaContextPathServiceBuilder
tree.mergeContextPaths()
: to merge previous context paths and current context paths.Example
Result:
/rest/catalog/product
=>getProductService
/rest/catalog/products
=>productsHandler
/rest/cart/foo/bar/checkout
=>checkoutService
/gql/catalog
=>GraphQLService