merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_post-private-categories.c (6528B)


      1 /*
      2   This file is part of TALER
      3   (C) 2024 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  * @file src/backend/taler-merchant-httpd_post-private-categories.c
     21  * @brief implementing POST /private/categories request handling
     22  * @author Christian Grothoff
     23  */
     24 #include "platform.h"
     25 #include "taler-merchant-httpd_post-private-categories.h"
     26 #include "taler-merchant-httpd_helper.h"
     27 #include <taler/taler_json_lib.h>
     28 #include "merchant-database/insert_category.h"
     29 #include "merchant-database/select_category_by_name.h"
     30 #include "merchant-database/start.h"
     31 
     32 /**
     33  * How often do we retry the simple INSERT database transaction?
     34  */
     35 #define MAX_RETRIES 3
     36 
     37 
     38 enum MHD_Result
     39 TMH_private_post_categories (const struct TMH_RequestHandler *rh,
     40                              struct MHD_Connection *connection,
     41                              struct TMH_HandlerContext *hc)
     42 {
     43   struct TMH_MerchantInstance *mi = hc->instance;
     44   const char *category_name;
     45   const json_t *category_name_i18n = NULL;
     46   json_t *oempty = NULL;
     47   uint64_t category_id;
     48   struct GNUNET_JSON_Specification spec[] = {
     49     GNUNET_JSON_spec_string ("name",
     50                              &category_name),
     51     GNUNET_JSON_spec_mark_optional (
     52       GNUNET_JSON_spec_object_const ("name_i18n",
     53                                      &category_name_i18n),
     54       NULL),
     55     GNUNET_JSON_spec_end ()
     56   };
     57   enum GNUNET_DB_QueryStatus qs;
     58 
     59   GNUNET_assert (NULL != mi);
     60   {
     61     enum GNUNET_GenericReturnValue res;
     62 
     63     res = TALER_MHD_parse_json_data (connection,
     64                                      hc->request_body,
     65                                      spec);
     66     if (GNUNET_OK != res)
     67     {
     68       GNUNET_break_op (0);
     69       return (GNUNET_NO == res)
     70              ? MHD_YES
     71              : MHD_NO;
     72     }
     73   }
     74   if (NULL == category_name_i18n)
     75   {
     76     /* NULL is not allowed in the DB, substitute with empty object */
     77     oempty = json_object ();
     78     category_name_i18n = oempty;
     79   }
     80   if (NULL == category_name_i18n)
     81   {
     82     /* NULL is not allowed in the DB, substitute with empty object */
     83     oempty = json_object ();
     84     category_name_i18n = oempty;
     85   }
     86   /* finally, interact with DB until no serialization error */
     87   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     88   {
     89     json_t *xcategory_name_i18n;
     90 
     91     if (GNUNET_OK !=
     92         TALER_MERCHANTDB_start (TMH_db,
     93                                 "POST /categories"))
     94     {
     95       GNUNET_break (0);
     96       GNUNET_JSON_parse_free (spec);
     97       json_decref (oempty);
     98       return TALER_MHD_reply_with_error (connection,
     99                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    100                                          TALER_EC_GENERIC_DB_START_FAILED,
    101                                          NULL);
    102     }
    103     qs = TALER_MERCHANTDB_select_category_by_name (TMH_db,
    104                                                    mi->settings.id,
    105                                                    category_name,
    106                                                    &xcategory_name_i18n,
    107                                                    &category_id);
    108     switch (qs)
    109     {
    110     case GNUNET_DB_STATUS_HARD_ERROR:
    111       /* Clean up and fail hard */
    112       GNUNET_break (0);
    113       TALER_MERCHANTDB_rollback (TMH_db);
    114       GNUNET_JSON_parse_free (spec);
    115       json_decref (oempty);
    116       return TALER_MHD_reply_with_error (connection,
    117                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    118                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    119                                          NULL);
    120     case GNUNET_DB_STATUS_SOFT_ERROR:
    121       /* restart transaction */
    122       goto retry;
    123     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    124       /* Good, we can proceed! */
    125       break;
    126     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    127       /* idempotency check: is etp == tp? */
    128       {
    129         bool eq;
    130 
    131         eq = (1 == json_equal (xcategory_name_i18n,
    132                                category_name_i18n));
    133         json_decref (xcategory_name_i18n);
    134         TALER_MERCHANTDB_rollback (TMH_db);
    135         GNUNET_JSON_parse_free (spec);
    136         json_decref (oempty);
    137         return eq
    138           ? TALER_MHD_REPLY_JSON_PACK (connection,
    139                                        MHD_HTTP_OK,
    140                                        GNUNET_JSON_pack_uint64 ("category_id",
    141                                                                 category_id))
    142           : TALER_MHD_reply_with_error (connection,
    143                                         MHD_HTTP_CONFLICT,
    144                                         TALER_EC_MERCHANT_PRIVATE_POST_CATEGORIES_CONFLICT_CATEGORY_EXISTS,
    145                                         category_name);
    146       }
    147     } /* end switch (qs) */
    148 
    149     qs = TALER_MERCHANTDB_insert_category (TMH_db,
    150                                            mi->settings.id,
    151                                            category_name,
    152                                            category_name_i18n,
    153                                            &category_id);
    154     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    155     {
    156       TALER_MERCHANTDB_rollback (TMH_db);
    157       break;
    158     }
    159     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    160     {
    161       qs = TALER_MERCHANTDB_commit (TMH_db);
    162       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    163         break;
    164     }
    165 retry:
    166     GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    167     TALER_MERCHANTDB_rollback (TMH_db);
    168   } /* for RETRIES loop */
    169   json_decref (oempty);
    170   GNUNET_JSON_parse_free (spec);
    171   if (qs < 0)
    172   {
    173     GNUNET_break (0);
    174     return TALER_MHD_reply_with_error (
    175       connection,
    176       MHD_HTTP_INTERNAL_SERVER_ERROR,
    177       (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    178       ? TALER_EC_GENERIC_DB_SOFT_FAILURE
    179       : TALER_EC_GENERIC_DB_COMMIT_FAILED,
    180       NULL);
    181   }
    182   return TALER_MHD_REPLY_JSON_PACK (
    183     connection,
    184     MHD_HTTP_OK,
    185     GNUNET_JSON_pack_uint64 ("category_id",
    186                              category_id));
    187 }
    188 
    189 
    190 /* end of taler-merchant-httpd_post-private-categories.c */