umami-ruby 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24515824a954d2c9b334b0445ef3a64f2faf79bb9fd0ec26657b702f0e170951
4
- data.tar.gz: 2eec535b5aa02bf528b05a7d767d843b5781d8d970d03d7070953fc604582904
3
+ metadata.gz: adc8943170e57c2126ffddff58f399b17ff3cb6f0736b293a75fd32c1f1fe6c7
4
+ data.tar.gz: 8a5477ecfeaca6b2cb2857c3b715fa4d32b01ad953b2dd640385d7344c526e0f
5
5
  SHA512:
6
- metadata.gz: 53784a10bf0025c49791ace32354800ac39bdb7e662234a7f81f78f50538234aec5c32fda101bd7d844bd3df5eaf8a94e6002d85037729db637b8ff818e2d55f
7
- data.tar.gz: 84cb4db13dd978dfb0ac65982081fe3a1a23b8e0b3f755c7cfc0c920e53c32d16861630b5a17c43059882c43222ce62f4e6aed1ff1b599d4da2e92bd56e56375
6
+ metadata.gz: ae319a33a3471d8688dfc36444eca87130c1c49fac89694f99377e91b5823454592164b18667cb06bc58fe43e2373040fa061772e79057ba02f76d9132bcd9e3
7
+ data.tar.gz: 87991d5621f16af9db3ae88e29a1b9b2d003c579418e1b92fca9054800aaea7f1ddacc7cc4c4922388dff0de8a0df9f0b8e6f429fbe81704604bd93351b3f5fc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [0.4.0] - 2026-11-06
2
+
3
+ ### Added
4
+
5
+ - `website_pageviews_over_range`: pageviews and sessions over a range of any length, keeping the unit you asked for. Umami silently answers `unit: "day"` in months once a range spans more than 7 calendar months (and `unit: "hour"` in days past 30 days), so a year of daily points came back as twelve monthly ones. The new method splits the range into requests short enough for the unit (90 days for `day`, 30 for `hour`) and joins the results.
6
+
7
+ ### Documentation
8
+
9
+ - README: "Two traps in the Umami API". List endpoints return 20 items unless you pass `pageSize`, and long ranges come back in coarser units than requested. The basic-usage example now passes `pageSize`.
10
+
1
11
  ## [0.3.0] - 2026-07-24
2
12
 
3
13
  ### Fixed
data/README.md CHANGED
@@ -95,8 +95,8 @@ client = Umami::Client.new
95
95
  # Get current user info
96
96
  me = client.me
97
97
 
98
- # Get all websites
99
- websites = client.websites
98
+ # Get your websites (first page only: Umami pages lists at 20, see below)
99
+ websites = client.websites(pageSize: 200)
100
100
 
101
101
  # Get a specific website
102
102
  website = client.website("website_id")
@@ -125,6 +125,14 @@ pageviews = client.website_pageviews("website_id", {
125
125
  timezone: "America/Los_Angeles"
126
126
  })
127
127
 
128
+ # Pageviews over a range of any length, keeping daily points (see below)
129
+ year = client.website_pageviews_over_range("website_id", {
130
+ startAt: Time.utc(2025, 11, 1).to_i * 1000,
131
+ endAt: Time.utc(2026, 11, 1).to_i * 1000 - 1,
132
+ unit: "day",
133
+ timezone: "UTC"
134
+ })
135
+
128
136
  # Get metrics by type
129
137
  metrics = client.website_metrics("website_id", {
130
138
  startAt: 1656679719687,
@@ -325,6 +333,34 @@ client.reset_website("website_id")
325
333
  client.delete_website("website_id")
326
334
  ```
327
335
 
336
+ ### Two traps in the Umami API
337
+
338
+ Both fail silently: you get a normal-looking answer with data missing.
339
+
340
+ **Lists stop at 20 items.** Every list endpoint (`websites`, `my_websites`,
341
+ `team_websites`, `admin_websites`, users, teams, reports, ...) returns one page
342
+ of 20 unless you ask for more. With 32 sites, `client.websites` returns 20, and
343
+ the other 12 look like they don't exist. Pass `pageSize`, and compare the
344
+ response's `count` with the items you got:
345
+
346
+ ```ruby
347
+ response = client.websites(pageSize: 200)
348
+ response["data"].size == response["count"] # otherwise, fetch the next page
349
+ ```
350
+
351
+ **Long ranges come back in coarser units than you asked for.** If a range is
352
+ too long for the requested `unit`, the server switches to a coarser one and
353
+ says nothing. On Umami 3.x, `unit: "day"` turns into monthly points once the
354
+ range spans more than 7 calendar months, and `unit: "hour"` turns into daily
355
+ points past 30 days (`getMinimumUnit` in Umami's `src/lib/date.ts`). A year of
356
+ `unit: "day"` from `website_pageviews` is twelve monthly points, and code that
357
+ reads them as days puts a month's total on one day.
358
+
359
+ `website_pageviews_over_range` takes the same params as `website_pageviews`,
360
+ splits a long range into requests short enough for the unit (90 days for
361
+ `day`, 30 for `hour`), and joins the results. Start `startAt` at midnight in
362
+ the `timezone` you pass, so no day is split across two requests.
363
+
328
364
  ## Available Methods
329
365
 
330
366
  ### Authentication
@@ -374,6 +410,7 @@ client.delete_website("website_id")
374
410
  - `website_stats(website_id, params)` - Get statistics
375
411
  - `website_active_visitors(website_id)` - Get active visitors
376
412
  - `website_pageviews(website_id, params)` - Get pageviews
413
+ - `website_pageviews_over_range(website_id, params)` - Get pageviews over a range of any length, keeping the requested unit
377
414
  - `website_events(website_id, params)` - Get events (time-series)
378
415
  - `website_events_series(website_id, params)` - Get events series
379
416
  - `website_metrics(website_id, params)` - Get metrics
data/lib/umami/client.rb CHANGED
@@ -513,6 +513,59 @@ module Umami
513
513
  get("/api/websites/#{website_id}/pageviews", params)
514
514
  end
515
515
 
516
+ # Longest range, in days, that one pageviews request can cover and still
517
+ # get the unit it asked for. Umami silently picks a coarser unit when the
518
+ # range is too long for the one requested: 3.x answers `unit: "day"` in
519
+ # months once the range spans more than 7 calendar months, and
520
+ # `unit: "hour"` in days past 30 days (src/lib/date.ts, getMinimumUnit).
521
+ # These limits leave room for older servers too.
522
+ PAGEVIEWS_DAYS_PER_REQUEST = { "hour" => 30, "day" => 90 }.freeze
523
+
524
+ # Get pageviews and sessions over a range of any length, keeping the unit
525
+ # you asked for
526
+ #
527
+ # Takes the same params as {#website_pageviews}. A range longer than one
528
+ # request can hold for that unit (see PAGEVIEWS_DAYS_PER_REQUEST) is split
529
+ # into consecutive requests, and their series are joined. Without this,
530
+ # a year of `unit: "day"` comes back as twelve monthly points, with no
531
+ # error.
532
+ #
533
+ # Start `startAt` at midnight (or on the hour) in the `timezone` you pass,
534
+ # so no bucket is split across two requests. If one is, its counts are
535
+ # added together, which can count a visitor twice in "sessions".
536
+ #
537
+ # @param website_id [String] The website's ID
538
+ # @param params [Hash] Same as {#website_pageviews}; :startAt, :endAt and :unit are required
539
+ # @return [Hash] "pageviews" and "sessions" arrays of { "x" => bucket, "y" => count }, in order
540
+ # @example A year of daily visitors
541
+ # client.website_pageviews_over_range(website_id,
542
+ # startAt: Time.utc(2025, 11, 1).to_i * 1000,
543
+ # endAt: Time.utc(2026, 11, 1).to_i * 1000 - 1,
544
+ # unit: "day", timezone: "UTC")
545
+ def website_pageviews_over_range(website_id, params = {})
546
+ params = params.transform_keys(&:to_sym)
547
+ days = PAGEVIEWS_DAYS_PER_REQUEST[params[:unit].to_s]
548
+ return website_pageviews(website_id, params) unless days
549
+
550
+ start_at, end_at = Integer(params.fetch(:startAt)), Integer(params.fetch(:endAt))
551
+ step = days * 24 * 60 * 60 * 1000
552
+ series = Hash.new { |hash, key| hash[key] = {} }
553
+
554
+ start_at.step(end_at, step) do |from|
555
+ response = website_pageviews(website_id, params.merge(startAt: from, endAt: [from + step - 1, end_at].min))
556
+
557
+ %w[pageviews sessions].each do |key|
558
+ Array(response[key]).each do |point|
559
+ series[key][point["x"]] = series[key].fetch(point["x"], 0) + point["y"].to_i
560
+ end
561
+ end
562
+ end
563
+
564
+ %w[pageviews sessions].to_h do |key|
565
+ [key, series[key].map { |x, y| { "x" => x, "y" => y } }]
566
+ end
567
+ end
568
+
516
569
  # Get website events within a time range
517
570
  #
518
571
  # This method returns event data with optional time-series grouping.
data/lib/umami/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Umami
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: umami-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rameerez
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-07-24 00:00:00.000000000 Z
10
+ date: 2026-11-06 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: faraday