diff options
-rw-r--r-- | src/backend/Makefile.am | 4 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 11 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-patch-products-ID.c | 2 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-patch-products-ID.h | 14 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-products-ID-lock.c | 126 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-products-ID-lock.h | 43 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 30 |
7 files changed, 218 insertions, 12 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 646841db..58f11ed9 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -40,7 +40,9 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-post-instances.c \ taler-merchant-httpd_private-post-instances.h \ taler-merchant-httpd_private-post-products.c \ - taler-merchant-httpd_private-post-products.h + taler-merchant-httpd_private-post-products.h \ + taler-merchant-httpd_private-post-products-ID-lock.c \ + taler-merchant-httpd_private-post-products-ID-lock.h DEAD = \ taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 58b68451..4efa6ad2 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -37,6 +37,7 @@ #include "taler-merchant-httpd_private-patch-instances-ID.h" #include "taler-merchant-httpd_private-patch-products-ID.h" #include "taler-merchant-httpd_private-post-instances.h" +#include "taler-merchant-httpd_private-post-products-ID-lock.h" /** * Backlog for listen operation on unix-domain sockets. @@ -785,11 +786,19 @@ url_handler (void *cls, }, /* PATCH /products/$ID/: */ { - .url_prefix = "/", + .url_prefix = "/products", .method = MHD_HTTP_METHOD_PATCH, .have_id_segment = true, .handler = &TMH_private_patch_products_ID }, + /* POST /products/$ID/lock: */ + { + .url_prefix = "/products", + .url_suffix = "lock", + .method = MHD_HTTP_METHOD_POST, + .have_id_segment = true, + .handler = &TMH_private_post_products_ID_lock + }, { NULL } diff --git a/src/backend/taler-merchant-httpd_private-patch-products-ID.c b/src/backend/taler-merchant-httpd_private-patch-products-ID.c index 8177207c..fd5a6c1e 100644 --- a/src/backend/taler-merchant-httpd_private-patch-products-ID.c +++ b/src/backend/taler-merchant-httpd_private-patch-products-ID.c @@ -169,7 +169,7 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_INTERNAL_INVARIANT_FAILURE, - "Impossible to parse the order"); + "Impossible to parse the product description"); } if (-1 == total_stocked) pd.total_stocked = UINT64_MAX; diff --git a/src/backend/taler-merchant-httpd_private-patch-products-ID.h b/src/backend/taler-merchant-httpd_private-patch-products-ID.h index dde86a9b..b034fd0f 100644 --- a/src/backend/taler-merchant-httpd_private-patch-products-ID.h +++ b/src/backend/taler-merchant-httpd_private-patch-products-ID.h @@ -18,12 +18,12 @@ */ /** - * @file backend/taler-merchant-httpd_private-patch-instances-ID.h - * @brief implementing POST /instances request handling + * @file backend/taler-merchant-httpd_private-patch-products-ID.h + * @brief implementing POST /products request handling * @author Christian Grothoff */ -#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_INSTANCES_ID_H -#define TALER_MERCHANT_HTTPD_PRIVATE_PATCH_INSTANCES_ID_H +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_PRODUCTS_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_PATCH_PRODUCTS_ID_H #include "taler-merchant-httpd.h" @@ -36,8 +36,8 @@ * @return MHD result code */ MHD_RESULT -TMH_private_patch_instances_ID (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc); +TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); #endif diff --git a/src/backend/taler-merchant-httpd_private-post-products-ID-lock.c b/src/backend/taler-merchant-httpd_private-post-products-ID-lock.c new file mode 100644 index 00000000..bc35efdf --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-products-ID-lock.c @@ -0,0 +1,126 @@ +/* + This file is part of TALER + (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file backend/taler-merchant-httpd_private-post-products-ID-lock.c + * @brief implementing POST /products/$ID/lock request handling + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-post-products-ID-lock.h" +#include <taler/taler_json_lib.h> + + +/** + * Lock an existing product. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_post_products_ID_lock (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + const char *product_id = hc->infix; + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_Uuid uuid; + uint32_t quantity; + struct GNUNET_TIME_Relative duration; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("lock_uuid", + &uuid), + GNUNET_JSON_spec_uint32 ("quantity", + &quantity), + GNUNET_JSON_spec_relative_time ("duration", + &duration), + GNUNET_JSON_spec_end () + }; + + GNUNET_assert (NULL != mi); + GNUNET_assert (NULL != product_id); + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + /* json is malformed */ + if (GNUNET_NO == res) + { + GNUNET_break_op (0); + return MHD_YES; + } + /* other internal errors might have occurred */ + if (GNUNET_SYSERR == res) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_INTERNAL_INVARIANT_FAILURE, + "Impossible to parse the lock request"); + } + + qs = TMH_db->lock_product (TMH_db->cls, + mi->settings.id, + product_id, + &uuid, + quantity, + GNUNET_TIME_relative_to_absolute (duration)); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_PRODUCTS_PATCH_DB_COMMIT_HARD_ERROR, + "Failed to execute DB transaction to lock product"); + case GNUNET_DB_STATUS_SOFT_ERROR: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_INTERNAL_INVARIANT_FAILURE, + "Serialization error for single-statment request"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + qs = TMH_db->lookup_product (TMH_db->cls, + mi->settings.id, + product_id, + NULL); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_PRODUCTS_LOCK_UNKNOWN_PRODUCT, + "The specified product is unknown"); + else + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_PRODUCTS_LOCK_INSUFFICIENT_STOCKS, + "The specified product is out of stock"); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + } + GNUNET_assert (0); + return MHD_NO; +} + + +/* end of taler-merchant-httpd_private-patch-products-ID-lock.c */ diff --git a/src/backend/taler-merchant-httpd_private-post-products-ID-lock.h b/src/backend/taler-merchant-httpd_private-post-products-ID-lock.h new file mode 100644 index 00000000..0d5d9a3a --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-products-ID-lock.h @@ -0,0 +1,43 @@ +/* + This file is part of TALER + (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file backend/taler-merchant-httpd_private-post-products-ID-lock.h + * @brief implementing POST /products/$ID/lock request handling + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_PRODUCTS_ID_LOCK_H +#define TALER_MERCHANT_HTTPD_PRIVATE_POST_PRODUCTS_ID_LOCK_H +#include "taler-merchant-httpd.h" + + +/** + * Lock an existing product. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_post_products_ID_lock (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +#endif diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 28913f59..353997db 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -571,14 +571,19 @@ struct TALER_MERCHANTDB_Plugin const struct TALER_MERCHANTDB_ProductDetails *pd); /** - * Update details about a particular product. + * Update details about a particular product. Note that the + * transaction must enforce that the sold/stocked/lost counters + * are not reduced (i.e. by expanding the WHERE clause on the existing + * values). * * @param cls closure * @param instance_id instance to lookup products for * @param product_id product to lookup * @param[out] pd set to the product details on success, can be NULL * (in that case we only want to check if the product exists) - * @return database result code + * @return database result code, #GNUNET_DB_SUCCESS_NO_RESULTS if the + * non-decreasing constraints are not met *or* if the product + * does not yet exist. */ enum GNUNET_DB_QueryStatus (*update_product)(void *cls, @@ -586,6 +591,27 @@ struct TALER_MERCHANTDB_Plugin const char *product_id, struct TALER_MERCHANTDB_ProductDetails *pd); + /** + * Lock stocks of a particular product. Note that the transaction must + * enforce that the "stocked-sold-lost >= locked" constraint holds. + * + * @param cls closure + * @param instance_id instance to lookup products for + * @param product_id product to lookup + * @param uuid the UUID that holds the lock + * @param quantity how many units should be locked + * @param expiration_time when should the lock expire + * @return database result code, #GNUNET_DB_SUCCESS_NO_RESULTS if the + * product is unknown OR if there insufficient stocks remaining + */ + enum GNUNET_DB_QueryStatus + (*lock_product)(void *cls, + const char *instance_id, + const char *product_id, + const struct GNUNET_Uuid *uuid, + uint32_t quantity, + struct GNUNET_TIME_Absolute expiration_time); + /* ****************** OLD API ******************** */ |