Skip to content
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

adding replyTo field in http request body to submit RFQ #140

Merged
merged 16 commits into from
Feb 6, 2024
Merged

Conversation

jiyoontbd
Copy link
Contributor

@jiyoontbd jiyoontbd commented Jan 20, 2024

http-client:
Add replyTo field to send message request if it was included with RFQ message.
Updated endpoint per spec change.
Created an overloaded method to help elucidate between sendMessage() that sends a non-rfq message or an rfq message without a replyTo field, and sendMessage() that sends specifically an rfq message with a replyTo field

http-server
Updated endpoint per spec change. Now anticipating an optional replyTo field included in the request JSON.

…f exists). wrote tests for client change. rename submitrfq handler to createexchange httpserver handler. checking validity of url in the handler. wrote test for handler change
Copy link

codecov bot commented Jan 20, 2024

Codecov Report

Merging #140 (b074ee3) into main (c9eb53a) will increase coverage by 1.43%.
The diff coverage is 96.07%.

❗ Current head b074ee3 differs from pull request most recent head 9c27b39. Consider uploading reports for the commit 9c27b39 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #140      +/-   ##
==========================================
+ Coverage   76.19%   77.63%   +1.43%     
==========================================
  Files          32       33       +1     
  Lines         752      778      +26     
  Branches       77       74       -3     
==========================================
+ Hits          573      604      +31     
  Misses        133      133              
+ Partials       46       41       -5     
Components Coverage Δ
protocol 85.00% <ø> (+1.45%) ⬆️
httpclient 78.89% <93.93%> (+1.72%) ⬆️

* @throws TbdexResponseException for request or response errors.
*/
fun sendMessage(message: Message) {
fun sendMessage(sendMessageRequest: SendMessageRequest) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads like javascript. We should check at compile-time that replyTo is only present with RFQs. Currently, this does the check at runtime. I'm wondering if there's a more kotlin-idiomatic way to write this.

What do you think of separating the methods?

// Sends messages, including RFQs, but doesn't support replyTo`
sendMessage(message: Message)
// Sends only RFQs and supports replyTo
sendRfq(rfq: Rfq, replyTo: string?)

Or maybe overloaded methods if you want to keep the name sendMessage for both.

// Sends messages, including RFQs, but doesn't support replyTo`
sendMessage(message: Message)
// Sends only RFQs and supports replyTo
sendMessage(rfq: Rfq, replyTo: string?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i did sendMessage(message:Message, replyTo: String? = null), since sendMessage() is used anytime the client is sending any tbdex message. i think the second option can also work...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering what we want the dev ex to be like. should we separate it out at the method level, or should we let the client just use one method and this method takes care of the validation / inclusion of replyTo field in the request?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed with @diehuxx - going with the second approach, with sendMessage(rfq: Rfq, replyTo: string) for the overloaded method.

@@ -99,7 +99,7 @@ class TbdexHttpServer(private val config: TbdexHttpServerConfig) {

route("/exchanges") {
post("/{exchangeId}/rfq") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. just a side q - would we leave the other endpoints as /exchanges/{exchangeId}/order|close?

import tbdex.sdk.protocol.serialization.Json
import kotlin.test.assertContains
import kotlin.test.assertEquals

class SubmitRfqTest : ServerTest() {
class CreateExchangeTest : ServerTest() {
@Test
fun `returns BadRequest if no request body is provided`() = runBlocking {
val response = client.post("/exchanges/123/rfq") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i'd like to hold off on updating the endpoints until we have the new tickets / PRs that phoebe referenced in this comment to update client and server code that incorporates the new endpoint style.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually i should just get rid of the /rfq bit since we've decided that can go, but the one up the air is whether /{exchangeId} should go also. updating

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

call.respond(HttpStatusCode.BadRequest, errorResponse)
return
}

try {
exchangesApi.getExchange(message.metadata.exchangeId.toString())
val errorDetail = ErrorDetail(detail = "RFQ already exists.")
Copy link
Contributor

@diehuxx diehuxx Jan 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tangential to the replyTo stuff, but can you update the catch (_: Exception) below to catch (_: NoSuchElementException) and add comment // exchangesApi.getExchange throws if no existing exchange is found. For a sec I was pretty confused why we were swallowing an error so egregiously.

Further musings: maybe this isn't the most graceful API for ExchangeApi. Personally I would return an empty list rather than throw. But that's a separate PR if we decide to do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed - yea, this try catch is a bit confusing!

in this request handler to create exchange, we want to make sure that exchangesApi.getExchange(exchangeId) does not return an exchange.

this try catch is actually hoping an NoSuchElementException is thrown when an exchange with the exchangeId is not found - which is what we want, because we want to create a new exchange

as you said, we should probably return an empty list if an exchange with the exchangeId is not found instead of throwing an exception.

@jiyoontbd jiyoontbd changed the title adding replyTo field in http request body to submit RFQ adding replyTo field in http request body to submit RFQ Jan 24, 2024
@@ -84,20 +87,41 @@ object TbdexHttpClient {
val pfiServiceEndpoint = getPfiServiceEndpoint(pfiDid)
val url = "$pfiServiceEndpoint/exchanges/$exchangeId/$kind"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If message is a Rfq, this will send to the wrong endpoint. We need to omit $kind if kind == 'rfq'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolutely correct, ty!

* @property rfq Rfq tbdex message received.
* @property replyTo Optional URL to be included in the request to create exchange.
*/
class CreateExchangeRequest(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is only used in tests, can we move it into test/ directory? Or is it intended to be used by consumers of this library in some way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do use this in Tbdexhttpclient#sendMessage method where i'm passing in rfq and replyTo (the new overloaded method)

val body: RequestBody = Json.stringify(CreateExchangeRequest(message, replyTo))
      .toRequestBody(jsonMediaType)

Copy link
Contributor

@diehuxx diehuxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good work @jiyoontbd !

* @throws TbdexResponseException for request or response errors.
* @param message The message to send.
*
* @throws TbdexResponseException for response errors.
*/
fun sendMessage(message: Message) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jiyoontbd what do you think about having a separate fun createExchange(rfq: RFQ, replyTo: String?) function for creating an exchange?

The reason being, there are now two functions that can be called to create an exchange. This one can accept messages of any type (including RFQ), but does not support replyTo URL. The other is also called sendMessage, but you can actually only send an RFQ, not just any message type.

You would essentially have one client method for creating an exchange, which can have a replyTo or not (optional), and a different method for all subsequent messages.

Does that make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea. so you're saying we'd call createExchange() for sending RFQ (with or without replyTo), and sendMessage() for everything else? i'll give that a go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wish we could type guard for sendMessage() to prevent RFQ from being passed in as message in kotlin. so without the type guard, i'd have to check the kind and throw IllegalArgumentException. i'm not sure if i like that dev ex. i suppose we could explicitly say pls don't use this method if you're sending an RFQ!!11 in the ktdocs.

i'm leaning towards keeping the overloaded sendMessage(), would love to hear @diehuxx 's thoughts

Copy link
Contributor

@diehuxx diehuxx Feb 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suppose we could explicitly say pls don't use this method if you're sending an RFQ!!11 in the ktdocs.

I would really prefer not to resort to that. IMO as long as we're leveraging the type system to enforce correct message types and options at compile-time, I can be flexible about how we name methods.

If I were writing this, I would keep the overloaded sendMessage AND add a createExchange endpoint which takes an RFQ and a replyTo. Admittedly, my ruby background is showing in this suggestion, as aliasing methods is common in ruby.

Feel free to take this idea or not. As long as the type system can enforce our choices, @jiyoontbd I trust your judgement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm good with that. the code editor should show the difference between the two. commit here

@@ -208,4 +237,25 @@ object TbdexHttpClient {
errors = errors
)
}

private fun validateMessage(message: Message) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@jiyoontbd jiyoontbd linked an issue Feb 5, 2024 that may be closed by this pull request
@jiyoontbd jiyoontbd merged commit 4dc1e42 into main Feb 6, 2024
3 of 5 checks passed
@jiyoontbd jiyoontbd deleted the 132-replyTo branch February 6, 2024 15:12
@jiyoontbd jiyoontbd linked an issue Feb 6, 2024 that may be closed by this pull request
@jiyoontbd jiyoontbd linked an issue Mar 22, 2024 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants