diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-10-30 17:13:09 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-10-30 17:13:09 +0100 |
commit | 09c40af96fa338f6ba57a24df7d43b65384420f9 (patch) | |
tree | 59cd6a3b6efec590cb0f3872b08792a7f7387f7f | |
parent | df8395e9c528a3eed89ce0814bc8b5c37daae267 (diff) | |
download | merchant-09c40af96fa338f6ba57a24df7d43b65384420f9.tar.gz merchant-09c40af96fa338f6ba57a24df7d43b65384420f9.tar.bz2 merchant-09c40af96fa338f6ba57a24df7d43b65384420f9.zip |
properly parse and return out-of-stock reply, fix 2 FIXMEs
-rw-r--r-- | src/include/taler_merchant_service.h | 92 | ||||
-rw-r--r-- | src/lib/merchant_api_post_orders.c | 136 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_post_orders.c | 40 |
3 files changed, 180 insertions, 88 deletions
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index 656ae76e..88a33ba5 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -1223,27 +1223,93 @@ TALER_MERCHANT_product_delete_cancel ( /** - * Handle to a POST /orders operation + * Handle to a POST /private/orders operation */ -// FIXME: rename: Operation => Handle! -struct TALER_MERCHANT_PostOrdersOperation; +struct TALER_MERCHANT_PostOrdersHandle; + + +/** + * Possible details from a reply to POST /private/orders. + */ +struct TALER_MERCHANT_PostOrdersReply +{ + + /** + * HTTP response details. HTTP status code + * determines what parts of @e details are valid. + */ + struct TALER_MERCHANT_HttpResponse hr; + + /** + * Details of the reply, depending on the HTTP + * status code. + */ + union { + + /** + * Details provided if the @e hr.http_status is + * #MHD_HTTP_OK and the order was created. + */ + struct { + + /** + * order id of the newly created order + */ + const char *order_id; + + /** + * the claim token generated by the merchant, + * (NULL if it was NOT generated). + */ + const struct TALER_ClaimTokenP *token; + + } ok; + + /** + * Details provided if the @e hr.http_status is + * #MHD_HTTP_GONE because a product was out of stock. + */ + struct { + /** + * ID of the product of the order that is out of + * stock. + */ + const char *product_id; + + /** + * How many units were requested by the order. + */ + uint64_t requested_quantity; + + /** + * How many units are actually still in stock. + */ + uint64_t available_quantity; + + /** + * When does the backend expect the stock to be + * restocked? 0 for unknown. + */ + struct GNUNET_TIME_Absolute restock_expected; + + } gone; + + } details; + +}; + /** * Callbacks of this type are used to serve the result of submitting a * POST /orders request to a merchant. * * @param cls closure - * @param hr HTTP response details - * @param order_id order id of the newly created order - * @param token the claim token generated by the merchant (NULL if - * it wasn't generated). + * @param por response details */ typedef void (*TALER_MERCHANT_PostOrdersCallback) ( void *cls, - const struct TALER_MERCHANT_HttpResponse *hr, - const char *order_id, - const struct TALER_ClaimTokenP *token); + const struct TALER_MERCHANT_PostOrdersReply *por); /** @@ -1259,7 +1325,7 @@ typedef void * @param cb_cls closure for @a cb * @return a handle for this request, NULL on error */ -struct TALER_MERCHANT_PostOrdersOperation * +struct TALER_MERCHANT_PostOrdersHandle * TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx, const char *backend_url, const json_t *order, @@ -1305,7 +1371,7 @@ struct TALER_MERCHANT_InventoryProduct * @param cb_cls closure for @a cb * @return a handle for this request, NULL on error */ -struct TALER_MERCHANT_PostOrdersOperation * +struct TALER_MERCHANT_PostOrdersHandle * TALER_MERCHANT_orders_post2 ( struct GNUNET_CURL_Context *ctx, const char *backend_url, @@ -1329,7 +1395,7 @@ TALER_MERCHANT_orders_post2 ( */ void TALER_MERCHANT_orders_post_cancel ( - struct TALER_MERCHANT_PostOrdersOperation *po); + struct TALER_MERCHANT_PostOrdersHandle *po); /** diff --git a/src/lib/merchant_api_post_orders.c b/src/lib/merchant_api_post_orders.c index a6f1ec71..9c45ad2c 100644 --- a/src/lib/merchant_api_post_orders.c +++ b/src/lib/merchant_api_post_orders.c @@ -37,7 +37,7 @@ /** * @brief A POST /orders Handle */ -struct TALER_MERCHANT_PostOrdersOperation +struct TALER_MERCHANT_PostOrdersHandle { /** @@ -76,7 +76,7 @@ struct TALER_MERCHANT_PostOrdersOperation * Function called when we're done processing the * HTTP POST /orders request. * - * @param cls the `struct TALER_MERCHANT_PostOrdersOperation` + * @param cls the `struct TALER_MERCHANT_PostOrdersHandle` * @param response_code HTTP response code, 0 on error * @param response response body, NULL if not JSON */ @@ -85,48 +85,50 @@ handle_post_order_finished (void *cls, long response_code, const void *response) { - struct TALER_MERCHANT_PostOrdersOperation *po = cls; - const char *order_id = NULL; - struct TALER_ClaimTokenP token = {0}; + struct TALER_MERCHANT_PostOrdersHandle *po = cls; const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - bool has_token = ((NULL != json_object_get (json, - "token")) && - (false == json_is_null (json_object_get (json, - "token")))); - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("order_id", - &order_id), - (! has_token) ? - GNUNET_JSON_spec_end () : - GNUNET_JSON_spec_fixed_auto ("token", - &token), - GNUNET_JSON_spec_end () + struct TALER_MERCHANT_PostOrdersReply por = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json }; - + struct TALER_ClaimTokenP token = {0}; + po->job = NULL; switch (response_code) { case 0: - hr.ec = TALER_EC_INVALID_RESPONSE; + por.hr.ec = TALER_EC_INVALID_RESPONSE; break; case MHD_HTTP_OK: - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) { - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_PROPOSAL_REPLY_MALFORMED; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("order_id", + &por.details.ok.order_id), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("token", + &token)), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + por.hr.http_status = 0; + por.hr.ec = TALER_EC_PROPOSAL_REPLY_MALFORMED; + } + else + { + if (! GNUNET_is_zero (&token)) + por.details.ok.token = &token; + } } break; case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + por.hr.ec = TALER_JSON_get_error_code (json); + por.hr.hint = TALER_JSON_get_error_hint (json); /* This should never happen, either us or the merchant is buggy (or API version conflict); just pass JSON reply to the application */ @@ -136,51 +138,75 @@ handle_post_order_finished (void *cls, of the signatures is invalid; as we checked them, this should never happen, we should pass the JSON reply to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + por.hr.ec = TALER_JSON_get_error_code (json); + por.hr.hint = TALER_JSON_get_error_hint (json); break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + por.hr.ec = TALER_JSON_get_error_code (json); + por.hr.hint = TALER_JSON_get_error_hint (json); break; case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + por.hr.ec = TALER_JSON_get_error_code (json); + por.hr.hint = TALER_JSON_get_error_hint (json); break; case MHD_HTTP_GONE: /* The quantity of some product requested was not available. */ - // FIXME: parse the OutOfStockResponse. - break; + { + + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ( + "product_id", + &por.details.gone.product_id), + GNUNET_JSON_spec_uint64 ( + "requested_quantity", + &por.details.gone.requested_quantity), + GNUNET_JSON_spec_uint64 ( + "available_quantity", + &por.details.gone.available_quantity), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_absolute_time ( + "restock_expected", + &por.details.gone.restock_expected)), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + por.hr.http_status = 0; + por.hr.ec = TALER_EC_PROPOSAL_REPLY_MALFORMED; + } + break; + } case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + por.hr.ec = TALER_JSON_get_error_code (json); + por.hr.hint = TALER_JSON_get_error_hint (json); break; default: /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); + por.hr.ec = TALER_JSON_get_error_code (json); + por.hr.hint = TALER_JSON_get_error_hint (json); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d\n", (unsigned int) response_code, - (int) hr.ec); + (int) por.hr.ec); GNUNET_break_op (0); break; } po->cb (po->cb_cls, - &hr, - order_id, - has_token ? &token : NULL); - if (MHD_HTTP_OK == response_code) - GNUNET_JSON_parse_free (spec); + &por); TALER_MERCHANT_orders_post_cancel (po); } -struct TALER_MERCHANT_PostOrdersOperation * +struct TALER_MERCHANT_PostOrdersHandle * TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx, const char *backend_url, const json_t *order, @@ -203,7 +229,7 @@ TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx, } -struct TALER_MERCHANT_PostOrdersOperation * +struct TALER_MERCHANT_PostOrdersHandle * TALER_MERCHANT_orders_post2 ( struct GNUNET_CURL_Context *ctx, const char *backend_url, @@ -218,11 +244,11 @@ TALER_MERCHANT_orders_post2 ( TALER_MERCHANT_PostOrdersCallback cb, void *cb_cls) { - struct TALER_MERCHANT_PostOrdersOperation *po; + struct TALER_MERCHANT_PostOrdersHandle *po; json_t *req; CURL *eh; - po = GNUNET_new (struct TALER_MERCHANT_PostOrdersOperation); + po = GNUNET_new (struct TALER_MERCHANT_PostOrdersHandle); po->ctx = ctx; po->cb = cb; po->cb_cls = cb_cls; @@ -327,7 +353,7 @@ TALER_MERCHANT_orders_post2 ( void TALER_MERCHANT_orders_post_cancel ( - struct TALER_MERCHANT_PostOrdersOperation *po) + struct TALER_MERCHANT_PostOrdersHandle *po) { if (NULL != po->job) { diff --git a/src/testing/testing_api_cmd_post_orders.c b/src/testing/testing_api_cmd_post_orders.c index b9ea27e9..b4a56ee3 100644 --- a/src/testing/testing_api_cmd_post_orders.c +++ b/src/testing/testing_api_cmd_post_orders.c @@ -68,7 +68,7 @@ struct OrdersState /** * The /orders operation handle. */ - struct TALER_MERCHANT_PostOrdersOperation *po; + struct TALER_MERCHANT_PostOrdersHandle *po; /** * The (initial) POST /orders/$ID/claim operation handle. @@ -243,26 +243,20 @@ orders_claim_cb (void *cls, * method. * * @param cls closure. - * @param hr HTTP response - * @param order_id order id of the orders. - * @param claim_token claim token + * @param por details about the response */ static void order_cb (void *cls, - const struct TALER_MERCHANT_HttpResponse *hr, - const char *order_id, - const struct TALER_ClaimTokenP *claim_token) + const struct TALER_MERCHANT_PostOrdersReply *por) { struct OrdersState *ps = cls; ps->po = NULL; - if (NULL != claim_token) - ps->claim_token = *claim_token; - if (ps->http_status != hr->http_status) + if (ps->http_status != por->hr.http_status) { TALER_LOG_ERROR ("Given vs expected: %u(%d) vs %u\n", - hr->http_status, - (int) hr->ec, + por->hr.http_status, + (int) por->hr.ec, ps->http_status); TALER_TESTING_FAIL (ps->is); } @@ -272,12 +266,14 @@ order_cb (void *cls, TALER_TESTING_interpreter_next (ps->is); return; } - switch (hr->http_status) + switch (por->hr.http_status) { case MHD_HTTP_OK: - ps->order_id = GNUNET_strdup (order_id); + if (NULL != por->details.ok.token) + ps->claim_token = *por->details.ok.token; + ps->order_id = GNUNET_strdup (por->details.ok.order_id); if ((NULL != ps->expected_order_id) && - (0 != strcmp (order_id, + (0 != strcmp (por->details.ok.order_id, ps->expected_order_id))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -290,6 +286,7 @@ order_cb (void *cls, const struct TALER_TESTING_Command *order_cmd; const struct TALER_ClaimTokenP *prev_token; struct TALER_ClaimTokenP zero_token = {0}; + order_cmd = TALER_TESTING_interpreter_lookup_command ( ps->is, ps->duplicate_of); @@ -303,10 +300,10 @@ order_cb (void *cls, TALER_TESTING_interpreter_fail (ps->is); return; } - if (NULL == claim_token) + if (NULL == por->details.ok.token) prev_token = &zero_token; if (0 != GNUNET_memcmp (prev_token, - claim_token)) + por->details.ok.token)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Claim tokens for identical requests do not match\n"); @@ -315,17 +312,20 @@ order_cb (void *cls, } } break; + case MHD_HTTP_GONE: + TALER_TESTING_interpreter_next (ps->is); + return; case MHD_HTTP_CONFLICT: TALER_TESTING_interpreter_next (ps->is); return; default: { - char *s = json_dumps (hr->reply, + char *s = json_dumps (por->hr.reply, JSON_COMPACT); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected status code from /orders: %u (%d) at %s; JSON: %s\n", - hr->http_status, - hr->ec, + por->hr.http_status, + (int) por->hr.ec, TALER_TESTING_interpreter_get_current_label (ps->is), s); GNUNET_free (s); |