gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] branch master updated: backenddb webhook


From: gnunet
Subject: [taler-merchant] branch master updated: backenddb webhook
Date: Fri, 25 Nov 2022 14:42:41 +0100

This is an automated email from the git hooks/post-receive script.

priscilla-huang pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new 7bd9e0d6 backenddb webhook
7bd9e0d6 is described below

commit 7bd9e0d6c7b1dec25b986a3db77027334e6a6085
Author: priscilla <priscilla.huang@efrei.net>
AuthorDate: Fri Nov 25 08:42:25 2022 -0500

    backenddb webhook
---
 src/backenddb/merchant-0004.sql            |  28 +-
 src/backenddb/merchantdb_helper.c          |  12 +
 src/backenddb/plugin_merchantdb_postgres.c | 344 +++++++++++++++++++++--
 src/backenddb/test_merchantdb.c            | 425 ++++++++++++++++++++++++++++-
 src/include/taler_merchantdb_lib.h         |  10 +
 src/include/taler_merchantdb_plugin.h      | 143 +++++++++-
 6 files changed, 919 insertions(+), 43 deletions(-)

diff --git a/src/backenddb/merchant-0004.sql b/src/backenddb/merchant-0004.sql
index 8e341583..10897f1a 100644
--- a/src/backenddb/merchant-0004.sql
+++ b/src/backenddb/merchant-0004.sql
@@ -44,7 +44,7 @@ COMMENT ON COLUMN merchant_template.template_contract
 COMMIT;
 
 
-CREATE TABLE IF NOT EXISTS merchant_webhooks
+CREATE TABLE IF NOT EXISTS merchant_webhook
   (webhook_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
   ,merchant_serial BIGINT NOT NULL
     REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE
@@ -52,22 +52,22 @@ CREATE TABLE IF NOT EXISTS merchant_webhooks
   ,event_type VARCHAR NOT NULL
   ,url VARCHAR NOT NULL
   ,http_method VARCHAR NOT NULL
-  ,header_template VARCHAR NOT NULL
-  ,body_template VARCHAR NOT NULL
-  ,UNIQUE (merchant_serial, webhooks_id)
+  ,header_template VARCHAR
+  ,body_template VARCHAR
+  ,UNIQUE (merchant_serial, webhook_id)
   );
-COMMENT ON TABLE merchant_webhooks
-  IS 'webhooks used by the merchant (may be incomplete, frontend can 
override)';
-COMMENT ON COLUMN merchant_webhooks.event_type
-  IS 'Event of the webhooks';
-COMMENT ON COLUMN merchant_webhooks.url
+COMMENT ON TABLE merchant_webhook
+  IS 'webhook used by the merchant (may be incomplete, frontend can override)';
+COMMENT ON COLUMN merchant_webhook.event_type
+  IS 'Event of the webhook';
+COMMENT ON COLUMN merchant_webhook.url
   IS 'URL use by the customer';
-COMMENT ON COLUMN merchant_webhooks.http_method
+COMMENT ON COLUMN merchant_webhook.http_method
   IS 'http method use by the merchant';
-COMMENT ON COLUMN merchant_webhooks.header_template
-  IS 'Header of the webhooks';
-COMMENT ON COLUMN merchant_webhooks.body_template
-  IS 'Body of the webhooks';
+COMMENT ON COLUMN merchant_webhook.header_template
+  IS 'Header of the webhook';
+COMMENT ON COLUMN merchant_webhook.body_template
+  IS 'Body of the webhook';
 
 COMMIT;
 
diff --git a/src/backenddb/merchantdb_helper.c 
b/src/backenddb/merchantdb_helper.c
index 51991ca7..e7d9f459 100644
--- a/src/backenddb/merchantdb_helper.c
+++ b/src/backenddb/merchantdb_helper.c
@@ -48,5 +48,17 @@ TALER_MERCHANTDB_template_details_free (
 }
 
 
+void
+TALER_MERCHANTDB_webhook_details_free (
+  struct TALER_MERCHANTDB_WebhookDetails *wb)
+{
+  GNUNET_free (wb->event_type);
+  GNUNET_free (wb->url);
+  GNUNET_free (wb->http_method);
+  GNUNET_free (wb->header_template);
+  GNUNET_free (wb->body_template);
+}
+
+
 
 /* end of merchantdb_helper.c */
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index 6a2bcb65..1176cdd5 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -6850,22 +6850,22 @@ postgres_delete_template (void *cls,
  * @param cls closure
  * @param instance_id instance to insert template for
  * @param template_id template identifier of template to insert
- * @param pd the template details to insert
+ * @param td the template details to insert
  * @return database result code
  */
 static enum GNUNET_DB_QueryStatus
 postgres_insert_template (void *cls,
                           const char *instance_id,
                           const char *template_id,
-                          const struct TALER_MERCHANTDB_TemplateDetails *pd)
+                          const struct TALER_MERCHANTDB_TemplateDetails *td)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (instance_id),
     GNUNET_PQ_query_param_string (template_id),
-    GNUNET_PQ_query_param_string (pd->template_description),
-    GNUNET_PQ_query_param_string (pd->image),
-    TALER_PQ_query_param_json (pd->template_contract),
+    GNUNET_PQ_query_param_string (td->template_description),
+    GNUNET_PQ_query_param_string (td->image),
+    TALER_PQ_query_param_json (td->template_contract),
     GNUNET_PQ_query_param_end
 
   };
@@ -6883,7 +6883,7 @@ postgres_insert_template (void *cls,
  * @param cls closure
  * @param instance_id instance to update template for
  * @param template_id template to update
- * @param pd update to the template details on success, can be NULL
+ * @param td update to the template details on success, can be NULL
  *             (in that case we only want to check if the template exists)
  * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
  *         does not yet exist.
@@ -6892,15 +6892,15 @@ static enum GNUNET_DB_QueryStatus
 postgres_update_template (void *cls,
                           const char *instance_id,
                           const char *template_id,
-                          const struct TALER_MERCHANTDB_TemplateDetails *pd)
+                          const struct TALER_MERCHANTDB_TemplateDetails *td)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (instance_id),
     GNUNET_PQ_query_param_string (template_id),
-    GNUNET_PQ_query_param_string (pd->template_description),
-    GNUNET_PQ_query_param_string (pd->image),
-    TALER_PQ_query_param_json (pd->template_contract),
+    GNUNET_PQ_query_param_string (td->template_description),
+    GNUNET_PQ_query_param_string (td->image),
+    TALER_PQ_query_param_json (td->template_contract),
     GNUNET_PQ_query_param_end
   };
 
@@ -7025,7 +7025,7 @@ postgres_lookup_templates (void *cls,
  * @param cls closure
  * @param instance_id instance to lookup template for
  * @param template_id template to lookup
- * @param[out] pd set to the template details on success, can be NULL
+ * @param[out] td set to the template details on success, can be NULL
  *             (in that case we only want to check if the template exists)
  * @return database result code
  */
@@ -7033,7 +7033,7 @@ static enum GNUNET_DB_QueryStatus
 postgres_lookup_template (void *cls,
                           const char *instance_id,
                           const char *template_id,
-                          struct TALER_MERCHANTDB_TemplateDetails *pd)
+                          struct TALER_MERCHANTDB_TemplateDetails *td)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
@@ -7042,7 +7042,7 @@ postgres_lookup_template (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  if (NULL == pd)
+  if (NULL == td)
   {
     struct GNUNET_PQ_ResultSpec rs_null[] = {
       GNUNET_PQ_result_spec_end
@@ -7058,11 +7058,11 @@ postgres_lookup_template (void *cls,
   {
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_string ("template_description",
-                                    &pd->template_description),
+                                    &td->template_description),
       GNUNET_PQ_result_spec_string ("image",
-                                    &pd->image),
+                                    &td->image),
       TALER_PQ_result_spec_json ("template_contract",
-                                 &pd->template_contract),
+                                 &td->template_contract),
       GNUNET_PQ_result_spec_end
     };
 
@@ -7082,25 +7082,263 @@ postgres_lookup_template (void *cls,
  * @param webhook_id webhook to delete
  * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
  *           if webhook unknown.
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_delete_webhook (void *cls,
+                          const char *instance_id,
+                          const char *webhook_id)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (webhook_id),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "delete_webhook",
+                                             params);
+}
+
+
+/**
+ * Insert details about a particular webhook.
  *
+ * @param cls closure
+ * @param instance_id instance to insert template for
+ * @param webhook_id webhook identifier of webhook to insert
+ * @param wb the webhook details to insert
+ * @return database result code
+ */
 static enum GNUNET_DB_QueryStatus
-postgres_delete_template (void *cls,
+postgres_insert_webhook (void *cls,
                           const char *instance_id,
-                          const char *template_id)
+                          const char *webhook_id,
+                          const struct TALER_MERCHANTDB_WebhookDetails *wb)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_string (template_id),
+    GNUNET_PQ_query_param_string (webhook_id),
+    GNUNET_PQ_query_param_string (wb->event_type),
+    GNUNET_PQ_query_param_string (wb->url),
+    GNUNET_PQ_query_param_string (wb->http_method),
+    GNUNET_PQ_query_param_string (wb->header_template),
+    GNUNET_PQ_query_param_string (wb->body_template),
     GNUNET_PQ_query_param_end
+
   };
 
   check_connection (pg);
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "delete_template",
+                                             "insert_webhook",
+                                             params);
+}
+
+
+/**
+ * Update details about a particular webhook.
+ *
+ * @param cls closure
+ * @param instance_id instance to update template for
+ * @param webhook_id template to update
+ * @param wb update to the webhook details on success, can be NULL
+ *             (in that case we only want to check if the webhook exists)
+ * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
webhook
+ *         does not yet exist.
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_update_webhook (void *cls,
+                          const char *instance_id,
+                          const char *webhook_id,
+                          const struct TALER_MERCHANTDB_WebhookDetails *wb)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (webhook_id),
+    GNUNET_PQ_query_param_string (wb->event_type),
+    GNUNET_PQ_query_param_string (wb->url),
+    GNUNET_PQ_query_param_string (wb->http_method),
+    GNUNET_PQ_query_param_string (wb->header_template),
+    GNUNET_PQ_query_param_string (wb->body_template),
+    GNUNET_PQ_query_param_end
+  };
+
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "update_webhook",
                                              params);
 }
-  */
+
+
+/**
+ * Context used for postgres_lookup_webhook().
+ */
+struct LookupWebhookContext
+{
+  /**
+   * Function to call with the results.
+   */
+  TALER_MERCHANTDB_WebhooksCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Did database result extraction fail?
+   */
+  bool extract_failed;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results about webhook.
+ *
+ * @param[in,out] cls of type `struct LookupWebhookContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lookup_webhooks_cb (void *cls,
+                     PGresult *result,
+                     unsigned int num_results)
+{
+  struct LookupWebhookContext *wlc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
+  {
+    char *webhook_id;
+    char *event_type;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_string ("webhook_id",
+                                    &webhook_id),
+      GNUNET_PQ_result_spec_string ("event_type",
+                                    &event_type),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      wlc->extract_failed = true;
+      return;
+    }
+    wlc->cb (wlc->cb_cls,
+             webhook_id,
+             event_type);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+/**
+ * Lookup all of the webhooks the given instance has configured.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup webhook for
+ * @param cb function to call on all webhook found
+ * @param cb_cls closure for @a cb
+ * @return database result code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_webhooks (void *cls,
+                           const char *instance_id,
+                           TALER_MERCHANTDB_WebhooksCallback cb,
+                           void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct LookupWebhookContext wlc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    /* Can be overwritten by the lookup_webhook_cb */
+    .extract_failed = false,
+  };
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "lookup_webhooks",
+                                             params,
+                                             &lookup_webhooks_cb,
+                                             &wlc);
+  /* If there was an error inside lookup_webhook_cb, return a hard error. */
+  if (wlc.extract_failed)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
+
+
+/**
+ * Lookup details about a particular webhook.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup webhook for
+ * @param webhook_id webhook to lookup
+ * @param[out] wb set to the webhook details on success, can be NULL
+ *             (in that case we only want to check if the webhook exists)
+ * @return database result code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_webhook (void *cls,
+                          const char *instance_id,
+                          const char *webhook_id,
+                          struct TALER_MERCHANTDB_WebhookDetails *wb)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (webhook_id),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (NULL == wb)
+  {
+    struct GNUNET_PQ_ResultSpec rs_null[] = {
+      GNUNET_PQ_result_spec_end
+    };
+
+    check_connection (pg);
+    return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                     "lookup_webhook",
+                                                     params,
+                                                     rs_null);
+  }
+  else
+  {
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_string ("event_type",
+                                    &wb->event_type),
+      GNUNET_PQ_result_spec_string ("url",
+                                    &wb->url),
+      GNUNET_PQ_result_spec_string ("http_method",
+                                    &wb->http_method),
+      GNUNET_PQ_result_spec_string ("header_template",
+                                    &wb->header_template),
+      GNUNET_PQ_result_spec_string ("body_template",
+                                    &wb->body_template),
+      GNUNET_PQ_result_spec_end
+    };
+
+    check_connection (pg);
+    return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                     "lookup_webhook",
+                                                     params,
+                                                     rs);
+  }
+}
+
 
 /**
  * Establish connection to the database.
@@ -9601,6 +9839,65 @@ postgres_connect (void *cls)
                             "      FROM merchant_instances"
                             "      WHERE merchant_id=$1)"
                             "   AND template_id=$2"),
+    /* for postgres_lookup_webhooks() */
+    GNUNET_PQ_make_prepare ("lookup_webhooks",
+                            "SELECT"
+                            " webhook_id"
+                            ",event_type"
+                            " FROM merchant_webhook"
+                            " JOIN merchant_instances"
+                            "   USING (merchant_serial)"
+                            " WHERE merchant_instances.merchant_id=$1"),
+    /* for postgres_lookup_webhook() */
+    GNUNET_PQ_make_prepare ("lookup_webhook",
+                            "SELECT"
+                            " event_type"
+                            ",url"
+                            ",http_method"
+                            ",header_template"
+                            ",body_template"
+                            " FROM merchant_webhook"
+                            " JOIN merchant_instances"
+                            "   USING (merchant_serial)"
+                            " WHERE merchant_instances.merchant_id=$1"
+                            "   AND merchant_webhook.webhook_id=$2"),
+    /* for postgres_delete_webhook() */
+    GNUNET_PQ_make_prepare ("delete_webhook",
+                            "DELETE"
+                            " FROM merchant_webhook"
+                            " WHERE merchant_template.merchant_serial="
+                            "     (SELECT merchant_serial "
+                            "        FROM merchant_instances"
+                            "        WHERE merchant_id=$1)"
+                            "   AND merchant_webhook.webhook_id=$2"),
+    /* for postgres_insert_webhook() */
+    GNUNET_PQ_make_prepare ("insert_webhook",
+                            "INSERT INTO merchant_webhook"
+                            "(merchant_serial"
+                            ",webhook_id"
+                            ",event_type"
+                            ",url"
+                            ",http_method"
+                            ",header_template"
+                            ",body_template"
+                            ")"
+                            " SELECT merchant_serial,"
+                            " $2, $3, $4, $5, $6, $7"
+                            " FROM merchant_instances"
+                            " WHERE merchant_id=$1"),
+    /* for postgres_update_webhook() */
+    GNUNET_PQ_make_prepare ("update_webhook",
+                            "UPDATE merchant_webhook SET"
+                            " event_type=$3"
+                            ",url=$4"
+                            ",http_method=$5"
+                            ",header_template=$6"
+                            ",body_template=$7"
+                            " WHERE merchant_serial="
+                            "   (SELECT merchant_serial"
+                            "      FROM merchant_instances"
+                            "      WHERE merchant_id=$1)"
+                            "   AND webhook_id=$2"),
     GNUNET_PQ_PREPARED_STATEMENT_END
   };
   struct GNUNET_PQ_ExecuteStatement es[] = {
@@ -9755,6 +10052,11 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
   plugin->delete_template = &postgres_delete_template;
   plugin->insert_template = &postgres_insert_template;
   plugin->update_template = &postgres_update_template;
+  plugin->lookup_webhooks = &postgres_lookup_webhooks;
+  plugin->lookup_webhook = &postgres_lookup_webhook;
+  plugin->delete_webhook = &postgres_delete_webhook;
+  plugin->insert_webhook = &postgres_insert_webhook;
+  plugin->update_webhook = &postgres_update_webhook;
   return plugin;
 }
 
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index 5a5b7d6b..1b186d9c 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -6889,7 +6889,7 @@ free_template_data (struct TemplateData *template)
 
 
 /**
- * Compare two template for equality.
+ * Compare two templates for equality.
  *
  * @param a the first template.
  * @param b the second template.
@@ -7262,6 +7262,428 @@ test_templates (void)
   return test_result;
 }
 
+
+/* *********** Webhooks ********** */
+
+/**
+ * A container for data relevant to a webhook.
+ */
+struct WebhookData
+{
+  /**
+   * The identifier of the webhook.
+   */
+  const char *id;
+
+  /**
+   * The details of the webhook.
+   */
+  struct TALER_MERCHANTDB_WebhookDetails webhook;
+};
+
+
+/**
+ * Creates a webhook for testing with.
+ *
+ * @param id the id of the webhook.
+ * @param webhook the webhook data to fill.
+ */
+static void
+make_webhook (const char *id,
+              struct WebhookData *webhook)
+{
+  webhook->id = id;
+  webhook->webhook.event_type= "Paid";
+  webhook->webhook.url= "https://example.com";;
+  webhook->webhook.http_method= "POST";
+  webhook->webhook.header_template= "Authorization:XYJAORKJEO";
+  webhook->webhook.body_template= "$Amount";
+}
+
+
+
+/**
+ * Compare two webhooks for equality.
+ *
+ * @param a the first webhook.
+ * @param b the second webhook.
+ * @return 0 on equality, 1 otherwise.
+ */
+static int
+check_webhooks_equal (const struct TALER_MERCHANTDB_WebhookDetails *a,
+                      const struct TALER_MERCHANTDB_WebhookDetails *b)
+{
+  if ((0 != strcmp (a->event_type,
+                    b->event_type)) ||
+      (0 != strcmp (a->url,
+                    b->url)) ||
+      (0 != strcmp (a->http_method,
+                    b->http_method)) ||
+      (0 != strcmp (a->header_template,
+                    b->header_template)) ||
+      (0 != strcmp (a->body_template,
+                    b->body_template)))
+    return 1;
+  return 0;
+}
+
+
+/**
+ * Tests inserting webhook data into the database.
+ *
+ * @param instance the instance to insert the webhook for.
+ * @param webhook the webhook data to insert.
+ * @param expected_result the result we expect the db to return.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_insert_webhook (const struct InstanceData *instance,
+                     const struct WebhookData *webhook,
+                     enum GNUNET_DB_QueryStatus expected_result)
+{
+  TEST_COND_RET_ON_FAIL (expected_result ==
+                         plugin->insert_webhook (plugin->cls,
+                                                 instance->instance.id,
+                                                 webhook->id,
+                                                 &webhook->webhook),
+                         "Insert webhook failed\n");
+  return 0;
+}
+
+
+/**
+ * Tests updating webhook data in the database.
+ *
+ * @param instance the instance to update the webhook for.
+ * @param webhook the webhook data to update.
+ * @param expected_result the result we expect the db to return.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_update_webhook (const struct InstanceData *instance,
+                     const struct WebhookData *webhook,
+                     enum GNUNET_DB_QueryStatus expected_result)
+{
+  TEST_COND_RET_ON_FAIL (expected_result ==
+                         plugin->update_webhook (plugin->cls,
+                                                 instance->instance.id,
+                                                 webhook->id,
+                                                 &webhook->webhook),
+                         "Update webhook failed\n");
+  return 0;
+}
+
+
+/**
+ * Tests looking up a webhook from the db.
+ *
+ * @param instance the instance to query from.
+ * @param webhook the webhook to query and compare to.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_lookup_webhook (const struct InstanceData *instance,
+                     const struct WebhookData *webhook)
+{
+  struct TALER_MERCHANTDB_WebhookDetails lookup_result;
+  if (0 > plugin->lookup_webhook (plugin->cls,
+                                  instance->instance.id,
+                                  webhook->id,
+                                  &lookup_result))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup webhook failed\n");
+    TALER_MERCHANTDB_webhook_details_free (&lookup_result);
+    return 1;
+  }
+  const struct TALER_MERCHANTDB_WebhookDetails *to_cmp = &webhook->webhook;
+  if (0 != check_webhooks_equal (&lookup_result,
+                                 to_cmp))
+  {
+    GNUNET_break (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup webhook failed: incorrect webhook returned\n");
+    TALER_MERCHANTDB_webhook_details_free (&lookup_result);
+    return 1;
+  }
+  TALER_MERCHANTDB_webhook_details_free (&lookup_result);
+  return 0;
+}
+
+
+/**
+ * Closure for testing webhook lookup
+ */
+struct TestLookupWebhooks_Closure
+{
+  /**
+   * Number of webhook ids to compare to
+   */
+  unsigned int webhooks_to_cmp_length;
+
+  /**
+   * Pointer to array of webhook ids
+   */
+  const struct WebhookData *webhooks_to_cmp;
+
+  /**
+   * Pointer to array of number of matches for each webhook
+   */
+  unsigned int *results_matching;
+
+  /**
+   * Total number of results returned
+   */
+  unsigned int results_length;
+};
+
+
+/**
+ * Function called after calling @e test_lookup_webhooks
+ *
+ * @param cls a pointer to the lookup closure.
+ * @param webhook_id the identifier of the webhook found.
+ */
+static void
+lookup_webhooks_cb (void *cls,
+                     const char *webhook_id,
+                     const char *event_type)
+{
+  struct TestLookupWebhooks_Closure *cmp = cls;
+  if (NULL == cmp)
+    return;
+  cmp->results_length += 1;
+  for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
+  {
+    if (0 == strcmp (cmp->webhooks_to_cmp[i].id,
+                     webhook_id))
+      cmp->results_matching[i] += 1;
+  }
+}
+
+
+/**
+ * Tests looking up all webhooks for an instance.
+ *
+ * @param instance the instance to query from.
+ * @param webhooks_length the number of webhooks we are expecting.
+ * @param webhooks the list of webhooks that we expect to be found.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_lookup_webhooks (const struct InstanceData *instance,
+                      unsigned int webhooks_length,
+                      const struct WebhookData *webhooks)
+{
+  unsigned int results_matching[webhooks_length];
+  struct TestLookupWebhooks_Closure cls = {
+    .webhooks_to_cmp_length = webhooks_length,
+    .webhooks_to_cmp = webhooks,
+    .results_matching = results_matching,
+    .results_length = 0
+  };
+  memset (results_matching, 0, sizeof (unsigned int) * webhooks_length);
+  if (0 > plugin->lookup_webhooks (plugin->cls,
+                                   instance->instance.id,
+                                   &lookup_webhooks_cb,
+                                   &cls))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup webhooks failed\n");
+    return 1;
+  }
+  if (webhooks_length != cls.results_length)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup webhooks failed: incorrect number of results\n");
+    return 1;
+  }
+  for (unsigned int i = 0; webhooks_length > i; ++i)
+  {
+    if (1 != cls.results_matching[i])
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Lookup webhooks failed: mismatched data\n");
+      return 1;
+    }
+  }
+  return 0;
+}
+
+
+/**
+ * Tests deleting a webhook.
+ *
+ * @param instance the instance to delete the webhook from.
+ * @param webhook the webhook that should be deleted.
+ * @param expected_result the result that we expect the plugin to return.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_delete_webhook (const struct InstanceData *instance,
+                     const struct WebhookData *webhook,
+                     enum GNUNET_DB_QueryStatus expected_result)
+{
+  TEST_COND_RET_ON_FAIL (expected_result ==
+                         plugin->delete_webhook (plugin->cls,
+                                                 instance->instance.id,
+                                                 webhook->id),
+                         "Delete webhook failed\n");
+  return 0;
+}
+
+
+/**
+ * Closure for webhook tests.
+ */
+struct TestWebhooks_Closure
+{
+  /**
+   * The instance to use for this test.
+   */
+  struct InstanceData instance;
+
+  /**
+   * The array of webhooks.
+   */
+  struct WebhookData webhooks[2];
+};
+
+
+/**
+ * Sets up the data structures used in the webhook tests.
+ *
+ * @param cls the closure to fill with test data.
+ */
+static void
+pre_test_webhooks (struct TestWebhooks_Closure *cls)
+{
+  /* Instance */
+  make_instance ("test_inst_webhooks",
+                 &cls->instance);
+
+  /* Webhooks */
+  make_webhook ("test_webhooks_wb_0",
+                &cls->webhooks[0]);
+
+  make_webhook ("test_webhooks_wb_1",
+                &cls->webhooks[1]);
+  cls->webhooks[1].webhook.event_type= "Test paid";
+}
+
+
+/**
+ * Handles all teardown after testing.
+ *
+ * @param cls the closure containing memory to be freed.
+ */
+static void
+post_test_webhooks (struct TestWebhooks_Closure *cls)
+{
+  free_instance_data (&cls->instance);
+}
+
+
+/**
+ * Runs the tests for webhooks.
+ *
+ * @param cls the container of the test data.
+ * @return 0 on success, 1 otherwise.
+ */
+static int
+run_test_webhooks (struct TestWebhooks_Closure *cls)
+{
+
+  /* Test that insert without an instance fails */
+  TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
+                                         &cls->webhooks[0],
+                                         GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
+  /* Insert the instance */
+  TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
+                                          
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+  /* Test inserting a webhook */
+  TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
+                                         &cls->webhooks[0],
+                                         GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+  /* Test that double insert fails */
+  TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
+                                         &cls->webhooks[0],
+                                         GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
+  /* Test lookup of individual webhooks */
+  TEST_RET_ON_FAIL (test_lookup_webhook (&cls->instance,
+                                         &cls->webhooks[0]));
+  /* Make sure it fails correctly for webhooks that don't exist */
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+      plugin->lookup_webhook (plugin->cls,
+                              cls->instance.instance.id,
+                              "nonexistent_webhook",
+                              NULL))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup webhook failed\n");
+    return 1;
+  }
+  /* Test webhook update */
+  cls->webhooks[0].webhook.event_type=
+    "Test paid";
+  cls->webhooks[0].webhook.url=
+    "https://example.com";;
+  cls->webhooks[0].webhook.http_method=
+    "POST";
+  cls->webhooks[0].webhook.header_template=
+    "Authorization:WEKFOEKEXZ";
+  cls->webhooks[0].webhook.body_template=
+    "$Amount";
+  TEST_RET_ON_FAIL (test_update_webhook (&cls->instance,
+                                         &cls->webhooks[0],
+                                         GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+
+  TEST_RET_ON_FAIL (test_lookup_webhook (&cls->instance,
+                                         &cls->webhooks[0]));
+  TEST_RET_ON_FAIL (test_update_webhook (&cls->instance,
+                                         &cls->webhooks[1],
+                                         GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
+  /* Test collective webhook lookup */
+  TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
+                                         &cls->webhooks[1],
+                                         GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+  TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
+                                          2,
+                                          cls->webhooks));
+
+  /* Test webhook deletion */
+  TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance,
+                                         &cls->webhooks[1],
+                                         GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+  /* Test double deletion fails */
+  TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance,
+                                         &cls->webhooks[1],
+                                         GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
+  TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
+                                          1,
+                                          cls->webhooks));
+  return 0;
+}
+
+
+/**
+ * Takes care of webhook testing.
+ *
+ * @return 0 on success, 1 otherwise.
+ */
+static int
+test_webhooks (void)
+{
+  struct TestWebhooks_Closure test_cls;
+  pre_test_webhooks (&test_cls);
+  int test_result = run_test_webhooks (&test_cls);
+  post_test_webhooks (&test_cls);
+  return test_result;
+}
+
+
+
+
 /**
  * Function that runs all tests.
  *
@@ -7280,6 +7702,7 @@ run_tests (void)
   TEST_RET_ON_FAIL (test_lookup_orders_all_filters ());
   TEST_RET_ON_FAIL (test_kyc ());
   TEST_RET_ON_FAIL (test_templates ());
+  TEST_RET_ON_FAIL (test_webhooks ());
   return 0;
 }
 
diff --git a/src/include/taler_merchantdb_lib.h 
b/src/include/taler_merchantdb_lib.h
index 8d84db79..09184661 100644
--- a/src/include/taler_merchantdb_lib.h
+++ b/src/include/taler_merchantdb_lib.h
@@ -68,6 +68,16 @@ void
 TALER_MERCHANTDB_template_details_free (
   struct TALER_MERCHANTDB_TemplateDetails *tp);
 
+
+/**
+ * Free members of @a wb, but not @a wb itself.
+ *
+ * @param[in] wb webhook details to clean up
+ */
+void
+TALER_MERCHANTDB_webhook_details_free (
+  struct TALER_MERCHANTDB_WebhookDetails *wb);
+
 #endif  /* MERCHANT_DB_H */
 
 /* end of taler_merchantdb_lib.h */
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index 1fa58ecd..0e70ec02 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -336,6 +336,53 @@ struct TALER_MERCHANTDB_TemplateDetails
 };
 
 
+/**
+ * Typically called by `lookup_webhooks`.
+ *
+ * @param cls a `json_t *` JSON array to build
+ * @param webhook_id ID of the webhook
+ */
+typedef void
+(*TALER_MERCHANTDB_WebhooksCallback)(void *cls,
+                                      const char *webhook_id,
+                                     const char *event_type);
+
+
+/**
+ * Details about a webhook.
+ */
+struct TALER_MERCHANTDB_WebhookDetails
+{
+  /**
+   * event of the webhook.
+   */
+  char *event_type;
+
+  /**
+   * URL of the webhook. The customer will be redirected on this url.
+   */
+  char *url;
+
+  /**
+   * Http method used by the webhook.
+   */
+  char *http_method;
+
+
+  /**
+   * Header template of the webhook.
+   */
+  char *header_template;
+
+
+  /**
+   * Body template of the webhook.
+   */
+  char *body_template;
+
+};
+
+
 /**
  * Filter preferences.
  */
@@ -2442,7 +2489,7 @@ struct TALER_MERCHANTDB_Plugin
  * @param cls closure
  * @param instance_id instance to lookup template for
  * @param template_id template to lookup
- * @param[out] pd set to the template details on success, can be NULL
+ * @param[out] td set to the template details on success, can be NULL
  *             (in that case we only want to check if the template exists)
  * @return database result code
  */
@@ -2450,13 +2497,13 @@ struct TALER_MERCHANTDB_Plugin
   (*lookup_template)(void *cls,
                      const char *instance_id,
                      const char *template_id,
-                     struct TALER_MERCHANTDB_TemplateDetails *pd);
+                     struct TALER_MERCHANTDB_TemplateDetails *td);
 
 /**
  * Delete information about a template.
  *
  * @param cls closure
- * @param instance_id instance to delete product of
+ * @param instance_id instance to delete template of
  * @param template_id template to delete
  * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
  *           if template unknown.
@@ -2473,14 +2520,14 @@ struct TALER_MERCHANTDB_Plugin
  * @param cls closure
  * @param instance_id instance to insert template for
  * @param template_id template identifier of template to insert
- * @param pd the template details to insert
+ * @param td the template details to insert
  * @return database result code
  */
   enum GNUNET_DB_QueryStatus
   (*insert_template)(void *cls,
                      const char *instance_id,
                      const char *template_id,
-                     const struct TALER_MERCHANTDB_TemplateDetails *pd);
+                     const struct TALER_MERCHANTDB_TemplateDetails *td);
 
 
 /**
@@ -2489,7 +2536,7 @@ struct TALER_MERCHANTDB_Plugin
  * @param cls closure
  * @param instance_id instance to update template for
  * @param template_id template to update
- * @param pd update to the template details on success, can be NULL
+ * @param td update to the template details on success, can be NULL
  *             (in that case we only want to check if the template exists)
  * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
  *         does not yet exist.
@@ -2499,7 +2546,89 @@ struct TALER_MERCHANTDB_Plugin
   (*update_template)(void *cls,
                      const char *instance_id,
                      const char *template_id,
-                     const struct TALER_MERCHANTDB_TemplateDetails *pd);
+                     const struct TALER_MERCHANTDB_TemplateDetails *td);
+
+
+/**
+ * Lookup all of the webhooks the given instance has configured.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup webhook for
+ * @param cb function to call on all webhook found
+ * @param cb_cls closure for @a cb
+ * @return database result code
+ */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_webhooks)(void *cls,
+                      const char *instance_id,
+                      TALER_MERCHANTDB_WebhooksCallback cb,
+                      void *cb_cls);
+
+
+/**
+ * Lookup details about a particular webhook.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup webhook for
+ * @param webhook_id webhook to lookup
+ * @param[out] wb set to the webhook details on success, can be NULL
+ *             (in that case we only want to check if the webhook exists)
+ * @return database result code
+ */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_webhook)(void *cls,
+                     const char *instance_id,
+                     const char *webhook_id,
+                     struct TALER_MERCHANTDB_WebhookDetails *wb);
+
+/**
+ * Delete information about a webhook.
+ *
+ * @param cls closure
+ * @param instance_id instance to delete webhook of
+ * @param webhook_id webhook to delete
+ * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
+ *           if webhook unknown.
+ */
+  enum GNUNET_DB_QueryStatus
+  (*delete_webhook)(void *cls,
+                     const char *instance_id,
+                     const char *webhook_id);
+
+
+/**
+ * Insert details about a particular webhook.
+ *
+ * @param cls closure
+ * @param instance_id instance to insert template for
+ * @param webhook_id webhook identifier of webhook to insert
+ * @param wb the webhook details to insert
+ * @return database result code
+ */
+  enum GNUNET_DB_QueryStatus
+  (*insert_webhook)(void *cls,
+                     const char *instance_id,
+                     const char *webhook_id,
+                     const struct TALER_MERCHANTDB_WebhookDetails *wb);
+
+
+/**
+ * Update details about a particular webhook.
+ *
+ * @param cls closure
+ * @param instance_id instance to update webhook for
+ * @param webhook_id webhook to update
+ * @param wb update to the webhook details on success, can be NULL
+ *             (in that case we only want to check if the webhook exists)
+ * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
webhook
+ *         does not yet exist.
+ */
+
+  enum GNUNET_DB_QueryStatus
+  (*update_webhook)(void *cls,
+                     const char *instance_id,
+                     const char *webhook_id,
+                     const struct TALER_MERCHANTDB_WebhookDetails *wb);
 
 };
 

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]