umami-ruby 0.2.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +4 -4
- data/context7.json +4 -0
- data/lib/umami/client.rb +34 -0
- data/lib/umami/errors.rb +10 -3
- data/lib/umami/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24515824a954d2c9b334b0445ef3a64f2faf79bb9fd0ec26657b702f0e170951
|
|
4
|
+
data.tar.gz: 2eec535b5aa02bf528b05a7d767d843b5781d8d970d03d7070953fc604582904
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 53784a10bf0025c49791ace32354800ac39bdb7e662234a7f81f78f50538234aec5c32fda101bd7d844bd3df5eaf8a94e6002d85037729db637b8ff818e2d55f
|
|
7
|
+
data.tar.gz: 84cb4db13dd978dfb0ac65982081fe3a1a23b8e0b3f755c7cfc0c920e53c32d16861630b5a17c43059882c43222ce62f4e6aed1ff1b599d4da2e92bd56e56375
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## [0.3.0] - 2026-07-24
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- **Username/password authentication against self-hosted instances actually works now.** Since the connection memoization was introduced, `authenticate` built (and memoized) the Faraday connection in order to POST `/api/auth/login` — before `@access_token` existed. Faraday bakes headers in at connection build time, so every request after a successful login went out without an `Authorization` header and failed with 401. The token-based flow was unaffected, which is how this survived: docs and tests exercised logins, never "login **then** call an endpoint". `authenticate` now drops the memoized connection after storing the token so the next request rebuilds it with the Bearer header, and the new `test/integration/authentication_flow_test.rb` pins the full lifecycle (login → authorized request → connection reuse without re-login).
|
|
6
|
+
- `authenticate` raises `Umami::AuthenticationError` when the login response is a 200 without a `token` field, instead of leaving a client that 401s on every call with no hint why.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- HTTP 401 and 403 responses now raise `Umami::AuthenticationError` instead of the undifferentiated `Umami::ClientError`, giving long-lived clients a re-authentication signal (expired/revoked token, insufficient permissions). To keep existing `rescue Umami::APIError` handlers working — they used to catch these as `ClientError` — `AuthenticationError` is now a subclass of `Umami::APIError` (previously it subclassed `Umami::Error` directly). Code matching `Umami::ClientError` specifically for 401/403 must switch to `Umami::AuthenticationError`.
|
|
11
|
+
|
|
1
12
|
## [0.2.0] - 2026-02-02
|
|
2
13
|
|
|
3
14
|
### Added
|
data/README.md
CHANGED
|
@@ -440,12 +440,12 @@ begin
|
|
|
440
440
|
client.website("non-existent-id")
|
|
441
441
|
rescue Umami::NotFoundError => e
|
|
442
442
|
puts "Website not found: #{e.message}"
|
|
443
|
+
rescue Umami::AuthenticationError => e
|
|
444
|
+
puts "Authentication failed: #{e.message}" # rejected login, expired/revoked token (401), forbidden (403)
|
|
443
445
|
rescue Umami::ClientError => e
|
|
444
446
|
puts "Client error: #{e.message}"
|
|
445
447
|
rescue Umami::ServerError => e
|
|
446
448
|
puts "Server error: #{e.message}"
|
|
447
|
-
rescue Umami::AuthenticationError => e
|
|
448
|
-
puts "Authentication failed: #{e.message}"
|
|
449
449
|
rescue Umami::ConfigurationError => e
|
|
450
450
|
puts "Configuration error: #{e.message}"
|
|
451
451
|
rescue Umami::APIError => e
|
|
@@ -456,10 +456,10 @@ end
|
|
|
456
456
|
**Error class hierarchy:**
|
|
457
457
|
- `Umami::Error` - Base error class
|
|
458
458
|
- `Umami::ConfigurationError` - Configuration issues
|
|
459
|
-
- `Umami::AuthenticationError` - Authentication failures
|
|
460
459
|
- `Umami::APIError` - API request failures
|
|
460
|
+
- `Umami::AuthenticationError` - Authentication failures: rejected logins, and HTTP 401/403 API responses (since 0.3.0 — an expired token on a long-lived client surfaces here so you can re-authenticate)
|
|
461
461
|
- `Umami::NotFoundError` - Resource not found (404)
|
|
462
|
-
- `Umami::ClientError` -
|
|
462
|
+
- `Umami::ClientError` - Other client errors (4xx)
|
|
463
463
|
- `Umami::ServerError` - Server errors (5xx)
|
|
464
464
|
|
|
465
465
|
## Logging
|
data/context7.json
ADDED
data/lib/umami/client.rb
CHANGED
|
@@ -72,6 +72,25 @@ module Umami
|
|
|
72
72
|
|
|
73
73
|
data = JSON.parse(response.body)
|
|
74
74
|
@access_token = data["token"]
|
|
75
|
+
|
|
76
|
+
# Umami answers a successful login with { "token": ..., "user": ... }
|
|
77
|
+
# (https://umami.is/docs/api/authentication#post-/api/auth/login).
|
|
78
|
+
# A 200 without a token would otherwise produce a client that fails
|
|
79
|
+
# every request with an opaque 401 — fail loudly at the source.
|
|
80
|
+
if @access_token.nil? || @access_token.empty?
|
|
81
|
+
raise Umami::AuthenticationError, "Authentication succeeded but the response carried no token"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# ⚠️ Load-bearing reset. The login POST above just memoized
|
|
85
|
+
# `@connection` — necessarily WITHOUT an Authorization header, since
|
|
86
|
+
# `@access_token` was still nil when the connection was built, and
|
|
87
|
+
# Faraday bakes headers into the connection at build time. Dropping
|
|
88
|
+
# the memo forces the next request to rebuild the connection, this
|
|
89
|
+
# time with the Bearer token (see #connection). Without this reset,
|
|
90
|
+
# every request after a username/password login fails with 401 —
|
|
91
|
+
# the exact bug shipped in <= 0.2.0 (why token-based auth was the
|
|
92
|
+
# only flow that worked against self-hosted instances).
|
|
93
|
+
@connection = nil
|
|
75
94
|
rescue Faraday::Error, JSON::ParserError => e
|
|
76
95
|
raise Umami::AuthenticationError, "Authentication failed: #{e.message}"
|
|
77
96
|
end
|
|
@@ -1298,6 +1317,12 @@ module Umami
|
|
|
1298
1317
|
handle_error(e)
|
|
1299
1318
|
end
|
|
1300
1319
|
|
|
1320
|
+
# Memoized so keep-alive and middleware setup are paid once, BUT the
|
|
1321
|
+
# Authorization header is baked in at build time — Faraday connections
|
|
1322
|
+
# don't re-read `@access_token` afterwards. Anything that changes the
|
|
1323
|
+
# token MUST set `@connection = nil` so the next request rebuilds with
|
|
1324
|
+
# the fresh header (see #authenticate, where forgetting exactly that
|
|
1325
|
+
# was the <= 0.2.0 username/password bug).
|
|
1301
1326
|
def connection
|
|
1302
1327
|
@connection ||= Faraday.new(url: uri_base) do |faraday|
|
|
1303
1328
|
faraday.request :json
|
|
@@ -1308,8 +1333,17 @@ module Umami
|
|
|
1308
1333
|
end
|
|
1309
1334
|
end
|
|
1310
1335
|
|
|
1336
|
+
# Order matters: Faraday::UnauthorizedError (401) and ForbiddenError
|
|
1337
|
+
# (403) are SUBCLASSES of Faraday::ClientError
|
|
1338
|
+
# (https://lostisland.github.io/faraday/#/middleware/included/raising-errors),
|
|
1339
|
+
# so they must be matched before the generic 4xx case or they'd
|
|
1340
|
+
# surface as an undifferentiated ClientError. Raising
|
|
1341
|
+
# AuthenticationError instead gives callers a re-authentication signal
|
|
1342
|
+
# (e.g. an expired or revoked token on a long-lived client).
|
|
1311
1343
|
def handle_error(error)
|
|
1312
1344
|
case error
|
|
1345
|
+
when Faraday::UnauthorizedError, Faraday::ForbiddenError
|
|
1346
|
+
raise Umami::AuthenticationError, "Authentication failed: #{error.message}"
|
|
1313
1347
|
when Faraday::ResourceNotFound
|
|
1314
1348
|
raise Umami::NotFoundError, "Resource not found: #{error.message}"
|
|
1315
1349
|
when Faraday::ClientError
|
data/lib/umami/errors.rb
CHANGED
|
@@ -5,12 +5,19 @@ module Umami
|
|
|
5
5
|
# Error raised when there's a configuration issue
|
|
6
6
|
class ConfigurationError < Error; end
|
|
7
7
|
|
|
8
|
-
# Error raised when authentication fails
|
|
9
|
-
class AuthenticationError < Error; end
|
|
10
|
-
|
|
11
8
|
# Base error class for API-related errors
|
|
12
9
|
class APIError < Error; end
|
|
13
10
|
|
|
11
|
+
# Error raised when authentication fails — a rejected login, a login
|
|
12
|
+
# response without a token, or an API request answered 401/403 (an
|
|
13
|
+
# expired or revoked token on a long-lived client, insufficient
|
|
14
|
+
# permissions). Deliberately a subclass of APIError since 0.2.1:
|
|
15
|
+
# before that, HTTP 401/403 surfaced as ClientError, so callers who
|
|
16
|
+
# `rescue Umami::APIError` were already catching them — subclassing
|
|
17
|
+
# APIError keeps those rescues working while giving auth failures
|
|
18
|
+
# their own class to match on.
|
|
19
|
+
class AuthenticationError < APIError; end
|
|
20
|
+
|
|
14
21
|
# Error raised when a resource is not found
|
|
15
22
|
class NotFoundError < APIError; end
|
|
16
23
|
|
data/lib/umami/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- rameerez
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-07-24 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: faraday
|
|
@@ -39,6 +39,7 @@ files:
|
|
|
39
39
|
- LICENSE.txt
|
|
40
40
|
- README.md
|
|
41
41
|
- Rakefile
|
|
42
|
+
- context7.json
|
|
42
43
|
- docs/Umami.html
|
|
43
44
|
- docs/Umami/APIError.html
|
|
44
45
|
- docs/Umami/AuthenticationError.html
|