commit 5c3299e4ecc82fa58b7014da8e8899d2c295bf42
parent 96f82418c638a2d91f047319fdd589068e8f8e7f
Author: Christian Grothoff <christian@grothoff.org>
Date: Thu, 30 Jul 2026 11:18:49 +0200
implement #11693
Diffstat:
4 files changed, 111 insertions(+), 20 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_get-private-orders.c b/src/backend/taler-merchant-httpd_get-private-orders.c
@@ -1495,26 +1495,67 @@ TMH_private_get_orders (const struct TMH_RequestHandler *rh,
}
}
}
- if ( (po->of.delta > 0) &&
- (NULL !=
- MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "max_age")) )
+ if (po->of.delta > 0)
{
/* Only narrow the range if the client actually asked for it:
- without the argument, the parser below would set @e duration
- to zero and thus cut off everything created before *now*. */
- struct GNUNET_TIME_Relative duration;
- struct GNUNET_TIME_Absolute cut_off;
-
- TALER_MHD_parse_request_rel_time (connection,
- "max_age",
- &duration);
- cut_off = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (),
- duration);
- po->of.date = GNUNET_TIME_timestamp_max (
- po->of.date,
- GNUNET_TIME_absolute_to_timestamp (cut_off));
+ the default duration of zero would cut off everything
+ created before *now*. */
+ bool have_max_age
+ = (NULL !=
+ MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "max_age"));
+ bool have_max_age_s
+ = (NULL !=
+ MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "max_age_s"));
+ struct GNUNET_TIME_Relative duration = GNUNET_TIME_UNIT_ZERO;
+
+ if (have_max_age)
+ {
+ /* deprecated in protocol v33, superseded by "max_age_s" */
+ TALER_MHD_parse_request_rel_time (connection,
+ "max_age",
+ &duration);
+ }
+ if (have_max_age_s)
+ {
+ /* since protocol v33 */
+ uint64_t max_age_s = 0;
+ struct GNUNET_TIME_Relative age;
+
+ TALER_MHD_parse_request_number (connection,
+ "max_age_s",
+ &max_age_s);
+ age = GNUNET_TIME_relative_saturating_multiply (GNUNET_TIME_UNIT_SECONDS,
+ max_age_s);
+ if (have_max_age &&
+ GNUNET_TIME_relative_cmp (duration,
+ !=,
+ age))
+ {
+ unsigned long long max_age_ms
+ = duration.rel_value_us
+ / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Client gave conflicting values for `max_age' (%llu ms) and `max_age_s' (%llu s); using `max_age_s'\n",
+ max_age_ms,
+ (unsigned long long) max_age_s);
+ }
+ duration = age;
+ }
+ if (have_max_age || have_max_age_s)
+ {
+ struct GNUNET_TIME_Absolute cut_off;
+
+ cut_off = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (),
+ duration);
+ po->of.date = GNUNET_TIME_timestamp_max (
+ po->of.date,
+ GNUNET_TIME_absolute_to_timestamp (cut_off));
+ }
}
if (po->of.delta > 0)
po->of.start_row = 0;
diff --git a/src/include/taler/merchant/get-private-orders.h b/src/include/taler/merchant/get-private-orders.h
@@ -350,6 +350,8 @@ TALER_MERCHANT_get_private_orders_create (
/**
* Set maximum age filter. Only return orders younger than the
* specified age. Only applicable if limit is positive.
+ * Transmitted with a granularity of one second, so sub-second
+ * parts of @a a are rounded down.
* @since protocol v27.
*
* @param a maximum age as a relative time
diff --git a/src/lib/merchant_api_get-private-orders.c b/src/lib/merchant_api_get-private-orders.c
@@ -420,7 +420,8 @@ TALER_MERCHANT_get_private_orders_start (
sizeof (mabuf),
"%llu",
(unsigned long long)
- oph->max_age.rel_value_us);
+ (oph->max_age.rel_value_us
+ / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
GNUNET_snprintf (dbuf,
sizeof (dbuf),
"%lld",
@@ -496,7 +497,7 @@ TALER_MERCHANT_get_private_orders_start (
fec,
"summary_filter",
sfilt,
- "max_age",
+ "max_age_s",
oph->have_max_age
? mabuf
: NULL,
diff --git a/src/testing/test_merchant_transfer_tracking.sh b/src/testing/test_merchant_transfer_tracking.sh
@@ -193,6 +193,53 @@ echo " OK"
ORDER_ID=$(jq -e -r .order_id < "$LAST_RESPONSE")
#TOKEN=$(jq -e -r .token < "$LAST_RESPONSE")
+# Lists orders with the given extra query arguments and checks whether the
+# order created above is included ($2 is "some") or filtered out ($2 is
+# "none"). The age filters only apply to an ascending listing, hence the
+# hard-coded positive "limit".
+check_order_listing() {
+ local args="$1"
+ local expect="$2"
+ local status
+ local num
+
+ status=$(curl "http://localhost:9966/instances/test/private/orders?limit=20&$args" \
+ -w "%{http_code}" \
+ -s \
+ -o "$LAST_RESPONSE")
+ if [ "$status" != "200" ]
+ then
+ cat "$LAST_RESPONSE"
+ exit_fail "Expected 200 ok listing orders with '$args'. got: $status"
+ fi
+ num=$(jq -e -r '.orders | length' < "$LAST_RESPONSE")
+ if [ "none" = "$expect" ] && [ "0" != "$num" ]
+ then
+ exit_fail "Expected no orders listed with '$args', got $num"
+ fi
+ if [ "some" = "$expect" ] && [ "0" = "$num" ]
+ then
+ exit_fail "Expected orders to be listed with '$args', got none"
+ fi
+}
+
+echo -n "Checking order listing with max_age_s..."
+check_order_listing 'max_age_s=3600' some
+check_order_listing 'max_age_s=0' none
+echo " OK"
+
+# "max_age" was deprecated in protocol v33, but MUST still work.
+echo -n "Checking order listing with deprecated max_age..."
+check_order_listing 'max_age=3600000' some
+check_order_listing 'max_age=0' none
+echo " OK"
+
+# If both are given, "max_age_s" wins (and the backend logs a warning).
+echo -n "Checking that max_age_s takes precedence over max_age..."
+check_order_listing 'max_age=0&max_age_s=3600' some
+check_order_listing 'max_age=3600000&max_age_s=0' none
+echo " OK"
+
echo -n "Checking for Typst ..."
if typst --version 2> /dev/null > /dev/null
then