summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-10-30 17:13:09 +0100
committerChristian Grothoff <christian@grothoff.org>2020-10-30 17:13:09 +0100
commit09c40af96fa338f6ba57a24df7d43b65384420f9 (patch)
tree59cd6a3b6efec590cb0f3872b08792a7f7387f7f
parentdf8395e9c528a3eed89ce0814bc8b5c37daae267 (diff)
downloadmerchant-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.h92
-rw-r--r--src/lib/merchant_api_post_orders.c136
-rw-r--r--src/testing/testing_api_cmd_post_orders.c40
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);