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

            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 2.0-1