merchant

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

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


      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   /* finally, interact with DB until no serialization error */
     81   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     82   {
     83     json_t *xcategory_name_i18n;
     84 
     85     if (GNUNET_OK !=
     86         TALER_MERCHANTDB_start (TMH_db,
     87                                 "POST /categories"))
     88     {
     89       GNUNET_break (0);
     90       GNUNET_JSON_parse_free (spec);
     91       json_decref (oempty);
     92       return TALER_MHD_reply_with_error (connection,
     93                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     94                                          TALER_EC_GENERIC_DB_START_FAILED,
     95                                          NULL);
     96     }
     97     qs = TALER_MERCHANTDB_select_category_by_name (TMH_db,
     98                                                    mi->settings.id,
     99                                                    category_name,
    100                                                    &xcategory_name_i18n,
    101                                                    &category_id);
    102     switch (qs)
    103     {
    104     case GNUNET_DB_STATUS_HARD_ERROR:
    105       /* Clean up and fail hard */
    106       GNUNET_break (0);
    107       TALER_MERCHANTDB_rollback (TMH_db);
    108       GNUNET_JSON_parse_free (spec);
    109       json_decref (oempty);
    110       return TALER_MHD_reply_with_error (connection,
    111                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    112                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    113                                          NULL);
    114     case GNUNET_DB_STATUS_SOFT_ERROR:
    115       /* restart transaction */
    116       goto retry;
    117     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    118       /* Good, we can proceed! */
    119       break;
    120     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    121       /* idempotency check: is etp == tp? */
    122       {
    123         bool eq;
    124 
    125         eq = (1 == json_equal (xcategory_name_i18n,
    126                                category_name_i18n));
    127         json_decref (xcategory_name_i18n);
    128         TALER_MERCHANTDB_rollback (TMH_db);
    129         GNUNET_JSON_parse_free (spec);
    130         json_decref (oempty);
    131         return eq
    132           ? TALER_MHD_REPLY_JSON_PACK (connection,
    133                                        MHD_HTTP_OK,
    134                                        GNUNET_JSON_pack_uint64 ("category_id",
    135                                                                 category_id))
    136           : TALER_MHD_reply_with_error (connection,
    137                                         MHD_HTTP_CONFLICT,
    138                                         TALER_EC_MERCHANT_PRIVATE_POST_CATEGORIES_CONFLICT_CATEGORY_EXISTS,
    139                                         category_name);
    140       }
    141     } /* end switch (qs) */
    142 
    143     qs = TALER_MERCHANTDB_insert_category (TMH_db,
    144                                            mi->settings.id,
    145                                            category_name,
    146                                            category_name_i18n,
    147                                            &category_id);
    148     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    149     {
    150       TALER_MERCHANTDB_rollback (TMH_db);
    151       break;
    152     }
    153     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    154     {
    155       qs = TALER_MERCHANTDB_commit (TMH_db);
    156       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    157         break;
    158     }
    159 retry:
    160     GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    161     TALER_MERCHANTDB_rollback (TMH_db);
    162   } /* for RETRIES loop */
    163   json_decref (oempty);
    164   GNUNET_JSON_parse_free (spec);
    165   if (qs < 0)
    166   {
    167     GNUNET_break (0);
    168     return TALER_MHD_reply_with_error (
    169       connection,
    170       MHD_HTTP_INTERNAL_SERVER_ERROR,
    171       (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    172       ? TALER_EC_GENERIC_DB_SOFT_FAILURE
    173       : TALER_EC_GENERIC_DB_COMMIT_FAILED,
    174       NULL);
    175   }
    176   return TALER_MHD_REPLY_JSON_PACK (
    177     connection,
    178     MHD_HTTP_OK,
    179     GNUNET_JSON_pack_uint64 ("category_id",
    180                              category_id));
    181 }
    182 
    183 
    184 /* end of taler-merchant-httpd_post-private-categories.c */