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
airframe-http: #1100 Support unary and n-ary functions in RPC #1126
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1126 +/- ##
==========================================
- Coverage 82.58% 82.43% -0.15%
==========================================
Files 272 276 +4
Lines 10427 10634 +207
Branches 684 686 +2
==========================================
+ Hits 8611 8766 +155
- Misses 1816 1868 +52
Continue to review full report at Codecov.
|
@takezoe @shimamoto I revised airframe-http Endpoint/RPC mapping so that we can support any type of functions including unary, n-ary functions even though primitive type values are involved. I know this PR is fairly complicated, but this change will affect all airframe-http applications, so it's better to have your look. |
Some(argCodec.fromMsgPack(paramValue.toMsgpack)) | ||
case None => | ||
// When the target parameter is not found in the MapValue, try mapping the content body as a whole | ||
argCodec.unpackMsgPack(msgpack) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though I know this is an original behavior of the parameter mapping in airframe-http, this behavior is sometimes convenient to take whole request body as a string, however, it might be a little dangerous because this happens unintentionally when we give a wrong parameter name.
def rpc1(name: String) = {}
{"name": "hello"} => "hello"
{"meme": "hello"} => """{"meme": "hello"}"""
If we need to do this intentionally, it would be better to use HttpRequest
injection than relying on this fallback behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. That's an issue and I'm worrying the compatibility of existing applications using airframe-http. For applications using sbt-airframe (e.g., Conductor), we can fix both of the client and server code at the same time, so we don't need to worry about the compatibility.
An idea is totally deprecating the whole content body mapping as you suggested, and ask users to use Request object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed this as #1129. At least for RPC, which uses generated clients, we don't need to support the whole body mapping
|
||
@RPC | ||
trait MyApi { | ||
def rpc1(p1: String): Unit = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way, how does airframe-http work if we define the same name methods as follows?
def rpc1(p1: String): Unit = {}
def rpc1(p1: String, p1: Int): Unit = {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will throw a runtime exception when building a Router because these methods will be detected as non-unique mappings.
I think we can generate different HTTP path mappings for the same name methods. I'll file a ticket.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #1128
I saw unintuitive cases in array (and map) conversion. def test(p1: Int) = {}
{"p1": [1]} => Seq(null)
def test(p1: Option[Int]) = {}
{"p1": [1]} => Seq(Some(null)) I think it's better to throw an exception in these cases, or use the default value ( |
In the mapping from the query string, Seq doesn't work for unary parameter while it works for a nested object. Is this a limitation in the unary parameter? // Query string: ?name=aaaa&name=bbbb
// This doesn't work
def test(name: Seq[String]) = {}
// This works
case class NestedObject(name: Seq[String])
def test(p1: NestedObject) = {} Even in the second case, if the query string contains a single parameter as |
@takezoe def test(p1: Int) = {}
{"p1": [1]} => Seq(null) <-- throw an Exception for incompatible types
def test(p1: Option[Int]) = {}
{"p1": [1]} => Seq(Some(null)) <-- throw an Excecption for incompatible types |
Thanks for fixing! Please allow me to ask about another corner case. How should def endpoint3(p1: Seq[String]): Unit = {} // => IllegalArgumentException
def endpoint3(p1: Option[Seq[String]]): Unit = {} // => IllegalArgumentException It would be nice if the first case will be |
@takezoe Right. It should bind Seq.empty and None respectively. Added test cases and found how to properly handle these cases (primitive, Seq[primitive], Option[primitive], Option[Seq[primitive]]). Thanks for the comment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great thanks! I have no other cases which don't work expected for now.
Awesome. Your review was really helpful. I appreciate it. |
Addresses #1100