commit a0ce2fd3c295559f11e27c0f375d11d2e6a96908
parent e03008ad962fae68116da0fd4556ed93e4ffd852
Author: Christian Grothoff <christian@grothoff.org>
Date: Mon, 30 Mar 2026 01:55:25 +0200
add options for GET /private/products API in libtalermerchant
Diffstat:
2 files changed, 424 insertions(+), 3 deletions(-)
diff --git a/src/include/taler/taler-merchant/get-private-products.h b/src/include/taler/taler-merchant/get-private-products.h
@@ -26,6 +26,114 @@
/**
+ * Possible options we can set for the GET /private/products request.
+ */
+enum TALER_MERCHANT_GetPrivateProductsOption
+{
+ /**
+ * End of list of options.
+ */
+ TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_END = 0,
+
+ /**
+ * Return at most N values. Negative for descending by row ID,
+ * positive for ascending by row ID. Default is 20.
+ * @since protocol v12.
+ */
+ TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_LIMIT,
+
+ /**
+ * Starting product_serial_id for pagination.
+ * @since protocol v12.
+ */
+ TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_OFFSET,
+
+ /**
+ * Filter by category name (case-insensitive substring match).
+ * @since protocol v23.
+ */
+ TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_CATEGORY_FILTER,
+
+ /**
+ * Filter by product name (case-insensitive substring match).
+ * @since protocol v23.
+ */
+ TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_NAME_FILTER,
+
+ /**
+ * Filter by product description (case-insensitive substring match).
+ * @since protocol v23.
+ */
+ TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_FILTER,
+
+ /**
+ * Filter by product group serial.
+ * @since protocol v25.
+ */
+ TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_PRODUCT_GROUP_SERIAL
+
+};
+
+
+/**
+ * Value for an option for the GET /private/products request.
+ */
+struct TALER_MERCHANT_GetPrivateProductsOptionValue
+{
+
+ /**
+ * Type of the option being set.
+ */
+ enum TALER_MERCHANT_GetPrivateProductsOption option;
+
+ /**
+ * Specific option value.
+ */
+ union
+ {
+
+ /**
+ * Value if @e option is
+ * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_LIMIT.
+ */
+ int64_t limit;
+
+ /**
+ * Value if @e option is
+ * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_OFFSET.
+ */
+ uint64_t offset;
+
+ /**
+ * Value if @e option is
+ * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_CATEGORY_FILTER.
+ */
+ const char *category_filter;
+
+ /**
+ * Value if @e option is
+ * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_NAME_FILTER.
+ */
+ const char *name_filter;
+
+ /**
+ * Value if @e option is
+ * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_FILTER.
+ */
+ const char *description_filter;
+
+ /**
+ * Value if @e option is
+ * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_PRODUCT_GROUP_SERIAL.
+ */
+ uint64_t product_group_serial;
+
+ } details;
+
+};
+
+
+/**
* Handle for a GET /private/products request.
*/
struct TALER_MERCHANT_GetPrivateProductsHandle;
@@ -105,6 +213,143 @@ TALER_MERCHANT_get_private_products_create (
const char *url);
+/**
+ * Terminate the list of the options.
+ *
+ * @return the terminating object of struct TALER_MERCHANT_GetPrivateProductsOptionValue
+ */
+#define TALER_MERCHANT_get_private_products_option_end_() \
+ (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \
+ { \
+ .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_END \
+ }
+
+/**
+ * Set limit on the number of results to return.
+ *
+ * @param l limit on the number of results to return
+ * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue
+ */
+#define TALER_MERCHANT_get_private_products_option_limit(l) \
+ (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \
+ { \
+ .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_LIMIT, \
+ .details.limit = (l) \
+ }
+
+/**
+ * Set row offset from which to return results.
+ *
+ * @param o offset to use
+ * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue
+ */
+#define TALER_MERCHANT_get_private_products_option_offset(o) \
+ (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \
+ { \
+ .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_OFFSET, \
+ .details.offset = (o) \
+ }
+
+/**
+ * Set category name filter (case-insensitive substring match).
+ *
+ * @param f category filter string
+ * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue
+ */
+#define TALER_MERCHANT_get_private_products_option_category_filter(f) \
+ (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \
+ { \
+ .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_CATEGORY_FILTER, \
+ .details.category_filter = (f) \
+ }
+
+/**
+ * Set product name filter (case-insensitive substring match).
+ *
+ * @param f name filter string
+ * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue
+ */
+#define TALER_MERCHANT_get_private_products_option_name_filter(f) \
+ (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \
+ { \
+ .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_NAME_FILTER, \
+ .details.name_filter = (f) \
+ }
+
+/**
+ * Set product description filter (case-insensitive substring match).
+ *
+ * @param f description filter string
+ * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue
+ */
+#define TALER_MERCHANT_get_private_products_option_description_filter(f) \
+ (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \
+ { \
+ .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_FILTER, \
+ .details.description_filter = (f) \
+ }
+
+/**
+ * Set product group serial filter.
+ *
+ * @param s product group serial to filter by
+ * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue
+ */
+#define TALER_MERCHANT_get_private_products_option_product_group_serial(s) \
+ (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \
+ { \
+ .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_PRODUCT_GROUP_SERIAL, \
+ .details.product_group_serial = (s) \
+ }
+
+
+/**
+ * Set the requested options for the operation.
+ *
+ * If any option fail other options may be or may be not applied.
+ *
+ * @param gpph the request to set the options for
+ * @param num_options length of the @a options array
+ * @param options an array of options
+ * @return #GNUNET_OK on success,
+ * #GNUNET_NO on failure,
+ * #GNUNET_SYSERR on internal error
+ */
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_get_private_products_set_options_ (
+ struct TALER_MERCHANT_GetPrivateProductsHandle *gpph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_GetPrivateProductsOptionValue *options);
+
+
+/**
+ * Set the requested options for the operation.
+ *
+ * If any option fail other options may be or may be not applied.
+ *
+ * It should be used with helpers that create required options, for example:
+ *
+ * TALER_MERCHANT_get_private_products_set_options (
+ * gpph,
+ * TALER_MERCHANT_get_private_products_option_limit (20),
+ * TALER_MERCHANT_get_private_products_option_name_filter ("widget"));
+ *
+ * @param gpph the request to set the options for
+ * @param ... the list of the options, each option must be created
+ * by helpers TALER_MERCHANT_get_private_products_option_NAME(VALUE)
+ * @return #GNUNET_OK on success,
+ * #GNUNET_NO on failure,
+ * #GNUNET_SYSERR on internal error
+ */
+#define TALER_MERCHANT_get_private_products_set_options(gpph,...) \
+ TALER_MERCHANT_get_private_products_set_options_ ( \
+ gpph, \
+ TALER_MERCHANT_COMMON_OPTIONS_ARRAY_MAX_SIZE, \
+ ((const struct TALER_MERCHANT_GetPrivateProductsOptionValue[]) \
+ {__VA_ARGS__, TALER_MERCHANT_get_private_products_option_end_ () } \
+ ))
+
+
#ifndef TALER_MERCHANT_GET_PRIVATE_PRODUCTS_RESULT_CLOSURE
/**
* Type of the closure used by
diff --git a/src/lib/merchant_api_get-private-products.c b/src/lib/merchant_api_get-private-products.c
@@ -70,6 +70,46 @@ struct TALER_MERCHANT_GetPrivateProductsHandle
* Reference to the execution context.
*/
struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Starting row for pagination.
+ */
+ uint64_t offset;
+
+ /**
+ * Limit on number of results.
+ */
+ int64_t limit;
+
+ /**
+ * Category name filter, or NULL.
+ */
+ char *category_filter;
+
+ /**
+ * Product name filter, or NULL.
+ */
+ char *name_filter;
+
+ /**
+ * Product description filter, or NULL.
+ */
+ char *description_filter;
+
+ /**
+ * Product group serial filter.
+ */
+ uint64_t product_group_serial;
+
+ /**
+ * True if offset was explicitly set.
+ */
+ bool have_offset;
+
+ /**
+ * True if product_group_serial was explicitly set.
+ */
+ bool have_product_group_serial;
};
@@ -215,10 +255,68 @@ TALER_MERCHANT_get_private_products_create (
gpph = GNUNET_new (struct TALER_MERCHANT_GetPrivateProductsHandle);
gpph->ctx = ctx;
gpph->base_url = GNUNET_strdup (url);
+ gpph->limit = 20;
+ gpph->offset = INT64_MAX;
return gpph;
}
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_get_private_products_set_options_ (
+ struct TALER_MERCHANT_GetPrivateProductsHandle *gpph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_GetPrivateProductsOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ const struct TALER_MERCHANT_GetPrivateProductsOptionValue *opt =
+ &options[i];
+
+ switch (opt->option)
+ {
+ case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_LIMIT:
+ gpph->limit = opt->details.limit;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_OFFSET:
+ if (opt->details.offset > INT64_MAX)
+ {
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ gpph->offset = opt->details.offset;
+ gpph->have_offset = true;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_CATEGORY_FILTER:
+ GNUNET_free (gpph->category_filter);
+ if (NULL != opt->details.category_filter)
+ gpph->category_filter = GNUNET_strdup (opt->details.category_filter);
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_NAME_FILTER:
+ GNUNET_free (gpph->name_filter);
+ if (NULL != opt->details.name_filter)
+ gpph->name_filter = GNUNET_strdup (opt->details.name_filter);
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_FILTER:
+ GNUNET_free (gpph->description_filter);
+ if (NULL != opt->details.description_filter)
+ gpph->description_filter =
+ GNUNET_strdup (opt->details.description_filter);
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_PRODUCT_GROUP_SERIAL:
+ gpph->product_group_serial = opt->details.product_group_serial;
+ gpph->have_product_group_serial = true;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
enum TALER_ErrorCode
TALER_MERCHANT_get_private_products_start (
struct TALER_MERCHANT_GetPrivateProductsHandle *gpph,
@@ -229,20 +327,95 @@ TALER_MERCHANT_get_private_products_start (
gpph->cb = cb;
gpph->cb_cls = cb_cls;
- gpph->url = TALER_url_join (gpph->base_url,
- "private/products",
- NULL);
+ {
+ char *cfilt = NULL;
+ char *nfilt = NULL;
+ char *dfilt = NULL;
+ char lbuf[30];
+ char obuf[30];
+ char pgsbuf[30];
+ bool have_offset;
+
+ GNUNET_snprintf (lbuf,
+ sizeof (lbuf),
+ "%lld",
+ (long long) gpph->limit);
+ GNUNET_snprintf (obuf,
+ sizeof (obuf),
+ "%llu",
+ (unsigned long long) gpph->offset);
+ if (gpph->have_product_group_serial)
+ GNUNET_snprintf (pgsbuf,
+ sizeof (pgsbuf),
+ "%llu",
+ (unsigned long long) gpph->product_group_serial);
+ if (gpph->limit > 0)
+ {
+ have_offset = gpph->have_offset
+ && (0 != gpph->offset);
+ }
+ else
+ {
+ have_offset = gpph->have_offset
+ && (INT64_MAX != gpph->offset);
+ }
+ if (NULL != gpph->category_filter)
+ (void) GNUNET_STRINGS_urlencode (strlen (gpph->category_filter),
+ gpph->category_filter,
+ &cfilt);
+ if (NULL != gpph->name_filter)
+ (void) GNUNET_STRINGS_urlencode (strlen (gpph->name_filter),
+ gpph->name_filter,
+ &nfilt);
+ if (NULL != gpph->description_filter)
+ (void) GNUNET_STRINGS_urlencode (strlen (gpph->description_filter),
+ gpph->description_filter,
+ &dfilt);
+ gpph->url = TALER_url_join (gpph->base_url,
+ "private/products",
+ "limit",
+ (20 != gpph->limit)
+ ? lbuf
+ : NULL,
+ "offset",
+ have_offset
+ ? obuf
+ : NULL,
+ "category_filter",
+ cfilt,
+ "name_filter",
+ nfilt,
+ "description_filter",
+ dfilt,
+ "product_group_serial",
+ gpph->have_product_group_serial
+ ? pgsbuf
+ : NULL,
+ NULL);
+ GNUNET_free (cfilt);
+ GNUNET_free (nfilt);
+ GNUNET_free (dfilt);
+ }
if (NULL == gpph->url)
+ {
+ GNUNET_break (0);
return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ }
eh = TALER_MERCHANT_curl_easy_get_ (gpph->url);
if (NULL == eh)
+ {
+ GNUNET_break (0);
return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ }
gpph->job = GNUNET_CURL_job_add (gpph->ctx,
eh,
&handle_get_products_finished,
gpph);
if (NULL == gpph->job)
+ {
+ GNUNET_break (0);
return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
return TALER_EC_NONE;
}
@@ -257,6 +430,9 @@ TALER_MERCHANT_get_private_products_cancel (
gpph->job = NULL;
}
GNUNET_free (gpph->url);
+ GNUNET_free (gpph->category_filter);
+ GNUNET_free (gpph->name_filter);
+ GNUNET_free (gpph->description_filter);
GNUNET_free (gpph->base_url);
GNUNET_free (gpph);
}