LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-webhooks.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.3 % 77 58
Test Date: 2025-11-06 19:31:41 Functions: 100.0 % 2 2

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2022 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify
       6              :   it under the terms of the GNU Affero General Public License as
       7              :   published by the Free Software Foundation; either version 3,
       8              :   or (at your option) any later version.
       9              : 
      10              :   TALER is distributed in the hope that it will be useful, but
      11              :   WITHOUT ANY WARRANTY; without even the implied warranty of
      12              :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13              :   GNU General Public License for more details.
      14              : 
      15              :   You should have received a copy of the GNU General Public
      16              :   License along with TALER; see the file COPYING.  If not,
      17              :   see <http://www.gnu.org/licenses/>
      18              : */
      19              : 
      20              : /**
      21              :  * @file taler-merchant-httpd_private-post-webhooks.c
      22              :  * @brief implementing POST /webhooks request handling
      23              :  * @author Priscilla HUANG
      24              :  */
      25              : #include "platform.h"
      26              : #include "taler-merchant-httpd_private-post-webhooks.h"
      27              : #include "taler-merchant-httpd_helper.h"
      28              : #include <taler/taler_json_lib.h>
      29              : 
      30              : 
      31              : /**
      32              :  * How often do we retry the simple INSERT database transaction?
      33              :  */
      34              : #define MAX_RETRIES 3
      35              : 
      36              : 
      37              : /**
      38              :  * Check if the two webhooks are identical.
      39              :  *
      40              :  * @param w1 webhook to compare
      41              :  * @param w2 other webhook to compare
      42              :  * @return true if they are 'equal', false if not or of payto_uris is not an array
      43              :  */
      44              : static bool
      45            4 : webhooks_equal (const struct TALER_MERCHANTDB_WebhookDetails *w1,
      46              :                 const struct TALER_MERCHANTDB_WebhookDetails *w2)
      47              : {
      48            4 :   return ( (0 == strcmp (w1->event_type,
      49            6 :                          w2->event_type)) &&
      50            2 :            (0 == strcmp (w1->url,
      51            2 :                          w2->url)) &&
      52            2 :            (0 == strcmp (w1->http_method,
      53            2 :                          w2->http_method)) &&
      54            2 :            ( ( (NULL == w1->header_template) &&
      55            0 :                (NULL == w2->header_template) ) ||
      56            2 :              ( (NULL != w1->header_template) &&
      57            2 :                (NULL != w2->header_template) &&
      58            2 :                (0 == strcmp (w1->header_template,
      59            8 :                              w2->header_template)) ) ) &&
      60            2 :            ( ( (NULL == w1->body_template) &&
      61            0 :                (NULL == w2->body_template) ) ||
      62            2 :              ( (NULL != w1->body_template) &&
      63            2 :                (NULL != w2->body_template) &&
      64            2 :                (0 == strcmp (w1->body_template,
      65            2 :                              w2->body_template)) )  ) );
      66              : }
      67              : 
      68              : 
      69              : MHD_RESULT
      70           10 : TMH_private_post_webhooks (const struct TMH_RequestHandler *rh,
      71              :                            struct MHD_Connection *connection,
      72              :                            struct TMH_HandlerContext *hc)
      73              : {
      74           10 :   struct TMH_MerchantInstance *mi = hc->instance;
      75           10 :   struct TALER_MERCHANTDB_WebhookDetails wb = { 0 };
      76              :   const char *webhook_id;
      77              :   enum GNUNET_DB_QueryStatus qs;
      78              :   struct GNUNET_JSON_Specification spec[] = {
      79           10 :     GNUNET_JSON_spec_string ("webhook_id",
      80              :                              &webhook_id),
      81           10 :     GNUNET_JSON_spec_string ("event_type",
      82              :                              (const char **) &wb.event_type),
      83           10 :     TALER_JSON_spec_web_url ("url",
      84              :                              (const char **) &wb.url),
      85           10 :     GNUNET_JSON_spec_string ("http_method",
      86              :                              (const char **) &wb.http_method),
      87           10 :     GNUNET_JSON_spec_mark_optional (
      88              :       GNUNET_JSON_spec_string ("header_template",
      89              :                                (const char **) &wb.header_template),
      90              :       NULL),
      91           10 :     GNUNET_JSON_spec_mark_optional (
      92              :       GNUNET_JSON_spec_string ("body_template",
      93              :                                (const char **) &wb.body_template),
      94              :       NULL),
      95           10 :     GNUNET_JSON_spec_end ()
      96              :   };
      97              : 
      98           10 :   GNUNET_assert (NULL != mi);
      99              :   {
     100              :     enum GNUNET_GenericReturnValue res;
     101              : 
     102           10 :     res = TALER_MHD_parse_json_data (connection,
     103           10 :                                      hc->request_body,
     104              :                                      spec);
     105           10 :     if (GNUNET_OK != res)
     106              :     {
     107            0 :       GNUNET_break_op (0);
     108              :       return (GNUNET_NO == res)
     109              :              ? MHD_YES
     110            0 :              : MHD_NO;
     111              :     }
     112              :   }
     113              : 
     114              : 
     115              :   /* finally, interact with DB until no serialization error */
     116           10 :   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     117              :   {
     118              :     /* Test if a webhook of this id is known */
     119              :     struct TALER_MERCHANTDB_WebhookDetails ewb;
     120              : 
     121           10 :     if (GNUNET_OK !=
     122           10 :         TMH_db->start (TMH_db->cls,
     123              :                        "/post webhooks"))
     124              :     {
     125            0 :       GNUNET_break (0);
     126            0 :       GNUNET_JSON_parse_free (spec);
     127            4 :       return TALER_MHD_reply_with_error (connection,
     128              :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     129              :                                          TALER_EC_GENERIC_DB_START_FAILED,
     130              :                                          NULL);
     131              :     }
     132           10 :     qs = TMH_db->lookup_webhook (TMH_db->cls,
     133           10 :                                  mi->settings.id,
     134              :                                  webhook_id,
     135              :                                  &ewb);
     136           10 :     switch (qs)
     137              :     {
     138            0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     139              :       /* Clean up and fail hard */
     140            0 :       GNUNET_break (0);
     141            0 :       TMH_db->rollback (TMH_db->cls);
     142            0 :       GNUNET_JSON_parse_free (spec);
     143            0 :       return TALER_MHD_reply_with_error (connection,
     144              :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     145              :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
     146              :                                          NULL);
     147            0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     148              :       /* restart transaction */
     149            0 :       goto retry;
     150            6 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     151              :       /* Good, we can proceed! */
     152            6 :       break;
     153            4 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     154              :       /* idempotency check: is ewb == wb? */
     155              :       {
     156              :         bool eq;
     157              : 
     158            4 :         eq = webhooks_equal (&wb,
     159              :                              &ewb);
     160            4 :         TALER_MERCHANTDB_webhook_details_free (&ewb);
     161            4 :         TMH_db->rollback (TMH_db->cls);
     162            4 :         GNUNET_JSON_parse_free (spec);
     163              :         return eq
     164            2 :           ? TALER_MHD_reply_static (connection,
     165              :                                     MHD_HTTP_NO_CONTENT,
     166              :                                     NULL,
     167              :                                     NULL,
     168              :                                     0)
     169            6 :           : TALER_MHD_reply_with_error (connection,
     170              :                                         MHD_HTTP_CONFLICT,
     171              :                                         TALER_EC_MERCHANT_PRIVATE_POST_WEBHOOKS_CONFLICT_WEBHOOK_EXISTS,
     172              :                                         webhook_id);
     173              :       }
     174              :     } /* end switch (qs) */
     175              : 
     176            6 :     qs = TMH_db->insert_webhook (TMH_db->cls,
     177            6 :                                  mi->settings.id,
     178              :                                  webhook_id,
     179              :                                  &wb);
     180            6 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     181              :     {
     182            0 :       TMH_db->rollback (TMH_db->cls);
     183            6 :       break;
     184              :     }
     185            6 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     186              :     {
     187            6 :       qs = TMH_db->commit (TMH_db->cls);
     188            6 :       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
     189            6 :         break;
     190              :     }
     191            0 : retry:
     192            0 :     GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     193            0 :     TMH_db->rollback (TMH_db->cls);
     194              :   } /* for RETRIES loop */
     195            6 :   GNUNET_JSON_parse_free (spec);
     196            6 :   if (qs < 0)
     197              :   {
     198            0 :     GNUNET_break (0);
     199            0 :     return TALER_MHD_reply_with_error (
     200              :       connection,
     201              :       MHD_HTTP_INTERNAL_SERVER_ERROR,
     202              :       (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     203              :       ? TALER_EC_GENERIC_DB_SOFT_FAILURE
     204              :       : TALER_EC_GENERIC_DB_COMMIT_FAILED,
     205              :       NULL);
     206              :   }
     207            6 :   return TALER_MHD_reply_static (connection,
     208              :                                  MHD_HTTP_NO_CONTENT,
     209              :                                  NULL,
     210              :                                  NULL,
     211              :                                  0);
     212              : }
     213              : 
     214              : 
     215              : /* end of taler-merchant-httpd_private-post-webhooks.c */
        

Generated by: LCOV version 2.0-1