LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-products.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 59 96 61.5 %
Date: 2025-07-15 14:11:34 Functions: 1 1 100.0 %

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

Generated by: LCOV version 1.16