summaryrefslogtreecommitdiff
path: root/src/lib/merchant_api_get_tips.c
diff options
context:
space:
mode:
authorJonathan Buchanan <jonathan.russ.buchanan@gmail.com>2020-06-10 01:44:32 -0400
committerJonathan Buchanan <jonathan.russ.buchanan@gmail.com>2020-06-10 01:44:32 -0400
commit26d5adb7e9ea120b356f40b444f410973c8a83d4 (patch)
tree179c5625ba76c1b5cae36031b7f13ede9ae0223d /src/lib/merchant_api_get_tips.c
parent7a9c8b206332c3500ffd733b79aa56b0ea01338a (diff)
downloadmerchant-26d5adb7e9ea120b356f40b444f410973c8a83d4.tar.gz
merchant-26d5adb7e9ea120b356f40b444f410973c8a83d4.tar.bz2
merchant-26d5adb7e9ea120b356f40b444f410973c8a83d4.zip
implementation of GET /private/tips
Diffstat (limited to 'src/lib/merchant_api_get_tips.c')
-rw-r--r--src/lib/merchant_api_get_tips.c369
1 files changed, 369 insertions, 0 deletions
diff --git a/src/lib/merchant_api_get_tips.c b/src/lib/merchant_api_get_tips.c
index 05f8e7b4..63bcb940 100644
--- a/src/lib/merchant_api_get_tips.c
+++ b/src/lib/merchant_api_get_tips.c
@@ -19,3 +19,372 @@
* @brief Implementation of the GET /private/tips request of the merchant's HTTP API
* @author Jonathan Buchanan
*/
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_merchant_service.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a GET /private/tips operation.
+ */
+struct TALER_MERCHANT_TipsGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TipsGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse tip information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) tip order data
+ * @param tgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static int
+parse_tips (const json_t *ia,
+ struct TALER_MERCHANT_TipsGetHandle *tgh)
+{
+ unsigned int tes_len = json_array_size (ia);
+ struct TALER_MERCHANT_TipEntry tes[tes_len];
+ size_t index;
+ json_t *value;
+ int ret;
+
+ ret = GNUNET_OK;
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_TipEntry *ie = &tes[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint64 ("row_id",
+ &ie->row_id),
+ GNUNET_JSON_spec_fixed_auto ("tip_id",
+ &ie->tip_id),
+ TALER_JSON_spec_amount ("tip_amount",
+ &ie->tip_amount),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ret = GNUNET_SYSERR;
+ continue;
+ }
+ if (GNUNET_SYSERR == ret)
+ break;
+ }
+ if (GNUNET_OK == ret)
+ {
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = MHD_HTTP_OK
+ };
+
+ tgh->cb (tgh->cb_cls,
+ &hr,
+ tes_len,
+ tes);
+ tgh->cb = NULL; /* just to be sure */
+ }
+ return ret;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/tips request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TipsGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param json response body, NULL if not in JSON
+ */
+static void
+handle_get_tips_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_TipsGetHandle *tgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/tips response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ json_t *tips;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_json ("tips",
+ &tips),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ hr.http_status = 0;
+ hr.ec = TALER_EC_INVALID_RESPONSE;
+ }
+ else
+ {
+ if ( (! json_is_array (tips)) ||
+ (GNUNET_OK ==
+ parse_tips (tips,
+ tgh)) )
+ {
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_tips_get_cancel (tgh);
+ return;
+ }
+ else
+ {
+ hr.http_status = 0;
+ hr.ec = TALER_EC_INVALID_RESPONSE;
+ }
+ }
+ GNUNET_JSON_parse_free (spec);
+ break;
+ }
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ 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);
+ break;
+ }
+ tgh->cb (tgh->cb_cls,
+ &hr,
+ 0,
+ NULL);
+ TALER_MERCHANT_tips_get_cancel (tgh);
+}
+
+
+/**
+ * Make a GET /private/tips request.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param cb function to call with the backend's tip information
+ * @param cb_cls closure for @a cb
+ * @return the request handle; NULL upon error
+ */
+struct TALER_MERCHANT_TipsGetHandle *
+TALER_MERCHANT_tips_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_TipsGetCallback cb,
+ void *cb_cls)
+{
+ return TALER_MERCHANT_tips_get2 (ctx,
+ backend_url,
+ false,
+ -20,
+ UINT64_MAX,
+ cb,
+ cb_cls);
+}
+
+
+/**
+ * Issue a GET /private/tips request with filters to the backend.
+ *
+ * @param ctx execution context
+ * @param backend_url base URL of the merchant backend
+ * @param include_expired whether to return all tips or only unexpired tips
+ * @param limit number of results to return, negative for descending row id, positive for ascending
+ * @param offset row id to start returning results from
+ * @param cb function to call with the result
+ * @param cb_cls closure for @a cb
+ * @return handle for this operation, NULL upon errors
+ */
+struct TALER_MERCHANT_TipsGetHandle *
+TALER_MERCHANT_tips_get2 (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ enum TALER_MERCHANT_YesNoAll expired,
+ int64_t limit,
+ uint64_t offset,
+ TALER_MERCHANT_TipsGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_TipsGetHandle *tgh;
+ CURL *eh;
+
+ GNUNET_assert (NULL != backend_url);
+ if (0 == limit)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ tgh = GNUNET_new (struct TALER_MERCHANT_TipsGetHandle);
+ tgh->ctx = ctx;
+ tgh->cb = cb;
+ tgh->cb_cls = cb_cls;
+
+ /* build tgh->url with the various optional arguments */
+ {
+ struct GNUNET_Buffer buf = { 0 };
+ bool first = true;
+ /**
+ * Macro to append @a a and @a b to @a buf, using
+ * the right separators between key (@a a) and
+ * value (@a b). Uses "first" to decide between
+ * using "?" and "&" as the separator.
+ *
+ * @param a a key
+ * @param b a value
+ */
+#define APPEND(a,b) \
+ do { \
+ if (first) \
+ GNUNET_buffer_write_str (&buf, \
+ "?"); \
+ else \
+ GNUNET_buffer_write_str (&buf, \
+ "&"); \
+ first = false; \
+ GNUNET_buffer_write_str (&buf, (a)); \
+ GNUNET_buffer_write_str (&buf, "="); \
+ GNUNET_buffer_write_str (&buf, (b)); \
+ } while (0)
+
+ {
+ char *url;
+
+ url = TALER_url_join (backend_url,
+ "private/tips",
+ NULL);
+ if (NULL == url)
+ goto finished;
+ GNUNET_buffer_write_str (&buf,
+ url);
+ GNUNET_free (url);
+ }
+ if (TALER_MERCHANT_YNA_NO != expired)
+ APPEND ("expired",
+ (TALER_MERCHANT_YNA_YES == expired) ? "yes" : "all");
+ if (limit > 0)
+ {
+ if (0 != offset)
+ {
+ char cbuf[30];
+
+ GNUNET_snprintf (cbuf,
+ sizeof (cbuf),
+ "%llu",
+ (unsigned long long) offset);
+ APPEND ("offset",
+ cbuf);
+ }
+ }
+ else
+ {
+ if (UINT64_MAX != offset)
+ {
+ char cbuf[30];
+
+ GNUNET_snprintf (cbuf,
+ sizeof (cbuf),
+ "%llu",
+ (unsigned long long) offset);
+ APPEND ("offset",
+ cbuf);
+ }
+ }
+ if (-20 != limit)
+ {
+ char cbuf[30];
+
+ GNUNET_snprintf (cbuf,
+ sizeof (cbuf),
+ "%lld",
+ (long long) limit);
+ APPEND ("limit",
+ cbuf);
+ }
+ tgh->url = GNUNET_buffer_reap_str (&buf);
+#undef APPEND
+ }
+
+finished:
+ if (NULL == tgh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (tgh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ tgh->url);
+ eh = curl_easy_init ();
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ tgh->url));
+ tgh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ GNUNET_YES,
+ &handle_get_tips_finished,
+ tgh);
+ return tgh;
+}
+
+
+/**
+ * Cancel GET /private/tips request. Must not be called by clients after
+ * the callback was invoked.
+ *
+ * @param ogh request to cancel.
+ */
+void
+TALER_MERCHANT_tips_get_cancel (
+ struct TALER_MERCHANT_TipsGetHandle *tgh)
+{
+ if (NULL != tgh->job)
+ GNUNET_CURL_job_cancel (tgh->job);
+ GNUNET_free (tgh->url);
+ GNUNET_free (tgh);
+}