merchant

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

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


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