merchant

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

taler-merchant-httpd_private-patch-orders-ID-forget.c (8282B)


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