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 }