Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2020-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 : /**
21 : * @file taler-merchant-httpd_private-post-products.c
22 : * @brief implementing POST /products request handling
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include "taler-merchant-httpd_private-post-products.h"
27 : #include "taler-merchant-httpd_helper.h"
28 : #include <taler/taler_json_lib.h>
29 :
30 :
31 : MHD_RESULT
32 16 : TMH_private_post_products (const struct TMH_RequestHandler *rh,
33 : struct MHD_Connection *connection,
34 : struct TMH_HandlerContext *hc)
35 : {
36 16 : struct TMH_MerchantInstance *mi = hc->instance;
37 16 : struct TALER_MERCHANTDB_ProductDetails pd = { 0 };
38 16 : const json_t *categories = NULL;
39 : const char *product_id;
40 : int64_t total_stock;
41 : struct GNUNET_JSON_Specification spec[] = {
42 16 : GNUNET_JSON_spec_string ("product_id",
43 : &product_id),
44 16 : GNUNET_JSON_spec_string ("description",
45 : (const char **) &pd.description),
46 16 : GNUNET_JSON_spec_mark_optional (
47 : GNUNET_JSON_spec_json ("description_i18n",
48 : &pd.description_i18n),
49 : NULL),
50 16 : GNUNET_JSON_spec_string ("unit",
51 : (const char **) &pd.unit),
52 16 : TALER_JSON_spec_amount_any ("price",
53 : &pd.price),
54 16 : GNUNET_JSON_spec_mark_optional (
55 : GNUNET_JSON_spec_string ("image",
56 : (const char **) &pd.image),
57 : NULL),
58 16 : GNUNET_JSON_spec_mark_optional (
59 : GNUNET_JSON_spec_json ("taxes",
60 : &pd.taxes),
61 : NULL),
62 16 : GNUNET_JSON_spec_mark_optional (
63 : GNUNET_JSON_spec_array_const ("categories",
64 : &categories),
65 : NULL),
66 16 : GNUNET_JSON_spec_int64 ("total_stock",
67 : &total_stock),
68 16 : GNUNET_JSON_spec_mark_optional (
69 : GNUNET_JSON_spec_json ("address",
70 : &pd.address),
71 : NULL),
72 16 : GNUNET_JSON_spec_mark_optional (
73 : GNUNET_JSON_spec_timestamp ("next_restock",
74 : &pd.next_restock),
75 : NULL),
76 16 : GNUNET_JSON_spec_mark_optional (
77 : GNUNET_JSON_spec_uint32 ("minimum_age",
78 : &pd.minimum_age),
79 : NULL),
80 16 : GNUNET_JSON_spec_end ()
81 : };
82 16 : size_t num_cats = 0;
83 16 : uint64_t *cats = NULL;
84 : bool conflict;
85 : bool no_instance;
86 : ssize_t no_cat;
87 : enum GNUNET_DB_QueryStatus qs;
88 : MHD_RESULT ret;
89 :
90 16 : GNUNET_assert (NULL != mi);
91 : {
92 : enum GNUNET_GenericReturnValue res;
93 :
94 16 : res = TALER_MHD_parse_json_data (connection,
95 16 : hc->request_body,
96 : spec);
97 16 : if (GNUNET_OK != res)
98 : {
99 0 : GNUNET_break_op (0);
100 : return (GNUNET_NO == res)
101 : ? MHD_YES
102 0 : : MHD_NO;
103 : }
104 : }
105 16 : if (total_stock < -1)
106 : {
107 0 : GNUNET_break_op (0);
108 0 : ret = TALER_MHD_reply_with_error (connection,
109 : MHD_HTTP_BAD_REQUEST,
110 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
111 : "total_stock");
112 0 : goto cleanup;
113 : }
114 16 : num_cats = json_array_size (categories);
115 16 : cats = GNUNET_new_array (num_cats,
116 : uint64_t);
117 : {
118 : size_t idx;
119 : json_t *val;
120 :
121 16 : json_array_foreach (categories, idx, val)
122 : {
123 0 : if (! json_is_integer (val))
124 : {
125 0 : GNUNET_break_op (0);
126 0 : ret = TALER_MHD_reply_with_error (connection,
127 : MHD_HTTP_BAD_REQUEST,
128 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
129 : "categories");
130 0 : goto cleanup;
131 : }
132 0 : cats[idx] = json_integer_value (val);
133 : }
134 : }
135 :
136 16 : if (-1 == total_stock)
137 1 : pd.total_stock = INT64_MAX;
138 : else
139 15 : pd.total_stock = (uint64_t) total_stock;
140 :
141 16 : if (NULL == pd.address)
142 4 : pd.address = json_object ();
143 16 : if (NULL == pd.description_i18n)
144 4 : pd.description_i18n = json_object ();
145 16 : if (NULL == pd.taxes)
146 2 : pd.taxes = json_array ();
147 :
148 : /* check taxes is well-formed */
149 16 : if (! TMH_taxes_array_valid (pd.taxes))
150 : {
151 0 : GNUNET_break_op (0);
152 0 : ret = TALER_MHD_reply_with_error (connection,
153 : MHD_HTTP_BAD_REQUEST,
154 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
155 : "taxes");
156 0 : goto cleanup;
157 : }
158 :
159 16 : if (! TMH_location_object_valid (pd.address))
160 : {
161 0 : GNUNET_break_op (0);
162 0 : ret = TALER_MHD_reply_with_error (connection,
163 : MHD_HTTP_BAD_REQUEST,
164 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
165 : "address");
166 0 : goto cleanup;
167 : }
168 :
169 16 : if (! TALER_JSON_check_i18n (pd.description_i18n))
170 : {
171 0 : GNUNET_break_op (0);
172 0 : ret = TALER_MHD_reply_with_error (connection,
173 : MHD_HTTP_BAD_REQUEST,
174 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
175 : "description_i18n");
176 0 : goto cleanup;
177 : }
178 :
179 16 : if (NULL == pd.image)
180 0 : pd.image = (char *) "";
181 16 : if (! TMH_image_data_url_valid (pd.image))
182 : {
183 0 : GNUNET_break_op (0);
184 0 : ret = TALER_MHD_reply_with_error (connection,
185 : MHD_HTTP_BAD_REQUEST,
186 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
187 : "image");
188 0 : goto cleanup;
189 : }
190 :
191 16 : qs = TMH_db->insert_product (TMH_db->cls,
192 16 : mi->settings.id,
193 : product_id,
194 : &pd,
195 : num_cats,
196 : cats,
197 : &no_instance,
198 : &conflict,
199 : &no_cat);
200 16 : switch (qs)
201 : {
202 0 : case GNUNET_DB_STATUS_HARD_ERROR:
203 : case GNUNET_DB_STATUS_SOFT_ERROR:
204 0 : ret = TALER_MHD_reply_with_error (
205 : connection,
206 : MHD_HTTP_INTERNAL_SERVER_ERROR,
207 : (GNUNET_DB_STATUS_SOFT_ERROR == qs)
208 : ? TALER_EC_GENERIC_DB_SOFT_FAILURE
209 : : TALER_EC_GENERIC_DB_COMMIT_FAILED,
210 : NULL);
211 0 : goto cleanup;
212 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
213 0 : ret = TALER_MHD_reply_with_error (
214 : connection,
215 : MHD_HTTP_INTERNAL_SERVER_ERROR,
216 : TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
217 : NULL);
218 0 : goto cleanup;
219 16 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
220 16 : break;
221 : }
222 16 : if (no_instance)
223 : {
224 0 : ret = TALER_MHD_reply_with_error (
225 : connection,
226 : MHD_HTTP_NOT_FOUND,
227 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
228 0 : mi->settings.id);
229 0 : goto cleanup;
230 : }
231 16 : if (conflict)
232 : {
233 2 : ret = TALER_MHD_reply_with_error (
234 : connection,
235 : MHD_HTTP_CONFLICT,
236 : TALER_EC_MERCHANT_PRIVATE_POST_PRODUCTS_CONFLICT_PRODUCT_EXISTS,
237 : product_id);
238 2 : goto cleanup;
239 : }
240 14 : if (-1 != no_cat)
241 : {
242 : char nocats[24];
243 :
244 0 : GNUNET_break_op (0);
245 0 : TMH_db->rollback (TMH_db->cls);
246 0 : GNUNET_snprintf (nocats,
247 : sizeof (nocats),
248 : "%llu",
249 : (unsigned long long) no_cat);
250 0 : ret = TALER_MHD_reply_with_error (
251 : connection,
252 : MHD_HTTP_NOT_FOUND,
253 : TALER_EC_MERCHANT_GENERIC_CATEGORY_UNKNOWN,
254 : nocats);
255 0 : goto cleanup;
256 : }
257 14 : ret = TALER_MHD_reply_static (connection,
258 : MHD_HTTP_NO_CONTENT,
259 : NULL,
260 : NULL,
261 : 0);
262 16 : cleanup:
263 16 : GNUNET_JSON_parse_free (spec);
264 16 : GNUNET_free (cats);
265 16 : return ret;
266 : }
267 :
268 :
269 : /* end of taler-merchant-httpd_private-post-products.c */
|