merchant_api_patch_unit.c (8013B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Lesser General Public License as published by the Free 7 Software Foundation; either version 2.1, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public License along with 14 TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file merchant_api_patch_unit.c 18 * @brief Implementation of PATCH /private/units/$ID 19 * @author Bohdan Potuzhnyi 20 */ 21 #include "platform.h" 22 #include <curl/curl.h> 23 #include <jansson.h> 24 #include <microhttpd.h> 25 #include <gnunet/gnunet_util_lib.h> 26 #include "taler_merchant_service.h" 27 #include "merchant_api_curl_defaults.h" 28 #include "merchant_api_common.h" 29 #include <taler/taler_json_lib.h> 30 #include <taler/taler_curl_lib.h> 31 32 33 /** 34 * Handle for a PATCH /private/units/$ID operation. 35 */ 36 struct TALER_MERCHANT_UnitPatchHandle 37 { 38 /** 39 * Fully qualified request URL. 40 */ 41 char *url; 42 43 /** 44 * In-flight CURL job. 45 */ 46 struct GNUNET_CURL_Job *job; 47 48 /** 49 * Completion callback. 50 */ 51 TALER_MERCHANT_UnitPatchCallback cb; 52 53 /** 54 * Closure for @a cb. 55 */ 56 void *cb_cls; 57 58 /** 59 * Execution context. 60 */ 61 struct GNUNET_CURL_Context *ctx; 62 63 /** 64 * Keeps POST body and headers alive. 65 */ 66 struct TALER_CURL_PostContext post_ctx; 67 }; 68 69 70 /** 71 * Called when the HTTP transfer finishes. 72 * 73 * @param cls operation handle 74 * @param response_code HTTP status (0 on failure) 75 * @param response parsed JSON reply (NULL if unavailable) 76 */ 77 static void 78 handle_patch_unit_finished (void *cls, 79 long response_code, 80 const void *response) 81 { 82 struct TALER_MERCHANT_UnitPatchHandle *uph = cls; 83 const json_t *json = response; 84 struct TALER_MERCHANT_HttpResponse hr = { 85 .http_status = (unsigned int) response_code, 86 .reply = json 87 }; 88 89 uph->job = NULL; 90 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 91 "PATCH /private/units completed with status %u\n", 92 (unsigned int) response_code); 93 switch (response_code) 94 { 95 case MHD_HTTP_NO_CONTENT: 96 break; 97 case MHD_HTTP_BAD_REQUEST: 98 case MHD_HTTP_UNAUTHORIZED: 99 case MHD_HTTP_FORBIDDEN: 100 case MHD_HTTP_NOT_FOUND: 101 case MHD_HTTP_CONFLICT: 102 case MHD_HTTP_INTERNAL_SERVER_ERROR: 103 hr.ec = TALER_JSON_get_error_code (json); 104 hr.hint = TALER_JSON_get_error_hint (json); 105 break; 106 case 0: 107 hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 108 break; 109 default: 110 TALER_MERCHANT_parse_error_details_ (json, 111 response_code, 112 &hr); 113 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 114 "Unexpected response %u/%d for PATCH /private/units\n", 115 (unsigned int) response_code, 116 (int) hr.ec); 117 GNUNET_break_op (0); 118 break; 119 } 120 uph->cb (uph->cb_cls, 121 &hr); 122 TALER_MERCHANT_unit_patch_cancel (uph); 123 } 124 125 126 struct TALER_MERCHANT_UnitPatchHandle * 127 TALER_MERCHANT_unit_patch (struct GNUNET_CURL_Context *ctx, 128 const char *backend_url, 129 const char *unit_id, 130 const char *unit_name_long, 131 const char *unit_name_short, 132 const json_t *unit_name_long_i18n, 133 const json_t *unit_name_short_i18n, 134 const bool *unit_allow_fraction, 135 const uint32_t *unit_precision_level, 136 const bool *unit_active, 137 TALER_MERCHANT_UnitPatchCallback cb, 138 void *cb_cls) 139 { 140 struct TALER_MERCHANT_UnitPatchHandle *uph; 141 json_t *req_obj; 142 char *path; 143 144 req_obj = json_object (); 145 if (NULL == req_obj) 146 return NULL; 147 if (NULL != unit_name_long) 148 { 149 if (0 != json_object_set_new (req_obj, 150 "unit_name_long", 151 json_string (unit_name_long))) 152 { 153 json_decref (req_obj); 154 return NULL; 155 } 156 } 157 if (NULL != unit_name_short) 158 { 159 if (0 != json_object_set_new (req_obj, 160 "unit_name_short", 161 json_string (unit_name_short))) 162 { 163 json_decref (req_obj); 164 return NULL; 165 } 166 } 167 if (NULL != unit_name_long_i18n) 168 { 169 if (0 != json_object_set_new (req_obj, 170 "unit_name_long_i18n", 171 json_incref ((json_t *) unit_name_long_i18n))) 172 { 173 json_decref (req_obj); 174 return NULL; 175 } 176 } 177 if (NULL != unit_name_short_i18n) 178 { 179 if (0 != json_object_set_new (req_obj, 180 "unit_name_short_i18n", 181 json_incref ( 182 (json_t *) unit_name_short_i18n))) 183 { 184 json_decref (req_obj); 185 return NULL; 186 } 187 } 188 if (NULL != unit_allow_fraction) 189 { 190 if (0 != json_object_set_new (req_obj, 191 "unit_allow_fraction", 192 json_boolean (*unit_allow_fraction))) 193 { 194 json_decref (req_obj); 195 return NULL; 196 } 197 } 198 if (NULL != unit_precision_level) 199 { 200 if (0 != json_object_set_new (req_obj, 201 "unit_precision_level", 202 json_integer ( 203 (json_int_t) *unit_precision_level))) 204 { 205 json_decref (req_obj); 206 return NULL; 207 } 208 } 209 if (NULL != unit_active) 210 { 211 if (0 != json_object_set_new (req_obj, 212 "unit_active", 213 json_boolean (*unit_active))) 214 { 215 json_decref (req_obj); 216 return NULL; 217 } 218 } 219 if (0 == json_object_size (req_obj)) 220 { 221 json_decref (req_obj); 222 GNUNET_break (0); 223 return NULL; 224 } 225 226 GNUNET_asprintf (&path, 227 "private/units/%s", 228 unit_id); 229 uph = GNUNET_new (struct TALER_MERCHANT_UnitPatchHandle); 230 uph->ctx = ctx; 231 uph->cb = cb; 232 uph->cb_cls = cb_cls; 233 uph->url = TALER_url_join (backend_url, 234 path, 235 NULL); 236 GNUNET_free (path); 237 if (NULL == uph->url) 238 { 239 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 240 "Failed to build /private/units/%s URL\n", 241 unit_id); 242 json_decref (req_obj); 243 GNUNET_free (uph); 244 return NULL; 245 } 246 { 247 CURL *eh; 248 249 eh = TALER_MERCHANT_curl_easy_get_ (uph->url); 250 if (GNUNET_OK != 251 TALER_curl_easy_post (&uph->post_ctx, 252 eh, 253 req_obj)) 254 { 255 GNUNET_break (0); 256 curl_easy_cleanup (eh); 257 json_decref (req_obj); 258 GNUNET_free (uph->url); 259 GNUNET_free (uph); 260 return NULL; 261 } 262 json_decref (req_obj); 263 GNUNET_assert (CURLE_OK == 264 curl_easy_setopt (eh, 265 CURLOPT_CUSTOMREQUEST, 266 MHD_HTTP_METHOD_PATCH)); 267 uph->job = GNUNET_CURL_job_add2 (ctx, 268 eh, 269 uph->post_ctx.headers, 270 &handle_patch_unit_finished, 271 uph); 272 } 273 return uph; 274 } 275 276 277 void 278 TALER_MERCHANT_unit_patch_cancel (struct TALER_MERCHANT_UnitPatchHandle *uph) 279 { 280 if (NULL != uph->job) 281 { 282 GNUNET_CURL_job_cancel (uph->job); 283 uph->job = NULL; 284 } 285 TALER_curl_easy_post_finished (&uph->post_ctx); 286 GNUNET_free (uph->url); 287 GNUNET_free (uph); 288 } 289 290 291 /* end of merchant_api_patch_unit.c */