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