umami-ruby 0.1.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 +7 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +118 -0
- data/Rakefile +4 -0
- data/docs/Umami/APIError.html +146 -0
- data/docs/Umami/AuthenticationError.html +142 -0
- data/docs/Umami/Client.html +6056 -0
- data/docs/Umami/ClientError.html +150 -0
- data/docs/Umami/Configuration.html +745 -0
- data/docs/Umami/ConfigurationError.html +142 -0
- data/docs/Umami/Error.html +138 -0
- data/docs/Umami/NotFoundError.html +150 -0
- data/docs/Umami/ServerError.html +150 -0
- data/docs/Umami.html +456 -0
- data/docs/_index.html +216 -0
- data/docs/class_list.html +51 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +497 -0
- data/docs/file.CHANGELOG.html +77 -0
- data/docs/file.LICENSE.html +70 -0
- data/docs/file.README.html +182 -0
- data/docs/file_list.html +66 -0
- data/docs/frames.html +22 -0
- data/docs/index.html +182 -0
- data/docs/js/app.js +314 -0
- data/docs/js/full_list.js +216 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +475 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/umami/client.rb +599 -0
- data/lib/umami/configuration.rb +63 -0
- data/lib/umami/errors.rb +22 -0
- data/lib/umami/version.rb +5 -0
- data/lib/umami.rb +35 -0
- data/sig/umami/ruby.rbs +6 -0
- metadata +156 -0
data/lib/umami/client.rb
ADDED
@@ -0,0 +1,599 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Umami
|
5
|
+
# The Client class provides methods to interact with the Umami API.
|
6
|
+
#
|
7
|
+
# @see https://umami.is/docs/api Umami API Documentation
|
8
|
+
class Client
|
9
|
+
attr_reader :uri_base, :request_timeout
|
10
|
+
|
11
|
+
# Initialize a new Umami API client
|
12
|
+
#
|
13
|
+
# @param options [Hash] options to create a client with.
|
14
|
+
# @option options [String] :uri_base The base URI for the Umami API
|
15
|
+
# @option options [Integer] :request_timeout Request timeout in seconds
|
16
|
+
# @option options [String] :access_token Access token for authentication
|
17
|
+
# @option options [String] :username Username for authentication (only for self-hosted instances)
|
18
|
+
# @option options [String] :password Password for authentication (only for self-hosted instances)
|
19
|
+
def initialize(options = {})
|
20
|
+
@config = options[:config] || Umami.configuration
|
21
|
+
@uri_base = options[:uri_base] || @config.uri_base
|
22
|
+
@request_timeout = options[:request_timeout] || @config.request_timeout
|
23
|
+
@access_token = options[:access_token] || @config.access_token
|
24
|
+
@username = options[:username] || @config.username
|
25
|
+
@password = options[:password] || @config.password
|
26
|
+
|
27
|
+
validate_client_options
|
28
|
+
|
29
|
+
authenticate if @access_token.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
# Check if the client is configured for Umami Cloud
|
33
|
+
#
|
34
|
+
# @return [Boolean] true if using Umami Cloud, false otherwise
|
35
|
+
def cloud?
|
36
|
+
@uri_base == Umami::Configuration::UMAMI_CLOUD_URL
|
37
|
+
end
|
38
|
+
|
39
|
+
# Check if the client is configured for a self-hosted Umami instance
|
40
|
+
#
|
41
|
+
# @return [Boolean] true if using a self-hosted instance, false otherwise
|
42
|
+
def self_hosted?
|
43
|
+
!cloud?
|
44
|
+
end
|
45
|
+
|
46
|
+
# Verify the authentication token
|
47
|
+
#
|
48
|
+
# @return [Hash] Token verification result
|
49
|
+
# @see https://umami.is/docs/api/authentication#post-/api/auth/verify
|
50
|
+
def verify_token
|
51
|
+
get("/api/auth/verify")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Authentication endpoints
|
55
|
+
|
56
|
+
# Authenticate with the Umami API using username and password
|
57
|
+
#
|
58
|
+
# This method is called automatically when initializing the client if an access token is not provided.
|
59
|
+
# It sets the @access_token instance variable upon successful authentication.
|
60
|
+
#
|
61
|
+
# @raise [Umami::AuthenticationError] if username or password is missing, or if authentication fails
|
62
|
+
# @return [void]
|
63
|
+
# @see https://umami.is/docs/api/authentication#post-/api/auth/login
|
64
|
+
def authenticate
|
65
|
+
raise Umami::AuthenticationError, "Username and password are required for authentication" if @username.nil? || @password.nil?
|
66
|
+
|
67
|
+
response = connection.post("/api/auth/login") do |req|
|
68
|
+
req.body = { username: @username, password: @password }.to_json
|
69
|
+
end
|
70
|
+
|
71
|
+
data = JSON.parse(response.body)
|
72
|
+
@access_token = data["token"]
|
73
|
+
rescue Faraday::Error, JSON::ParserError => e
|
74
|
+
raise Umami::AuthenticationError, "Authentication failed: #{e.message}"
|
75
|
+
end
|
76
|
+
|
77
|
+
# -------- Users endpoints --------
|
78
|
+
|
79
|
+
# Create a new user
|
80
|
+
#
|
81
|
+
# @param username [String] The user's username
|
82
|
+
# @param password [String] The user's password
|
83
|
+
# @param role [String] The user's role ('admin' or 'user')
|
84
|
+
# @return [Hash] Created user details
|
85
|
+
# @see https://umami.is/docs/api/users-api#post-/api/users
|
86
|
+
def create_user(username, password, role)
|
87
|
+
post("/api/users", { username: username, password: password, role: role })
|
88
|
+
end
|
89
|
+
|
90
|
+
# Get all users (admin access required)
|
91
|
+
#
|
92
|
+
# @return [Array<Hash>] List of all users
|
93
|
+
# @see https://umami.is/docs/api/users-api#get-/api/admin/users
|
94
|
+
def users
|
95
|
+
get("/api/admin/users")
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get a user by ID
|
99
|
+
#
|
100
|
+
# @param user_id [String] The user's ID
|
101
|
+
# @return [Hash] User details
|
102
|
+
# @see https://umami.is/docs/api/users-api#get-/api/users/:userid
|
103
|
+
def user(user_id)
|
104
|
+
get("/api/users/#{user_id}")
|
105
|
+
end
|
106
|
+
|
107
|
+
# Update a user
|
108
|
+
#
|
109
|
+
# @param user_id [String] The user's ID
|
110
|
+
# @param params [Hash] User parameters to update
|
111
|
+
# @option params [String] :username The user's new username
|
112
|
+
# @option params [String] :password The user's new password
|
113
|
+
# @option params [String] :role The user's new role
|
114
|
+
# @return [Hash] Updated user details
|
115
|
+
# @see https://umami.is/docs/api/users-api#post-/api/users/:userid
|
116
|
+
def update_user(user_id, params = {})
|
117
|
+
post("/api/users/#{user_id}", params)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Delete a user
|
121
|
+
#
|
122
|
+
# @param user_id [String] The user's ID
|
123
|
+
# @return [String] Confirmation message
|
124
|
+
# @see https://umami.is/docs/api/users-api#delete-/api/users/:userid
|
125
|
+
def delete_user(user_id)
|
126
|
+
delete("/api/users/#{user_id}")
|
127
|
+
end
|
128
|
+
|
129
|
+
# Get all websites for a user
|
130
|
+
#
|
131
|
+
# @param user_id [String] The user's ID
|
132
|
+
# @param params [Hash] Query parameters
|
133
|
+
# @option params [String] :query Search text
|
134
|
+
# @option params [Integer] :page Page number
|
135
|
+
# @option params [Integer] :pageSize Number of results per page
|
136
|
+
# @option params [String] :orderBy Column to order by
|
137
|
+
# @return [Array<Hash>] List of user's websites
|
138
|
+
# @see https://umami.is/docs/api/users-api#get-/api/users/:userid/websites
|
139
|
+
def user_websites(user_id, params = {})
|
140
|
+
get("/api/users/#{user_id}/websites", params)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Get all teams for a user
|
144
|
+
#
|
145
|
+
# @param user_id [String] The user's ID
|
146
|
+
# @param params [Hash] Query parameters
|
147
|
+
# @option params [String] :query Search text
|
148
|
+
# @option params [Integer] :page Page number
|
149
|
+
# @option params [Integer] :pageSize Number of results per page
|
150
|
+
# @option params [String] :orderBy Column to order by
|
151
|
+
# @return [Array<Hash>] List of user's teams
|
152
|
+
# @see https://umami.is/docs/api/users-api#get-/api/users/:userid/teams
|
153
|
+
def user_teams(user_id, params = {})
|
154
|
+
get("/api/users/#{user_id}/teams", params)
|
155
|
+
end
|
156
|
+
|
157
|
+
# -------- Teams endpoints --------
|
158
|
+
|
159
|
+
# Create a new team
|
160
|
+
#
|
161
|
+
# @param name [String] The team's name
|
162
|
+
# @return [Hash] Created team details
|
163
|
+
# @see https://umami.is/docs/api/teams-api#post-/api/teams
|
164
|
+
def create_team(name)
|
165
|
+
post("/api/teams", { name: name })
|
166
|
+
end
|
167
|
+
|
168
|
+
# Get all teams
|
169
|
+
#
|
170
|
+
# @param params [Hash] Query parameters
|
171
|
+
# @option params [String] :query Search text
|
172
|
+
# @option params [Integer] :page Page number
|
173
|
+
# @option params [Integer] :pageSize Number of results per page
|
174
|
+
# @option params [String] :orderBy Column to order by
|
175
|
+
# @return [Array<Hash>] List of teams
|
176
|
+
# @see https://umami.is/docs/api/teams-api#get-/api/teams
|
177
|
+
def teams(params = {})
|
178
|
+
get("/api/teams", params)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Join a team
|
182
|
+
#
|
183
|
+
# @param access_code [String] The team's access code
|
184
|
+
# @return [Hash] Joined team details
|
185
|
+
# @see https://umami.is/docs/api/teams-api#post-/api/teams/join
|
186
|
+
def join_team(access_code)
|
187
|
+
post("/api/teams/join", { accessCode: access_code })
|
188
|
+
end
|
189
|
+
|
190
|
+
# Get a team by ID
|
191
|
+
#
|
192
|
+
# @param team_id [String] The team's ID
|
193
|
+
# @return [Hash] Team details
|
194
|
+
# @see https://umami.is/docs/api/teams-api#get-/api/teams/:teamid
|
195
|
+
def team(team_id)
|
196
|
+
get("/api/teams/#{team_id}")
|
197
|
+
end
|
198
|
+
|
199
|
+
# Update a team
|
200
|
+
#
|
201
|
+
# @param team_id [String] The team's ID
|
202
|
+
# @param params [Hash] Team parameters to update
|
203
|
+
# @option params [String] :name The team's new name
|
204
|
+
# @option params [String] :accessCode The team's new access code
|
205
|
+
# @return [Hash] Updated team details
|
206
|
+
# @see https://umami.is/docs/api/teams-api#post-/api/teams/:teamid
|
207
|
+
def update_team(team_id, params = {})
|
208
|
+
post("/api/teams/#{team_id}", params)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Delete a team
|
212
|
+
#
|
213
|
+
# @param team_id [String] The team's ID
|
214
|
+
# @return [String] Confirmation message
|
215
|
+
# @see https://umami.is/docs/api/teams-api#delete-/api/teams/:teamid
|
216
|
+
def delete_team(team_id)
|
217
|
+
delete("/api/teams/#{team_id}")
|
218
|
+
end
|
219
|
+
|
220
|
+
# Get all users in a team
|
221
|
+
#
|
222
|
+
# @param team_id [String] The team's ID
|
223
|
+
# @param params [Hash] Query parameters
|
224
|
+
# @option params [String] :query Search text
|
225
|
+
# @option params [Integer] :page Page number
|
226
|
+
# @option params [Integer] :pageSize Number of results per page
|
227
|
+
# @option params [String] :orderBy Column to order by
|
228
|
+
# @return [Array<Hash>] List of team users
|
229
|
+
# @see https://umami.is/docs/api/teams-api#get-/api/teams/:teamid/users
|
230
|
+
def team_users(team_id, params = {})
|
231
|
+
get("/api/teams/#{team_id}/users", params)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Add a user to a team
|
235
|
+
#
|
236
|
+
# @param team_id [String] The team's ID
|
237
|
+
# @param user_id [String] The user's ID
|
238
|
+
# @param role [String] The user's role in the team
|
239
|
+
# @return [Hash] Added team user details
|
240
|
+
# @see https://umami.is/docs/api/teams-api#post-/api/teams/:teamid/users
|
241
|
+
def add_team_user(team_id, user_id, role)
|
242
|
+
post("/api/teams/#{team_id}/users", { userId: user_id, role: role })
|
243
|
+
end
|
244
|
+
|
245
|
+
# Get a user in a team
|
246
|
+
#
|
247
|
+
# @param team_id [String] The team's ID
|
248
|
+
# @param user_id [String] The user's ID
|
249
|
+
# @return [Hash] Team user details
|
250
|
+
# @see https://umami.is/docs/api/teams-api#get-/api/teams/:teamid/users/:userid
|
251
|
+
def team_user(team_id, user_id)
|
252
|
+
get("/api/teams/#{team_id}/users/#{user_id}")
|
253
|
+
end
|
254
|
+
|
255
|
+
# Update a user's role in a team
|
256
|
+
#
|
257
|
+
# @param team_id [String] The team's ID
|
258
|
+
# @param user_id [String] The user's ID
|
259
|
+
# @param role [String] The user's new role
|
260
|
+
# @return [String] Confirmation message
|
261
|
+
# @see https://umami.is/docs/api/teams-api#post-/api/teams/:teamid/users/:userid
|
262
|
+
def update_team_user(team_id, user_id, role)
|
263
|
+
post("/api/teams/#{team_id}/users/#{user_id}", { role: role })
|
264
|
+
end
|
265
|
+
|
266
|
+
# Remove a user from a team
|
267
|
+
#
|
268
|
+
# @param team_id [String] The team's ID
|
269
|
+
# @param user_id [String] The user's ID
|
270
|
+
# @return [String] Confirmation message
|
271
|
+
# @see https://umami.is/docs/api/teams-api#delete-/api/teams/:teamid/users/:userid
|
272
|
+
def delete_team_user(team_id, user_id)
|
273
|
+
delete("/api/teams/#{team_id}/users/#{user_id}")
|
274
|
+
end
|
275
|
+
|
276
|
+
# Get all websites for a team
|
277
|
+
#
|
278
|
+
# @param team_id [String] The team's ID
|
279
|
+
# @param params [Hash] Query parameters
|
280
|
+
# @option params [String] :query Search text
|
281
|
+
# @option params [Integer] :page Page number
|
282
|
+
# @option params [Integer] :pageSize Number of results per page
|
283
|
+
# @option params [String] :orderBy Column to order by
|
284
|
+
# @return [Array<Hash>] List of team websites
|
285
|
+
# @see https://umami.is/docs/api/teams-api#get-/api/teams/:teamid/websites
|
286
|
+
def team_websites(team_id, params = {})
|
287
|
+
get("/api/teams/#{team_id}/websites", params)
|
288
|
+
end
|
289
|
+
|
290
|
+
# -------- Websites endpoints --------
|
291
|
+
|
292
|
+
# Get all websites
|
293
|
+
#
|
294
|
+
# @param params [Hash] Query parameters
|
295
|
+
# @option params [String] :query Search text
|
296
|
+
# @option params [Integer] :page Page number
|
297
|
+
# @option params [Integer] :pageSize Number of results per page
|
298
|
+
# @option params [String] :orderBy Column to order by
|
299
|
+
# @return [Array<Hash>] List of websites
|
300
|
+
# @see https://umami.is/docs/api/websites-api#get-/api/websites
|
301
|
+
def websites(params = {})
|
302
|
+
get("/api/websites", params)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Create a new website
|
306
|
+
#
|
307
|
+
# @param params [Hash] Website parameters
|
308
|
+
# @option params [String] :domain The full domain of the tracked website
|
309
|
+
# @option params [String] :name The name of the website in Umami
|
310
|
+
# @option params [String] :shareId A unique string to enable a share url (optional)
|
311
|
+
# @option params [String] :teamId The ID of the team the website will be created under (optional)
|
312
|
+
# @return [Hash] Created website details
|
313
|
+
# @see https://umami.is/docs/api/websites-api#post-/api/websites
|
314
|
+
def create_website(params = {})
|
315
|
+
post("/api/websites", params)
|
316
|
+
end
|
317
|
+
|
318
|
+
# Get a website by ID
|
319
|
+
#
|
320
|
+
# @param id [String] The website's ID
|
321
|
+
# @return [Hash] Website details
|
322
|
+
# @see https://umami.is/docs/api/websites-api#get-/api/websites/:websiteid
|
323
|
+
def website(id)
|
324
|
+
get("/api/websites/#{id}")
|
325
|
+
end
|
326
|
+
|
327
|
+
# Update a website
|
328
|
+
#
|
329
|
+
# @param website_id [String] The website's ID
|
330
|
+
# @param params [Hash] Website parameters to update
|
331
|
+
# @option params [String] :name The name of the website in Umami
|
332
|
+
# @option params [String] :domain The full domain of the tracked website
|
333
|
+
# @option params [String] :shareId A unique string to enable a share url
|
334
|
+
# @return [Hash] Updated website details
|
335
|
+
# @see https://umami.is/docs/api/websites-api#post-/api/websites/:websiteid
|
336
|
+
def update_website(website_id, params = {})
|
337
|
+
post("/api/websites/#{website_id}", params)
|
338
|
+
end
|
339
|
+
|
340
|
+
# Delete a website
|
341
|
+
#
|
342
|
+
# @param website_id [String] The website's ID
|
343
|
+
# @return [String] Confirmation message
|
344
|
+
# @see https://umami.is/docs/api/websites-api#delete-/api/websites/:websiteid
|
345
|
+
def delete_website(website_id)
|
346
|
+
delete("/api/websites/#{website_id}")
|
347
|
+
end
|
348
|
+
|
349
|
+
# Reset a website's data
|
350
|
+
#
|
351
|
+
# @param website_id [String] The website's ID
|
352
|
+
# @return [String] Confirmation message
|
353
|
+
# @see https://umami.is/docs/api/websites-api#post-/api/websites/:websiteid/reset
|
354
|
+
def reset_website(website_id)
|
355
|
+
post("/api/websites/#{website_id}/reset")
|
356
|
+
end
|
357
|
+
|
358
|
+
# -------- Website stats endpoints --------
|
359
|
+
|
360
|
+
# Get website statistics
|
361
|
+
#
|
362
|
+
# @param id [String] The website's ID
|
363
|
+
# @param params [Hash] Query parameters
|
364
|
+
# @option params [Integer] :startAt Timestamp (in ms) of starting date
|
365
|
+
# @option params [Integer] :endAt Timestamp (in ms) of end date
|
366
|
+
# @option params [String] :url Name of URL
|
367
|
+
# @option params [String] :referrer Name of referrer
|
368
|
+
# @option params [String] :title Name of page title
|
369
|
+
# @option params [String] :query Name of query
|
370
|
+
# @option params [String] :event Name of event
|
371
|
+
# @option params [String] :os Name of operating system
|
372
|
+
# @option params [String] :browser Name of browser
|
373
|
+
# @option params [String] :device Name of device
|
374
|
+
# @option params [String] :country Name of country
|
375
|
+
# @option params [String] :region Name of region/state/province
|
376
|
+
# @option params [String] :city Name of city
|
377
|
+
# @return [Hash] Website statistics
|
378
|
+
# @see https://umami.is/docs/api/website-stats#get-/api/websites/:websiteid/stats
|
379
|
+
def website_stats(id, params = {})
|
380
|
+
get("/api/websites/#{id}/stats", params)
|
381
|
+
end
|
382
|
+
|
383
|
+
# Get active visitors for a website
|
384
|
+
#
|
385
|
+
# @param id [String] The website's ID
|
386
|
+
# @return [Hash] Number of active visitors
|
387
|
+
# @see https://umami.is/docs/api/website-stats#get-/api/websites/:websiteid/active
|
388
|
+
def website_active_visitors(id)
|
389
|
+
get("/api/websites/#{id}/active")
|
390
|
+
end
|
391
|
+
|
392
|
+
# Get website events
|
393
|
+
#
|
394
|
+
# @param id [String] The website's ID
|
395
|
+
# @param params [Hash] Query parameters
|
396
|
+
# @option params [Integer] :startAt Timestamp (in ms) of starting date
|
397
|
+
# @option params [Integer] :endAt Timestamp (in ms) of end date
|
398
|
+
# @option params [String] :unit Time unit (year | month | hour | day)
|
399
|
+
# @option params [String] :timezone Timezone (ex. America/Los_Angeles)
|
400
|
+
# @option params [String] :url Name of URL
|
401
|
+
# @return [Array<Hash>] Website events
|
402
|
+
# @see https://umami.is/docs/api/website-stats#get-/api/websites/:websiteid/events
|
403
|
+
def website_events(id, params = {})
|
404
|
+
get("/api/websites/#{id}/events", params)
|
405
|
+
end
|
406
|
+
|
407
|
+
# Get website pageviews
|
408
|
+
#
|
409
|
+
# @param id [String] The website's ID
|
410
|
+
# @param params [Hash] Query parameters
|
411
|
+
# @option params [Integer] :startAt Timestamp (in ms) of starting date
|
412
|
+
# @option params [Integer] :endAt Timestamp (in ms) of end date
|
413
|
+
# @option params [String] :unit Time unit (year | month | hour | day)
|
414
|
+
# @option params [String] :timezone Timezone (ex. America/Los_Angeles)
|
415
|
+
# @option params [String] :url Name of URL
|
416
|
+
# @option params [String] :referrer Name of referrer
|
417
|
+
# @option params [String] :title Name of page title
|
418
|
+
# @option params [String] :os Name of operating system
|
419
|
+
# @option params [String] :browser Name of browser
|
420
|
+
# @option params [String] :device Name of device
|
421
|
+
# @option params [String] :country Name of country
|
422
|
+
# @option params [String] :region Name of region/state/province
|
423
|
+
# @option params [String] :city Name of city
|
424
|
+
# @return [Hash] Website pageviews and sessions
|
425
|
+
# @see https://umami.is/docs/api/website-stats#get-/api/websites/:websiteid/pageviews
|
426
|
+
def website_pageviews(id, params = {})
|
427
|
+
get("/api/websites/#{id}/pageviews", params)
|
428
|
+
end
|
429
|
+
|
430
|
+
# Get website metrics
|
431
|
+
#
|
432
|
+
# @param id [String] The website's ID
|
433
|
+
# @param params [Hash] Query parameters
|
434
|
+
# @option params [Integer] :startAt Timestamp (in ms) of starting date
|
435
|
+
# @option params [Integer] :endAt Timestamp (in ms) of end date
|
436
|
+
# @option params [String] :type Metrics type (url | referrer | browser | os | device | country | event)
|
437
|
+
# @option params [String] :url Name of URL
|
438
|
+
# @option params [String] :referrer Name of referrer
|
439
|
+
# @option params [String] :title Name of page title
|
440
|
+
# @option params [String] :query Name of query
|
441
|
+
# @option params [String] :event Name of event
|
442
|
+
# @option params [String] :os Name of operating system
|
443
|
+
# @option params [String] :browser Name of browser
|
444
|
+
# @option params [String] :device Name of device
|
445
|
+
# @option params [String] :country Name of country
|
446
|
+
# @option params [String] :region Name of region/state/province
|
447
|
+
# @option params [String] :city Name of city
|
448
|
+
# @option params [String] :language Name of language
|
449
|
+
# @option params [Integer] :limit Number of results to return (default: 500)
|
450
|
+
# @return [Array<Hash>] Website metrics
|
451
|
+
# @see https://umami.is/docs/api/website-stats#get-/api/websites/:websiteid/metrics
|
452
|
+
def website_metrics(id, params = {})
|
453
|
+
get("/api/websites/#{id}/metrics", params)
|
454
|
+
end
|
455
|
+
|
456
|
+
# Get event data events
|
457
|
+
#
|
458
|
+
# @param website_id [String] The website's ID
|
459
|
+
# @param params [Hash] Query parameters
|
460
|
+
# @option params [Integer] :startAt Timestamp (in ms) of starting date
|
461
|
+
# @option params [Integer] :endAt Timestamp (in ms) of end date
|
462
|
+
# @option params [String] :event Event Name filter
|
463
|
+
# @return [Array<Hash>] Event data events
|
464
|
+
# @see https://umami.is/docs/api/event-data#get-/api/event-data/events
|
465
|
+
def event_data_events(website_id, params = {})
|
466
|
+
get("/api/event-data/events", params.merge(websiteId: website_id))
|
467
|
+
end
|
468
|
+
|
469
|
+
|
470
|
+
# -------- Event data endpoints --------
|
471
|
+
|
472
|
+
# Get event data fields
|
473
|
+
#
|
474
|
+
# @param website_id [String] The website's ID
|
475
|
+
# @param params [Hash] Query parameters
|
476
|
+
# @option params [Integer] :startAt Timestamp (in ms) of starting date
|
477
|
+
# @option params [Integer] :endAt Timestamp (in ms) of end date
|
478
|
+
# @return [Array<Hash>] Event data fields
|
479
|
+
# @see https://umami.is/docs/api/event-data#get-/api/event-data/fields
|
480
|
+
def event_data_fields(website_id, params = {})
|
481
|
+
get("/api/event-data/fields", params.merge(websiteId: website_id))
|
482
|
+
end
|
483
|
+
|
484
|
+
# Get event data stats
|
485
|
+
#
|
486
|
+
# @param website_id [String] The website's ID
|
487
|
+
# @param params [Hash] Query parameters
|
488
|
+
# @option params [Integer] :startAt Timestamp (in ms) of starting date
|
489
|
+
# @option params [Integer] :endAt Timestamp (in ms) of end date
|
490
|
+
# @return [Array<Hash>] Event data stats
|
491
|
+
# @see https://umami.is/docs/api/event-data#get-/api/event-data/stats
|
492
|
+
def event_data_stats(website_id, params = {})
|
493
|
+
get("/api/event-data/stats", params.merge(websiteId: website_id))
|
494
|
+
end
|
495
|
+
|
496
|
+
# -------- Sending stats endpoint --------
|
497
|
+
|
498
|
+
# Send an event
|
499
|
+
#
|
500
|
+
# @param payload [Hash] Event payload
|
501
|
+
# @option payload [String] :hostname Name of host
|
502
|
+
# @option payload [String] :language Language of visitor (ex. "en-US")
|
503
|
+
# @option payload [String] :referrer Referrer URL
|
504
|
+
# @option payload [String] :screen Screen resolution (ex. "1920x1080")
|
505
|
+
# @option payload [String] :title Page title
|
506
|
+
# @option payload [String] :url Page URL
|
507
|
+
# @option payload [String] :website Website ID
|
508
|
+
# @option payload [String] :name Name of the event
|
509
|
+
# @option payload [Hash] :data Additional data for the event
|
510
|
+
# @return [Hash] Response from the server
|
511
|
+
# @see https://umami.is/docs/api/sending-stats
|
512
|
+
def send_event(payload)
|
513
|
+
post("/api/send", { type: "event", payload: payload })
|
514
|
+
end
|
515
|
+
|
516
|
+
|
517
|
+
private
|
518
|
+
|
519
|
+
def authenticate
|
520
|
+
raise Umami::AuthenticationError, "Username and password are required for authentication" if @username.nil? || @password.nil?
|
521
|
+
|
522
|
+
response = connection.post("/api/auth/login") do |req|
|
523
|
+
req.body = { username: @username, password: @password }.to_json
|
524
|
+
end
|
525
|
+
|
526
|
+
data = JSON.parse(response.body)
|
527
|
+
@access_token = data["token"]
|
528
|
+
rescue Faraday::Error, JSON::ParserError => e
|
529
|
+
raise Umami::AuthenticationError, "Authentication failed: #{e.message}"
|
530
|
+
end
|
531
|
+
|
532
|
+
def get(path, params = {})
|
533
|
+
response = connection.get(path, params)
|
534
|
+
JSON.parse(response.body)
|
535
|
+
rescue Faraday::Error => e
|
536
|
+
handle_error(e)
|
537
|
+
end
|
538
|
+
|
539
|
+
def post(path, body = {})
|
540
|
+
response = connection.post(path, body.to_json)
|
541
|
+
JSON.parse(response.body)
|
542
|
+
rescue Faraday::Error => e
|
543
|
+
handle_error(e)
|
544
|
+
end
|
545
|
+
|
546
|
+
def delete(path)
|
547
|
+
response = connection.delete(path)
|
548
|
+
response.body == "ok" ? "ok" : JSON.parse(response.body)
|
549
|
+
rescue Faraday::Error => e
|
550
|
+
handle_error(e)
|
551
|
+
end
|
552
|
+
|
553
|
+
def connection
|
554
|
+
@connection ||= Faraday.new(url: uri_base) do |faraday|
|
555
|
+
faraday.request :json
|
556
|
+
faraday.response :raise_error
|
557
|
+
faraday.adapter Faraday.default_adapter
|
558
|
+
faraday.headers["Authorization"] = "Bearer #{@access_token}" if @access_token
|
559
|
+
faraday.options.timeout = request_timeout
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
def handle_error(error)
|
564
|
+
case error
|
565
|
+
when Faraday::ResourceNotFound
|
566
|
+
raise Umami::NotFoundError, "Resource not found: #{error.message}"
|
567
|
+
when Faraday::ClientError
|
568
|
+
raise Umami::ClientError, "Client error: #{error.message}"
|
569
|
+
when Faraday::ServerError
|
570
|
+
raise Umami::ServerError, "Server error: #{error.message}"
|
571
|
+
else
|
572
|
+
raise Umami::APIError, "API request failed: #{error.message}"
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
def validate_client_options
|
577
|
+
if @access_token && @uri_base.nil?
|
578
|
+
@uri_base = Umami::Configuration::UMAMI_CLOUD_URL
|
579
|
+
Umami.logger.info "No URI base provided with access token. Using Umami Cloud URL: #{@uri_base}"
|
580
|
+
end
|
581
|
+
|
582
|
+
raise Umami::ConfigurationError, "URI base is required for self-hosted instances" if @uri_base.nil? && !@access_token
|
583
|
+
|
584
|
+
if cloud? && (@username || @password)
|
585
|
+
raise Umami::ConfigurationError, "Username/password authentication is not supported for Umami Cloud"
|
586
|
+
end
|
587
|
+
|
588
|
+
if @access_token && (@username || @password)
|
589
|
+
Umami.logger.warn "Both access token and credentials provided. Access token will be used."
|
590
|
+
@username = nil
|
591
|
+
@password = nil
|
592
|
+
end
|
593
|
+
|
594
|
+
if @uri_base != Umami::Configuration::UMAMI_CLOUD_URL && !@access_token && !@username && !@password
|
595
|
+
raise Umami::ConfigurationError, "Authentication is required for self-hosted instances"
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|
599
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Umami
|
2
|
+
class Configuration
|
3
|
+
UMAMI_CLOUD_URL = "https://api.umami.is".freeze
|
4
|
+
|
5
|
+
attr_reader :uri_base, :request_timeout, :access_token, :username, :password
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@uri_base = nil
|
9
|
+
@request_timeout = 120
|
10
|
+
@access_token = nil
|
11
|
+
@username = nil
|
12
|
+
@password = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def uri_base=(url)
|
16
|
+
@uri_base = url&.chomp('/')
|
17
|
+
validate_configuration
|
18
|
+
end
|
19
|
+
|
20
|
+
def access_token=(token)
|
21
|
+
@access_token = token
|
22
|
+
@username = nil
|
23
|
+
@password = nil
|
24
|
+
validate_configuration
|
25
|
+
end
|
26
|
+
|
27
|
+
def credentials=(creds)
|
28
|
+
raise Umami::ConfigurationError, "Both username and password are required" unless creds[:username] && creds[:password]
|
29
|
+
|
30
|
+
@username = creds[:username]
|
31
|
+
@password = creds[:password]
|
32
|
+
@access_token = nil
|
33
|
+
validate_configuration
|
34
|
+
end
|
35
|
+
|
36
|
+
def cloud?
|
37
|
+
@access_token && @uri_base.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def validate_configuration
|
43
|
+
if cloud?
|
44
|
+
@uri_base = UMAMI_CLOUD_URL
|
45
|
+
Umami.logger.info "Using Umami Cloud (#{UMAMI_CLOUD_URL})"
|
46
|
+
end
|
47
|
+
|
48
|
+
if @uri_base == UMAMI_CLOUD_URL && (@username || @password)
|
49
|
+
raise Umami::ConfigurationError, "Username/password authentication is not supported for Umami Cloud"
|
50
|
+
end
|
51
|
+
|
52
|
+
if @access_token && (@username || @password)
|
53
|
+
Umami.logger.warn "Both access token and credentials provided. Access token will be used."
|
54
|
+
@username = nil
|
55
|
+
@password = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
if @uri_base && @uri_base != UMAMI_CLOUD_URL && !@access_token && !@username && !@password
|
59
|
+
raise Umami::ConfigurationError, "Authentication is required for self-hosted instances"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/umami/errors.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Umami
|
2
|
+
# Base error class for Umami-related errors
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
# Error raised when there's a configuration issue
|
6
|
+
class ConfigurationError < Error; end
|
7
|
+
|
8
|
+
# Error raised when authentication fails
|
9
|
+
class AuthenticationError < Error; end
|
10
|
+
|
11
|
+
# Base error class for API-related errors
|
12
|
+
class APIError < Error; end
|
13
|
+
|
14
|
+
# Error raised when a resource is not found
|
15
|
+
class NotFoundError < APIError; end
|
16
|
+
|
17
|
+
# Error raised for client-side errors (4xx status codes)
|
18
|
+
class ClientError < APIError; end
|
19
|
+
|
20
|
+
# Error raised for server-side errors (5xx status codes)
|
21
|
+
class ServerError < APIError; end
|
22
|
+
end
|