Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser General Public License as published by the Free Software
7 : Foundation; either version 2.1, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
12 :
13 : You should have received a copy of the GNU Lesser General Public License along with
14 : TALER; see the file COPYING.LGPL. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file merchant_api_get_statistics.c
19 : * @brief Implementation of the GET /statistics-[counter,amount]/$SLUG request of the merchant's HTTP API
20 : * @author Martin Schanzenbach
21 : */
22 : #include "platform.h"
23 : #include <curl/curl.h>
24 : #include <gnunet/gnunet_common.h>
25 : #include <gnunet/gnunet_json_lib.h>
26 : #include <jansson.h>
27 : #include <microhttpd.h> /* just for HTTP status codes */
28 : #include <gnunet/gnunet_util_lib.h>
29 : #include <gnunet/gnunet_curl_lib.h>
30 : #include "taler_merchant_service.h"
31 : #include "merchant_api_curl_defaults.h"
32 : #include <taler/taler_json_lib.h>
33 : #include <taler/taler_signatures.h>
34 :
35 : /**
36 : * Maximum number of statistics we return
37 : */
38 : #define MAX_STATISTICS 1024
39 :
40 : /**
41 : * Handle for a GET /statistics-amount/$SLUG operation.
42 : */
43 : struct TALER_MERCHANT_StatisticsAmountGetHandle
44 : {
45 : /**
46 : * The url for this request.
47 : */
48 : char *url;
49 :
50 : /**
51 : * Handle for the request.
52 : */
53 : struct GNUNET_CURL_Job *job;
54 :
55 : /**
56 : * Function to call with the result.
57 : */
58 : TALER_MERCHANT_StatisticsAmountGetCallback cb;
59 :
60 : /**
61 : * Closure for @a cb.
62 : */
63 : void *cb_cls;
64 :
65 : /**
66 : * Reference to the execution context.
67 : */
68 : struct GNUNET_CURL_Context *ctx;
69 :
70 : };
71 :
72 : /**
73 : * Handle for a GET /statistics-counter/$SLUG operation.
74 : */
75 : struct TALER_MERCHANT_StatisticsCounterGetHandle
76 : {
77 : /**
78 : * The url for this request.
79 : */
80 : char *url;
81 :
82 : /**
83 : * Handle for the request.
84 : */
85 : struct GNUNET_CURL_Job *job;
86 :
87 : /**
88 : * Function to call with the result.
89 : */
90 : TALER_MERCHANT_StatisticsCounterGetCallback cb;
91 :
92 : /**
93 : * Closure for @a cb.
94 : */
95 : void *cb_cls;
96 :
97 : /**
98 : * Reference to the execution context.
99 : */
100 : struct GNUNET_CURL_Context *ctx;
101 :
102 : };
103 :
104 :
105 : /**
106 : * Parse interval information from buckets and intervals.
107 : *
108 : * @param json overall JSON reply
109 : * @param jbuckets JSON array (or NULL!) with bucket data
110 : * @param jintervals JSON array (or NULL!) with bucket data
111 : * @param scgh operation handle
112 : * @return #GNUNET_OK on success
113 : */
114 : static enum GNUNET_GenericReturnValue
115 2 : parse_intervals_and_buckets_amt (
116 : const json_t *json,
117 : const json_t *jbuckets,
118 : const char *buckets_description,
119 : const json_t *jintervals,
120 : const char *intervals_description,
121 : struct TALER_MERCHANT_StatisticsAmountGetHandle *sgh
122 : )
123 : {
124 2 : unsigned int resp_buckets_len = json_array_size (jbuckets);
125 2 : unsigned int resp_intervals_len = json_array_size (jintervals);
126 :
127 2 : if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) ||
128 2 : (json_array_size (jintervals) != (size_t) resp_intervals_len) ||
129 2 : (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) )
130 : {
131 0 : GNUNET_break (0);
132 0 : return GNUNET_SYSERR;
133 : }
134 2 : {
135 2 : struct TALER_MERCHANT_StatisticAmountByBucket resp_buckets[
136 2 : GNUNET_NZL (resp_buckets_len)];
137 2 : struct TALER_MERCHANT_StatisticAmountByInterval resp_intervals[
138 2 : GNUNET_NZL (resp_intervals_len)];
139 : size_t index;
140 : json_t *value;
141 : enum GNUNET_GenericReturnValue ret;
142 :
143 2 : ret = GNUNET_OK;
144 2 : json_array_foreach (jintervals, index, value) {
145 0 : struct TALER_MERCHANT_StatisticAmountByInterval *jinterval
146 : = &resp_intervals[index];
147 : const json_t *amounts_arr;
148 : size_t amounts_len;
149 :
150 : struct GNUNET_JSON_Specification spec[] = {
151 0 : GNUNET_JSON_spec_timestamp ("start_time",
152 : &jinterval->start_time),
153 0 : GNUNET_JSON_spec_array_const ("cumulative_amount",
154 : &amounts_arr),
155 0 : GNUNET_JSON_spec_end ()
156 : };
157 :
158 0 : if (GNUNET_OK !=
159 0 : GNUNET_JSON_parse (value,
160 : spec,
161 : NULL, NULL))
162 : {
163 0 : GNUNET_break_op (0);
164 0 : ret = GNUNET_SYSERR;
165 0 : continue;
166 : }
167 0 : if (GNUNET_SYSERR == ret)
168 0 : break;
169 0 : amounts_len = json_array_size (amounts_arr);
170 : if (0 > amounts_len)
171 : {
172 : GNUNET_break_op (0);
173 : ret = GNUNET_SYSERR;
174 : break;
175 : }
176 0 : {
177 0 : struct TALER_Amount amt_arr[amounts_len];
178 : size_t aindex;
179 : json_t *avalue;
180 0 : jinterval->cumulative_amount_len = amounts_len;
181 0 : jinterval->cumulative_amounts = amt_arr;
182 0 : json_array_foreach (amounts_arr, aindex, avalue) {
183 0 : if (! json_is_string (avalue))
184 : {
185 0 : GNUNET_break_op (0);
186 0 : return GNUNET_SYSERR;
187 : }
188 0 : if (GNUNET_OK !=
189 0 : TALER_string_to_amount (json_string_value (avalue),
190 : &amt_arr[aindex]))
191 : {
192 0 : GNUNET_break_op (0);
193 0 : return GNUNET_SYSERR;
194 : }
195 : }
196 : }
197 : }
198 2 : ret = GNUNET_OK;
199 14 : json_array_foreach (jbuckets, index, value) {
200 12 : struct TALER_MERCHANT_StatisticAmountByBucket *jbucket
201 : = &resp_buckets[index];
202 : const json_t *amounts_arr;
203 : size_t amounts_len;
204 : struct GNUNET_JSON_Specification spec[] = {
205 12 : GNUNET_JSON_spec_timestamp ("start_time",
206 : &jbucket->start_time),
207 12 : GNUNET_JSON_spec_timestamp ("end_time",
208 : &jbucket->end_time),
209 12 : GNUNET_JSON_spec_string ("range",
210 : &jbucket->range),
211 12 : GNUNET_JSON_spec_array_const ("cumulative_amount",
212 : &amounts_arr),
213 12 : GNUNET_JSON_spec_end ()
214 : };
215 :
216 12 : if (GNUNET_OK !=
217 12 : GNUNET_JSON_parse (value,
218 : spec,
219 : NULL, NULL))
220 : {
221 0 : GNUNET_break_op (0);
222 0 : ret = GNUNET_SYSERR;
223 0 : continue;
224 : }
225 12 : if (GNUNET_SYSERR == ret)
226 0 : break;
227 12 : amounts_len = json_array_size (amounts_arr);
228 : if (0 > amounts_len)
229 : {
230 : GNUNET_break_op (0);
231 : ret = GNUNET_SYSERR;
232 : break;
233 : }
234 12 : {
235 12 : struct TALER_Amount amt_arr[amounts_len];
236 : size_t aindex;
237 : json_t *avalue;
238 12 : jbucket->cumulative_amount_len = amounts_len;
239 12 : jbucket->cumulative_amounts = amt_arr;
240 24 : json_array_foreach (amounts_arr, aindex, avalue) {
241 12 : if (! json_is_string (avalue))
242 : {
243 0 : GNUNET_break_op (0);
244 0 : return GNUNET_SYSERR;
245 : }
246 12 : if (GNUNET_OK !=
247 12 : TALER_string_to_amount (json_string_value (avalue),
248 : &amt_arr[aindex]))
249 : {
250 0 : GNUNET_break_op (0);
251 0 : return GNUNET_SYSERR;
252 : }
253 : }
254 : }
255 : }
256 2 : if (GNUNET_OK == ret)
257 : {
258 2 : struct TALER_MERCHANT_StatisticsAmountGetResponse gsr = {
259 : .hr.http_status = MHD_HTTP_OK,
260 : .hr.reply = json,
261 : .details.ok.buckets_length = resp_buckets_len,
262 : .details.ok.buckets = resp_buckets,
263 : .details.ok.buckets_description = buckets_description,
264 : .details.ok.intervals_length = resp_intervals_len,
265 : .details.ok.intervals = resp_intervals,
266 : .details.ok.intervals_description = intervals_description,
267 : };
268 2 : sgh->cb (sgh->cb_cls,
269 : &gsr);
270 2 : sgh->cb = NULL; /* just to be sure */
271 : }
272 2 : return ret;
273 : }
274 : }
275 :
276 :
277 : /**
278 : * Function called when we're done processing the
279 : * HTTP GET /statistics-amount/$SLUG request.
280 : *
281 : * @param cls the `struct TALER_MERCHANT_StatisticsAmountGetHandle`
282 : * @param response_code HTTP response code, 0 on error
283 : * @param response response body, NULL if not in JSON
284 : */
285 : static void
286 2 : handle_get_statistics_amount_finished (void *cls,
287 : long response_code,
288 : const void *response)
289 : {
290 2 : struct TALER_MERCHANT_StatisticsAmountGetHandle *handle = cls;
291 2 : const json_t *json = response;
292 2 : struct TALER_MERCHANT_StatisticsAmountGetResponse res = {
293 2 : .hr.http_status = (unsigned int) response_code,
294 : .hr.reply = json
295 : };
296 :
297 2 : handle->job = NULL;
298 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
299 : "Got /statistics-amount/$SLUG response with status code %u\n",
300 : (unsigned int) response_code);
301 2 : switch (response_code)
302 : {
303 2 : case MHD_HTTP_OK:
304 : {
305 : const json_t *buckets;
306 : const json_t *intervals;
307 2 : const char *buckets_description = NULL;
308 2 : const char *intervals_description = NULL;
309 : struct GNUNET_JSON_Specification spec[] = {
310 2 : GNUNET_JSON_spec_array_const ("buckets",
311 : &buckets),
312 2 : GNUNET_JSON_spec_mark_optional (
313 : GNUNET_JSON_spec_string ("buckets_description",
314 : &buckets_description),
315 : NULL),
316 2 : GNUNET_JSON_spec_array_const ("intervals",
317 : &intervals),
318 2 : GNUNET_JSON_spec_mark_optional (
319 : GNUNET_JSON_spec_string ("intervals_description",
320 : &intervals_description),
321 : NULL),
322 2 : GNUNET_JSON_spec_end ()
323 : };
324 :
325 2 : if (GNUNET_OK !=
326 2 : GNUNET_JSON_parse (json,
327 : spec,
328 : NULL, NULL))
329 : {
330 0 : res.hr.http_status = 0;
331 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
332 0 : break;
333 : }
334 2 : if (GNUNET_OK ==
335 2 : parse_intervals_and_buckets_amt (json,
336 : buckets,
337 : buckets_description,
338 : intervals,
339 : intervals_description,
340 : handle))
341 : {
342 2 : TALER_MERCHANT_statistic_amount_get_cancel (handle);
343 2 : return;
344 : }
345 0 : res.hr.http_status = 0;
346 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
347 0 : break;
348 : }
349 0 : case MHD_HTTP_UNAUTHORIZED:
350 0 : res.hr.ec = TALER_JSON_get_error_code (json);
351 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
352 : /* Nothing really to verify, merchant says we need to authenticate. */
353 0 : break;
354 0 : case MHD_HTTP_NOT_FOUND:
355 0 : res.hr.ec = TALER_JSON_get_error_code (json);
356 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
357 0 : break;
358 0 : default:
359 : /* unexpected response code */
360 0 : res.hr.ec = TALER_JSON_get_error_code (json);
361 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
362 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
363 : "Unexpected response code %u/%d\n",
364 : (unsigned int) response_code,
365 : (int) res.hr.ec);
366 0 : break;
367 : }
368 : }
369 :
370 :
371 : /**
372 : * Parse interval information from @a ia.
373 : *
374 : * @param json overall JSON reply
375 : * @param jbuckets JSON array (or NULL!) with bucket data
376 : * @param jintervals JSON array (or NULL!) with bucket data
377 : * @param scgh operation handle
378 : * @return #GNUNET_OK on success
379 : */
380 : static enum GNUNET_GenericReturnValue
381 4 : parse_intervals_and_buckets (
382 : const json_t *json,
383 : const json_t *jbuckets,
384 : const char *buckets_description,
385 : const json_t *jintervals,
386 : const char *intervals_description,
387 : struct TALER_MERCHANT_StatisticsCounterGetHandle *scgh)
388 : {
389 4 : unsigned int resp_buckets_len = json_array_size (jbuckets);
390 4 : unsigned int resp_intervals_len = json_array_size (jintervals);
391 :
392 4 : if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) ||
393 4 : (json_array_size (jintervals) != (size_t) resp_intervals_len) ||
394 4 : (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) )
395 : {
396 0 : GNUNET_break (0);
397 0 : return GNUNET_SYSERR;
398 : }
399 4 : {
400 4 : struct TALER_MERCHANT_StatisticCounterByBucket resp_buckets[
401 4 : GNUNET_NZL (resp_buckets_len)];
402 4 : struct TALER_MERCHANT_StatisticCounterByInterval resp_intervals[
403 4 : GNUNET_NZL (resp_intervals_len)];
404 : size_t index;
405 : json_t *value;
406 : enum GNUNET_GenericReturnValue ret;
407 :
408 4 : ret = GNUNET_OK;
409 4 : json_array_foreach (jintervals, index, value) {
410 0 : struct TALER_MERCHANT_StatisticCounterByInterval *jinterval
411 : = &resp_intervals[index];
412 : struct GNUNET_JSON_Specification spec[] = {
413 0 : GNUNET_JSON_spec_timestamp ("start_time",
414 : &jinterval->start_time),
415 0 : GNUNET_JSON_spec_uint64 ("cumulative_counter",
416 : &jinterval->cumulative_counter),
417 0 : GNUNET_JSON_spec_end ()
418 : };
419 :
420 0 : if (GNUNET_OK !=
421 0 : GNUNET_JSON_parse (value,
422 : spec,
423 : NULL, NULL))
424 : {
425 0 : GNUNET_break_op (0);
426 0 : ret = GNUNET_SYSERR;
427 0 : continue;
428 : }
429 0 : if (GNUNET_SYSERR == ret)
430 0 : break;
431 : }
432 4 : ret = GNUNET_OK;
433 28 : json_array_foreach (jbuckets, index, value) {
434 24 : struct TALER_MERCHANT_StatisticCounterByBucket *jbucket = &resp_buckets[
435 : index];
436 : struct GNUNET_JSON_Specification spec[] = {
437 24 : GNUNET_JSON_spec_timestamp ("start_time",
438 : &jbucket->start_time),
439 24 : GNUNET_JSON_spec_timestamp ("end_time",
440 : &jbucket->end_time),
441 24 : GNUNET_JSON_spec_string ("range",
442 : &jbucket->range),
443 24 : GNUNET_JSON_spec_uint64 ("cumulative_counter",
444 : &jbucket->cumulative_counter),
445 24 : GNUNET_JSON_spec_end ()
446 : };
447 :
448 24 : if (GNUNET_OK !=
449 24 : GNUNET_JSON_parse (value,
450 : spec,
451 : NULL, NULL))
452 : {
453 0 : GNUNET_break_op (0);
454 0 : ret = GNUNET_SYSERR;
455 0 : continue;
456 : }
457 24 : if (GNUNET_SYSERR == ret)
458 0 : break;
459 : }
460 4 : if (GNUNET_OK == ret)
461 : {
462 4 : struct TALER_MERCHANT_StatisticsCounterGetResponse gsr = {
463 : .hr.http_status = MHD_HTTP_OK,
464 : .hr.reply = json,
465 : .details.ok.buckets_length = resp_buckets_len,
466 : .details.ok.buckets = resp_buckets,
467 : .details.ok.buckets_description = buckets_description,
468 : .details.ok.intervals_length = resp_intervals_len,
469 : .details.ok.intervals = resp_intervals,
470 : .details.ok.intervals_description = intervals_description,
471 : };
472 4 : scgh->cb (scgh->cb_cls,
473 : &gsr);
474 4 : scgh->cb = NULL; /* just to be sure */
475 : }
476 4 : return ret;
477 : }
478 : }
479 :
480 :
481 : /**
482 : * Function called when we're done processing the
483 : * HTTP GET /statistics-counter/$SLUG request.
484 : *
485 : * @param cls the `struct TALER_MERCHANT_StatisticsCounterGetHandle`
486 : * @param response_code HTTP response code, 0 on error
487 : * @param response response body, NULL if not in JSON
488 : */
489 : static void
490 4 : handle_get_statistics_counter_finished (void *cls,
491 : long response_code,
492 : const void *response)
493 : {
494 4 : struct TALER_MERCHANT_StatisticsCounterGetHandle *handle = cls;
495 4 : const json_t *json = response;
496 4 : struct TALER_MERCHANT_StatisticsCounterGetResponse res = {
497 4 : .hr.http_status = (unsigned int) response_code,
498 : .hr.reply = json
499 : };
500 :
501 4 : handle->job = NULL;
502 4 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
503 : "Got /statistics-counter/$SLUG response with status code %u\n",
504 : (unsigned int) response_code);
505 4 : switch (response_code)
506 : {
507 4 : case MHD_HTTP_OK:
508 : {
509 : const json_t *buckets;
510 : const json_t *intervals;
511 : const char *buckets_description;
512 : const char *intervals_description;
513 : struct GNUNET_JSON_Specification spec[] = {
514 4 : GNUNET_JSON_spec_array_const ("buckets",
515 : &buckets),
516 4 : GNUNET_JSON_spec_mark_optional (
517 : GNUNET_JSON_spec_string ("buckets_description",
518 : &buckets_description),
519 : NULL),
520 4 : GNUNET_JSON_spec_array_const ("intervals",
521 : &intervals),
522 4 : GNUNET_JSON_spec_mark_optional (
523 : GNUNET_JSON_spec_string ("intervals_description",
524 : &intervals_description),
525 : NULL),
526 4 : GNUNET_JSON_spec_end ()
527 : };
528 :
529 4 : if (GNUNET_OK !=
530 4 : GNUNET_JSON_parse (json,
531 : spec,
532 : NULL, NULL))
533 : {
534 0 : res.hr.http_status = 0;
535 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
536 0 : break;
537 : }
538 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
539 : "%s\n", json_dumps (json, JSON_INDENT (1)));
540 4 : if (GNUNET_OK ==
541 4 : parse_intervals_and_buckets (json,
542 : buckets,
543 : buckets_description,
544 : intervals,
545 : intervals_description,
546 : handle))
547 : {
548 4 : TALER_MERCHANT_statistic_counter_get_cancel (handle);
549 4 : return;
550 : }
551 0 : res.hr.http_status = 0;
552 0 : res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
553 0 : break;
554 : }
555 0 : case MHD_HTTP_UNAUTHORIZED:
556 0 : res.hr.ec = TALER_JSON_get_error_code (json);
557 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
558 : /* Nothing really to verify, merchant says we need to authenticate. */
559 0 : break;
560 0 : case MHD_HTTP_NOT_FOUND:
561 0 : res.hr.ec = TALER_JSON_get_error_code (json);
562 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
563 0 : break;
564 0 : default:
565 : /* unexpected response code */
566 0 : res.hr.ec = TALER_JSON_get_error_code (json);
567 0 : res.hr.hint = TALER_JSON_get_error_hint (json);
568 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
569 : "Unexpected response code %u/%d\n",
570 : (unsigned int) response_code,
571 : (int) res.hr.ec);
572 0 : break;
573 : }
574 : }
575 :
576 :
577 : struct TALER_MERCHANT_StatisticsCounterGetHandle *
578 4 : TALER_MERCHANT_statistic_counter_get (
579 : struct GNUNET_CURL_Context *ctx,
580 : const char *backend_url,
581 : const char *slug,
582 : enum TALER_MERCHANT_StatisticsType stype,
583 : TALER_MERCHANT_StatisticsCounterGetCallback cb,
584 : void *cb_cls)
585 : {
586 : struct TALER_MERCHANT_StatisticsCounterGetHandle *handle;
587 : CURL *eh;
588 :
589 4 : handle = GNUNET_new (struct TALER_MERCHANT_StatisticsCounterGetHandle);
590 4 : handle->ctx = ctx;
591 4 : handle->cb = cb;
592 4 : handle->cb_cls = cb_cls;
593 : {
594 4 : const char *filter = NULL;
595 : char *path;
596 :
597 4 : switch (stype)
598 : {
599 0 : case TALER_MERCHANT_STATISTICS_BY_BUCKET:
600 0 : filter = "bucket";
601 0 : break;
602 0 : case TALER_MERCHANT_STATISTICS_BY_INTERVAL:
603 0 : filter = "interval";
604 0 : break;
605 4 : case TALER_MERCHANT_STATISTICS_ALL:
606 4 : filter = NULL;
607 4 : break;
608 : }
609 4 : GNUNET_asprintf (&path,
610 : "private/statistics-counter/%s",
611 : slug);
612 4 : handle->url = TALER_url_join (backend_url,
613 : path,
614 : "by",
615 : filter,
616 : NULL);
617 4 : GNUNET_free (path);
618 : }
619 4 : if (NULL == handle->url)
620 : {
621 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
622 : "Could not construct request URL.\n");
623 0 : GNUNET_free (handle);
624 0 : return NULL;
625 : }
626 4 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627 : "Requesting URL '%s'\n",
628 : handle->url);
629 4 : eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
630 4 : handle->job = GNUNET_CURL_job_add (ctx,
631 : eh,
632 : &handle_get_statistics_counter_finished,
633 : handle);
634 4 : return handle;
635 : }
636 :
637 :
638 : void
639 4 : TALER_MERCHANT_statistic_counter_get_cancel (
640 : struct TALER_MERCHANT_StatisticsCounterGetHandle *handle)
641 : {
642 4 : if (NULL != handle->job)
643 0 : GNUNET_CURL_job_cancel (handle->job);
644 4 : GNUNET_free (handle->url);
645 4 : GNUNET_free (handle);
646 4 : }
647 :
648 :
649 : struct TALER_MERCHANT_StatisticsAmountGetHandle *
650 2 : TALER_MERCHANT_statistic_amount_get (
651 : struct GNUNET_CURL_Context *ctx,
652 : const char *backend_url,
653 : const char *slug,
654 : enum TALER_MERCHANT_StatisticsType stype,
655 : TALER_MERCHANT_StatisticsAmountGetCallback cb,
656 : void *cb_cls)
657 : {
658 : struct TALER_MERCHANT_StatisticsAmountGetHandle *handle;
659 : CURL *eh;
660 :
661 2 : handle = GNUNET_new (struct TALER_MERCHANT_StatisticsAmountGetHandle);
662 2 : handle->ctx = ctx;
663 2 : handle->cb = cb;
664 2 : handle->cb_cls = cb_cls;
665 : {
666 2 : const char *filter = NULL;
667 : char *path;
668 :
669 2 : switch (stype)
670 : {
671 0 : case TALER_MERCHANT_STATISTICS_BY_BUCKET:
672 0 : filter = "bucket";
673 0 : break;
674 0 : case TALER_MERCHANT_STATISTICS_BY_INTERVAL:
675 0 : filter = "interval";
676 0 : break;
677 2 : case TALER_MERCHANT_STATISTICS_ALL:
678 2 : filter = NULL;
679 2 : break;
680 : }
681 2 : GNUNET_asprintf (&path,
682 : "private/statistics-amount/%s",
683 : slug);
684 2 : handle->url = TALER_url_join (backend_url,
685 : path,
686 : "by",
687 : filter,
688 : NULL);
689 2 : GNUNET_free (path);
690 : }
691 2 : if (NULL == handle->url)
692 : {
693 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
694 : "Could not construct request URL.\n");
695 0 : GNUNET_free (handle);
696 0 : return NULL;
697 : }
698 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
699 : "Requesting URL '%s'\n",
700 : handle->url);
701 2 : eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
702 2 : handle->job = GNUNET_CURL_job_add (ctx,
703 : eh,
704 : &handle_get_statistics_amount_finished,
705 : handle);
706 2 : return handle;
707 : }
708 :
709 :
710 : void
711 2 : TALER_MERCHANT_statistic_amount_get_cancel (
712 : struct TALER_MERCHANT_StatisticsAmountGetHandle *handle)
713 : {
714 2 : if (NULL != handle->job)
715 0 : GNUNET_CURL_job_cancel (handle->job);
716 2 : GNUNET_free (handle->url);
717 2 : GNUNET_free (handle);
718 2 : }
|