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