merchant

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

taler-merchant-httpd_patch-private-orders-ORDER_ID-forget.c (8587B)


      1 /*
      2   This file is part of TALER
      3   (C) 2020 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_patch-private-orders-ORDER_ID-forget.c
     21  * @brief implementing PATCH /orders/$ORDER_ID/forget request handling
     22  * @author Jonathan Buchanan
     23  */
     24 #include "platform.h"
     25 #include "taler-merchant-httpd_patch-private-orders-ORDER_ID-forget.h"
     26 #include <taler/taler_json_lib.h>
     27 #include "merchant-database/lookup_contract_terms.h"
     28 #include "merchant-database/update_contract_terms.h"
     29 #include "merchant-database/start.h"
     30 
     31 
     32 /**
     33  * How often do we retry the UPDATE database transaction?
     34  */
     35 #define MAX_RETRIES 3
     36 
     37 
     38 /**
     39  * Forget part of the contract terms.
     40  *
     41  * @param cls pointer to the result of the forget operation.
     42  * @param object_id name of the object to forget.
     43  * @param parent parent of the object at @e object_id.
     44  */
     45 static void
     46 forget (void *cls,
     47         const char *object_id,
     48         json_t *parent)
     49 {
     50   int *res = cls;
     51   int ret;
     52 
     53   ret = TALER_JSON_contract_part_forget (parent,
     54                                          object_id);
     55   if (GNUNET_SYSERR == ret)
     56   {
     57     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     58                 "Matching path `%s' not forgettable!\n",
     59                 object_id);
     60     *res = GNUNET_SYSERR;
     61   }
     62   if (GNUNET_NO == ret)
     63   {
     64     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     65                 "Matching path `%s' already forgotten!\n",
     66                 object_id);
     67   }
     68   else
     69   {
     70     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     71                 "Forgot `%s'\n",
     72                 object_id);
     73     if (GNUNET_NO == *res)
     74       *res = GNUNET_OK;
     75   }
     76 }
     77 
     78 
     79 /**
     80  * Forget fields of an order's contract terms.
     81  *
     82  * @param rh context of the handler
     83  * @param connection the MHD connection to handle
     84  * @param[in,out] hc context with further information about the request
     85  * @return MHD result code
     86  */
     87 enum MHD_Result
     88 TMH_private_patch_orders_ID_forget (const struct TMH_RequestHandler *rh,
     89                                     struct MHD_Connection *connection,
     90                                     struct TMH_HandlerContext *hc)
     91 {
     92   const char *order_id = hc->infix;
     93   enum GNUNET_DB_QueryStatus qs;
     94   uint64_t order_serial;
     95 
     96   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     97   {
     98     const json_t *fields;
     99     json_t *contract_terms;
    100     bool changed = false;
    101 
    102     if (GNUNET_OK !=
    103         TALER_MERCHANTDB_start (TMH_db,
    104                                 "forget order"))
    105     {
    106       return TALER_MHD_reply_with_error (connection,
    107                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    108                                          TALER_EC_GENERIC_DB_START_FAILED,
    109                                          NULL);
    110     }
    111     qs = TALER_MERCHANTDB_lookup_contract_terms (TMH_db,
    112                                                  hc->instance->settings.id,
    113                                                  order_id,
    114                                                  &contract_terms,
    115                                                  &order_serial,
    116                                                  NULL);
    117     switch (qs)
    118     {
    119     case GNUNET_DB_STATUS_HARD_ERROR:
    120       TALER_MERCHANTDB_rollback (TMH_db);
    121       return TALER_MHD_reply_with_error (connection,
    122                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    123                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    124                                          "contract terms");
    125     case GNUNET_DB_STATUS_SOFT_ERROR:
    126       TALER_MERCHANTDB_rollback (TMH_db);
    127       continue;
    128     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    129       TALER_MERCHANTDB_rollback (TMH_db);
    130       return TALER_MHD_reply_with_error (connection,
    131                                          MHD_HTTP_NOT_FOUND,
    132                                          TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
    133                                          order_id);
    134     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    135       GNUNET_assert (NULL != contract_terms);
    136       break;
    137     }
    138 
    139     {
    140       struct GNUNET_JSON_Specification spec[] = {
    141         GNUNET_JSON_spec_array_const ("fields",
    142                                       &fields),
    143         GNUNET_JSON_spec_end ()
    144       };
    145       enum GNUNET_GenericReturnValue res;
    146 
    147       res = TALER_MHD_parse_json_data (connection,
    148                                        hc->request_body,
    149                                        spec);
    150       if (GNUNET_OK != res)
    151       {
    152         TALER_MERCHANTDB_rollback (TMH_db);
    153         json_decref (contract_terms);
    154         return (GNUNET_NO == res)
    155                ? MHD_YES
    156                : MHD_NO;
    157       }
    158     }
    159     {
    160       size_t index;
    161       json_t *value;
    162 
    163       json_array_foreach (fields, index, value) {
    164         int forget_status = GNUNET_NO;
    165         int expand_status;
    166 
    167         if (! (json_is_string (value)))
    168         {
    169           TALER_MERCHANTDB_rollback (TMH_db);
    170           json_decref (contract_terms);
    171           return TALER_MHD_reply_with_error (connection,
    172                                              MHD_HTTP_BAD_REQUEST,
    173                                              TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT,
    174                                              "field is not a string");
    175         }
    176         expand_status = TALER_JSON_expand_path (contract_terms,
    177                                                 json_string_value (value),
    178                                                 &forget,
    179                                                 &forget_status);
    180         if (GNUNET_SYSERR == forget_status)
    181         {
    182           /* We tried to forget a field that isn't forgettable */
    183           TALER_MERCHANTDB_rollback (TMH_db);
    184           json_decref (contract_terms);
    185           return TALER_MHD_reply_with_error (connection,
    186                                              MHD_HTTP_CONFLICT,
    187                                              TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_NOT_FORGETTABLE,
    188                                              json_string_value (value));
    189         }
    190         if (GNUNET_OK == forget_status)
    191           changed = true;
    192         if (GNUNET_SYSERR == expand_status)
    193         {
    194           /* One of the paths was malformed and couldn't be expanded */
    195           TALER_MERCHANTDB_rollback (TMH_db);
    196           json_decref (contract_terms);
    197           return TALER_MHD_reply_with_error (connection,
    198                                              MHD_HTTP_BAD_REQUEST,
    199                                              TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT,
    200                                              json_string_value (value));
    201         }
    202       }
    203     }
    204 
    205     if (! changed)
    206     {
    207       TALER_MERCHANTDB_rollback (TMH_db);
    208       json_decref (contract_terms);
    209       return TALER_MHD_reply_static (connection,
    210                                      MHD_HTTP_NO_CONTENT,
    211                                      NULL,
    212                                      NULL,
    213                                      0);
    214     }
    215     qs = TALER_MERCHANTDB_update_contract_terms (TMH_db,
    216                                                  hc->instance->settings.id,
    217                                                  order_id,
    218                                                  contract_terms);
    219     json_decref (contract_terms);
    220     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    221     {
    222       TALER_MERCHANTDB_rollback (TMH_db);
    223       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    224         break;
    225     }
    226     else
    227     {
    228       qs = TALER_MERCHANTDB_commit (TMH_db);
    229       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    230         break;
    231     }
    232   }
    233   if (0 > qs)
    234   {
    235     return TALER_MHD_reply_with_error (connection,
    236                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    237                                        TALER_EC_GENERIC_DB_COMMIT_FAILED,
    238                                        NULL);
    239   }
    240 
    241   return TALER_MHD_reply_static (connection,
    242                                  MHD_HTTP_OK,
    243                                  NULL,
    244                                  NULL,
    245                                  0);
    246 }