fizzy-api-client 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/CHANGELOG.md +45 -0
- data/LICENSE.txt +21 -0
- data/README.md +469 -0
- data/examples/README.md +102 -0
- data/examples/demo.rb +636 -0
- data/examples/sydney.jpg +0 -0
- data/fizzy-api-client.gemspec +31 -0
- data/lib/fizzy_api_client/client.rb +125 -0
- data/lib/fizzy_api_client/colors.rb +91 -0
- data/lib/fizzy_api_client/configuration.rb +34 -0
- data/lib/fizzy_api_client/connection.rb +215 -0
- data/lib/fizzy_api_client/error.rb +59 -0
- data/lib/fizzy_api_client/multipart.rb +59 -0
- data/lib/fizzy_api_client/pagination.rb +9 -0
- data/lib/fizzy_api_client/request.rb +50 -0
- data/lib/fizzy_api_client/resources/boards.rb +51 -0
- data/lib/fizzy_api_client/resources/cards.rb +212 -0
- data/lib/fizzy_api_client/resources/columns.rb +78 -0
- data/lib/fizzy_api_client/resources/comments.rb +45 -0
- data/lib/fizzy_api_client/resources/direct_uploads.rb +88 -0
- data/lib/fizzy_api_client/resources/identity.rb +12 -0
- data/lib/fizzy_api_client/resources/notifications.rb +29 -0
- data/lib/fizzy_api_client/resources/reactions.rb +31 -0
- data/lib/fizzy_api_client/resources/steps.rb +51 -0
- data/lib/fizzy_api_client/resources/tags.rb +11 -0
- data/lib/fizzy_api_client/resources/users.rb +75 -0
- data/lib/fizzy_api_client/response.rb +61 -0
- data/lib/fizzy_api_client/version.rb +5 -0
- data/lib/fizzy_api_client.rb +51 -0
- metadata +117 -0
data/examples/demo.rb
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Comprehensive demo script to test every feature of fizzy-api-client
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# FIZZY_API_TOKEN=xxx FIZZY_ACCOUNT=yyy ruby examples/demo.rb
|
|
8
|
+
# Or for self-hosted:
|
|
9
|
+
# FIZZY_BASE_URL=https://fizzy.mycompany.com FIZZY_API_TOKEN=xxx ruby examples/demo.rb
|
|
10
|
+
#
|
|
11
|
+
# Options:
|
|
12
|
+
# SKIP_DESTRUCTIVE=1 - Skip operations that modify existing data (deactivate_user, etc.)
|
|
13
|
+
# VERBOSE=1 - Show full response data
|
|
14
|
+
|
|
15
|
+
require_relative "../lib/fizzy_api_client"
|
|
16
|
+
require "tempfile"
|
|
17
|
+
|
|
18
|
+
class Demo
|
|
19
|
+
def initialize
|
|
20
|
+
@passed = 0
|
|
21
|
+
@failed = 0
|
|
22
|
+
@skipped = 0
|
|
23
|
+
@verbose = ENV["VERBOSE"] == "1"
|
|
24
|
+
@skip_destructive = ENV["SKIP_DESTRUCTIVE"] == "1"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def run
|
|
28
|
+
print_banner
|
|
29
|
+
check_configuration
|
|
30
|
+
|
|
31
|
+
@client = FizzyApiClient::Client.new
|
|
32
|
+
|
|
33
|
+
# Run all tests
|
|
34
|
+
test_identity
|
|
35
|
+
test_boards
|
|
36
|
+
test_columns
|
|
37
|
+
test_cards
|
|
38
|
+
test_steps
|
|
39
|
+
test_comments
|
|
40
|
+
test_reactions
|
|
41
|
+
test_tags
|
|
42
|
+
test_users
|
|
43
|
+
test_notifications
|
|
44
|
+
test_direct_uploads
|
|
45
|
+
test_pagination
|
|
46
|
+
test_error_handling
|
|
47
|
+
|
|
48
|
+
cleanup
|
|
49
|
+
print_summary
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def print_banner
|
|
55
|
+
puts "=" * 60
|
|
56
|
+
puts "Fizzy API Client - Comprehensive Demo"
|
|
57
|
+
puts "=" * 60
|
|
58
|
+
puts "Version: #{FizzyApiClient::VERSION}"
|
|
59
|
+
puts "Base URL: #{ENV['FIZZY_BASE_URL'] || 'https://app.fizzy.do'}"
|
|
60
|
+
puts "Skip destructive: #{@skip_destructive}"
|
|
61
|
+
puts "=" * 60
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def check_configuration
|
|
65
|
+
unless ENV["FIZZY_API_TOKEN"]
|
|
66
|
+
puts "\nError: FIZZY_API_TOKEN environment variable required"
|
|
67
|
+
puts "Usage: FIZZY_API_TOKEN=xxx FIZZY_ACCOUNT=yyy ruby examples/demo.rb"
|
|
68
|
+
exit 1
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def print_header(text)
|
|
73
|
+
puts "\n#{text}"
|
|
74
|
+
puts "-" * text.length
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def pass(text, data = nil)
|
|
78
|
+
@passed += 1
|
|
79
|
+
puts " ✓ #{text}"
|
|
80
|
+
puts " #{data.inspect}" if @verbose && data
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def fail(text, error)
|
|
84
|
+
@failed += 1
|
|
85
|
+
puts " ✗ #{text}"
|
|
86
|
+
puts " Error: #{error.message}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def skip(text, reason = nil)
|
|
90
|
+
@skipped += 1
|
|
91
|
+
puts " ○ #{text} (skipped#{reason ? ": #{reason}" : ''})"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test(description)
|
|
95
|
+
result = yield
|
|
96
|
+
pass(description, result)
|
|
97
|
+
result
|
|
98
|
+
rescue => e
|
|
99
|
+
fail(description, e)
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# ============================================================
|
|
104
|
+
# IDENTITY
|
|
105
|
+
# ============================================================
|
|
106
|
+
def test_identity
|
|
107
|
+
print_header "1. IDENTITY"
|
|
108
|
+
|
|
109
|
+
@identity = test("Get identity") { @client.identity }
|
|
110
|
+
|
|
111
|
+
if @identity && @identity["accounts"]&.any?
|
|
112
|
+
accounts = @identity["accounts"].map { |a| a["slug"] }
|
|
113
|
+
puts " Accounts: #{accounts.join(', ')}"
|
|
114
|
+
|
|
115
|
+
unless @client.account_slug
|
|
116
|
+
first_account = @identity["accounts"].first["slug"]
|
|
117
|
+
@client.account_slug = first_account
|
|
118
|
+
puts " Using account: #{@client.account_slug}"
|
|
119
|
+
end
|
|
120
|
+
else
|
|
121
|
+
puts " No accounts found - cannot continue"
|
|
122
|
+
exit 1
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# ============================================================
|
|
127
|
+
# BOARDS
|
|
128
|
+
# ============================================================
|
|
129
|
+
def test_boards
|
|
130
|
+
print_header "2. BOARDS"
|
|
131
|
+
|
|
132
|
+
# List boards
|
|
133
|
+
@boards = test("List boards") { @client.boards }
|
|
134
|
+
return unless @boards&.any?
|
|
135
|
+
|
|
136
|
+
@board = @boards.first
|
|
137
|
+
puts " Using board: #{@board['name']} (#{@board['id']})"
|
|
138
|
+
|
|
139
|
+
# Get single board
|
|
140
|
+
test("Get single board") { @client.board(@board["id"]) }
|
|
141
|
+
|
|
142
|
+
# Create board
|
|
143
|
+
@test_board = test("Create board") do
|
|
144
|
+
@client.create_board(name: "API Demo Test Board")
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
if @test_board
|
|
148
|
+
# Update board
|
|
149
|
+
test("Update board name") do
|
|
150
|
+
@client.update_board(@test_board["id"], name: "API Demo Test Board (Updated)")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
test("Update board settings") do
|
|
154
|
+
@client.update_board(@test_board["id"], auto_postpone_period: 14)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# ============================================================
|
|
160
|
+
# COLUMNS
|
|
161
|
+
# ============================================================
|
|
162
|
+
def test_columns
|
|
163
|
+
print_header "3. COLUMNS"
|
|
164
|
+
|
|
165
|
+
return skip("Columns tests", "no test board") unless @test_board
|
|
166
|
+
|
|
167
|
+
# List columns
|
|
168
|
+
columns = test("List columns") { @client.columns(@test_board["id"]) }
|
|
169
|
+
|
|
170
|
+
# Create column with named color (NEW FEATURE!)
|
|
171
|
+
@test_column = test("Create column with named color :lime") do
|
|
172
|
+
@client.create_column(board_id: @test_board["id"], name: "In Progress", color: :lime)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
if @test_column
|
|
176
|
+
# Get single column
|
|
177
|
+
test("Get single column") do
|
|
178
|
+
@client.column(@test_board["id"], @test_column["id"])
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Update column with named color
|
|
182
|
+
test("Update column with named color :purple") do
|
|
183
|
+
@client.update_column(@test_board["id"], @test_column["id"], color: :purple)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Update column name
|
|
187
|
+
test("Update column name") do
|
|
188
|
+
@client.update_column(@test_board["id"], @test_column["id"], name: "Review")
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Test all named colors
|
|
192
|
+
test("Create column with :blue (default)") do
|
|
193
|
+
@client.create_column(board_id: @test_board["id"], name: "Blue Column", color: :blue)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
test("Create column with :pink") do
|
|
197
|
+
@client.create_column(board_id: @test_board["id"], name: "Pink Column", color: :pink)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
test("Create column with CSS variable (backwards compat)") do
|
|
201
|
+
@client.create_column(board_id: @test_board["id"], name: "Yellow Column", color: "var(--color-card-3)")
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# ============================================================
|
|
207
|
+
# CARDS
|
|
208
|
+
# ============================================================
|
|
209
|
+
def test_cards
|
|
210
|
+
print_header "4. CARDS"
|
|
211
|
+
|
|
212
|
+
return skip("Cards tests", "no board") unless @board
|
|
213
|
+
|
|
214
|
+
# List cards
|
|
215
|
+
test("List cards") { @client.cards }
|
|
216
|
+
|
|
217
|
+
test("List cards with board filter") do
|
|
218
|
+
@client.cards(board_ids: [@board["id"]])
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Create card
|
|
222
|
+
@test_card = test("Create card") do
|
|
223
|
+
@client.create_card(board_id: @board["id"], title: "API Demo Test Card")
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
return unless @test_card
|
|
227
|
+
|
|
228
|
+
@card_number = @test_card["number"]
|
|
229
|
+
puts " Created card ##{@card_number}"
|
|
230
|
+
|
|
231
|
+
# Get single card
|
|
232
|
+
test("Get single card") { @client.card(@card_number) }
|
|
233
|
+
|
|
234
|
+
# Update card
|
|
235
|
+
test("Update card title") do
|
|
236
|
+
@client.update_card(@card_number, title: "API Demo Test Card (Updated)")
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
test("Update card description") do
|
|
240
|
+
@client.update_card(@card_number, description: "This card was created by the API demo script.")
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Card state changes
|
|
244
|
+
test("Close card") { @client.close_card(@card_number) }
|
|
245
|
+
test("Reopen card") { @client.reopen_card(@card_number) }
|
|
246
|
+
test("Postpone card (not_now)") { @client.postpone_card(@card_number) }
|
|
247
|
+
|
|
248
|
+
# Triage - need a column on the same board as the card
|
|
249
|
+
# Create a column on @board for triage testing
|
|
250
|
+
triage_column = test("Create column for triage test") do
|
|
251
|
+
@client.create_column(board_id: @board["id"], name: "Triage Test Column", color: :aqua)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
if triage_column
|
|
255
|
+
test("Triage card to column") do
|
|
256
|
+
@client.triage_card(@card_number, column_id: triage_column["id"])
|
|
257
|
+
end
|
|
258
|
+
test("Untriage card") { @client.untriage_card(@card_number) }
|
|
259
|
+
# Clean up triage column
|
|
260
|
+
@client.delete_column(@board["id"], triage_column["id"]) rescue nil
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Watch/unwatch
|
|
264
|
+
test("Watch card") { @client.watch_card(@card_number) }
|
|
265
|
+
test("Unwatch card") { @client.unwatch_card(@card_number) }
|
|
266
|
+
|
|
267
|
+
# Toggle tag - create a tag by toggling with a new title
|
|
268
|
+
test("Toggle tag on card (creates tag if needed)") do
|
|
269
|
+
@client.toggle_tag(@card_number, tag_title: "demo-test-tag")
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Toggle it off
|
|
273
|
+
test("Toggle tag off card") do
|
|
274
|
+
@client.toggle_tag(@card_number, tag_title: "demo-test-tag")
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Toggle assignment - use current user from identity
|
|
278
|
+
if @identity && @identity["user"]
|
|
279
|
+
current_user_id = @identity["user"]["id"]
|
|
280
|
+
test("Toggle assignment on card") do
|
|
281
|
+
@client.toggle_assignment(@card_number, assignee_id: current_user_id)
|
|
282
|
+
end
|
|
283
|
+
test("Toggle assignment off card") do
|
|
284
|
+
@client.toggle_assignment(@card_number, assignee_id: current_user_id)
|
|
285
|
+
end
|
|
286
|
+
else
|
|
287
|
+
skip("Toggle assignment on card", "no current user in identity")
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Card with image upload using sydney.jpg
|
|
291
|
+
image_path = File.expand_path("sydney.jpg", __dir__)
|
|
292
|
+
if File.exist?(image_path)
|
|
293
|
+
@image_card = test("Create card with background image (sydney.jpg)") do
|
|
294
|
+
card = @client.create_card(board_id: @board["id"], title: "Card with Sydney Image", image: image_path)
|
|
295
|
+
@cards_to_cleanup ||= []
|
|
296
|
+
@cards_to_cleanup << card["number"]
|
|
297
|
+
card
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Verify card has image_url
|
|
301
|
+
if @image_card && @image_card["image_url"]
|
|
302
|
+
puts " Image URL: #{@image_card['image_url'][0..60]}..."
|
|
303
|
+
end
|
|
304
|
+
else
|
|
305
|
+
skip("Create card with background image", "sydney.jpg not found")
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Golden ticket (gild/ungild)
|
|
309
|
+
test("Make card a golden ticket") { @client.gild_card(@card_number) }
|
|
310
|
+
|
|
311
|
+
# Verify card is now golden
|
|
312
|
+
gilded_card = test("Verify card is golden") do
|
|
313
|
+
card = @client.card(@card_number)
|
|
314
|
+
raise "Card is not golden!" unless card["golden"]
|
|
315
|
+
card
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
test("Remove golden ticket status") { @client.ungild_card(@card_number) }
|
|
319
|
+
|
|
320
|
+
# Verify card is no longer golden
|
|
321
|
+
test("Verify card is not golden") do
|
|
322
|
+
card = @client.card(@card_number)
|
|
323
|
+
raise "Card is still golden!" if card["golden"]
|
|
324
|
+
card
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# ============================================================
|
|
329
|
+
# STEPS
|
|
330
|
+
# ============================================================
|
|
331
|
+
def test_steps
|
|
332
|
+
print_header "5. STEPS"
|
|
333
|
+
|
|
334
|
+
return skip("Steps tests", "no test card") unless @card_number
|
|
335
|
+
|
|
336
|
+
# Create step
|
|
337
|
+
@test_step = test("Create step") do
|
|
338
|
+
@client.create_step(@card_number, content: "Demo step from API client")
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
return unless @test_step
|
|
342
|
+
|
|
343
|
+
step_id = @test_step["id"]
|
|
344
|
+
|
|
345
|
+
# Get single step
|
|
346
|
+
test("Get single step") { @client.step(@card_number, step_id) }
|
|
347
|
+
|
|
348
|
+
# Update step
|
|
349
|
+
test("Update step content") do
|
|
350
|
+
@client.update_step(@card_number, step_id, content: "Updated step content")
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Complete/incomplete step
|
|
354
|
+
test("Complete step") { @client.complete_step(@card_number, step_id) }
|
|
355
|
+
test("Incomplete step") { @client.incomplete_step(@card_number, step_id) }
|
|
356
|
+
|
|
357
|
+
# Create another step for deletion test
|
|
358
|
+
step2 = test("Create step for deletion") do
|
|
359
|
+
@client.create_step(@card_number, content: "Step to delete")
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
if step2
|
|
363
|
+
test("Delete step") { @client.delete_step(@card_number, step2["id"]) }
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# ============================================================
|
|
368
|
+
# COMMENTS
|
|
369
|
+
# ============================================================
|
|
370
|
+
def test_comments
|
|
371
|
+
print_header "6. COMMENTS"
|
|
372
|
+
|
|
373
|
+
return skip("Comments tests", "no test card") unless @card_number
|
|
374
|
+
|
|
375
|
+
# Create comment
|
|
376
|
+
@test_comment = test("Create comment") do
|
|
377
|
+
@client.create_comment(@card_number, body: "This is a test comment from the API demo.")
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
return unless @test_comment
|
|
381
|
+
|
|
382
|
+
comment_id = @test_comment["id"]
|
|
383
|
+
|
|
384
|
+
# List comments
|
|
385
|
+
test("List comments") { @client.comments(@card_number) }
|
|
386
|
+
|
|
387
|
+
# Get single comment
|
|
388
|
+
test("Get single comment") { @client.comment(@card_number, comment_id) }
|
|
389
|
+
|
|
390
|
+
# Update comment
|
|
391
|
+
test("Update comment") do
|
|
392
|
+
@client.update_comment(@card_number, comment_id, body: "Updated comment body")
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Create another comment for deletion test
|
|
396
|
+
comment2 = test("Create comment for deletion") do
|
|
397
|
+
@client.create_comment(@card_number, body: "Comment to delete")
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
if comment2
|
|
401
|
+
test("Delete comment") { @client.delete_comment(@card_number, comment2["id"]) }
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# ============================================================
|
|
406
|
+
# REACTIONS
|
|
407
|
+
# ============================================================
|
|
408
|
+
def test_reactions
|
|
409
|
+
print_header "7. REACTIONS"
|
|
410
|
+
|
|
411
|
+
return skip("Reactions tests", "no test comment") unless @test_comment
|
|
412
|
+
|
|
413
|
+
comment_id = @test_comment["id"]
|
|
414
|
+
|
|
415
|
+
# Add reaction
|
|
416
|
+
@test_reaction = test("Add reaction") do
|
|
417
|
+
@client.add_reaction(@card_number, comment_id, content: ":+1:")
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
return unless @test_reaction
|
|
421
|
+
|
|
422
|
+
# List reactions
|
|
423
|
+
test("List reactions") { @client.reactions(@card_number, comment_id) }
|
|
424
|
+
|
|
425
|
+
# Remove reaction
|
|
426
|
+
test("Remove reaction") do
|
|
427
|
+
@client.remove_reaction(@card_number, comment_id, @test_reaction["id"])
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# ============================================================
|
|
432
|
+
# TAGS
|
|
433
|
+
# ============================================================
|
|
434
|
+
def test_tags
|
|
435
|
+
print_header "8. TAGS"
|
|
436
|
+
|
|
437
|
+
# List tags
|
|
438
|
+
test("List tags") { @client.tags }
|
|
439
|
+
|
|
440
|
+
# List tags with pagination
|
|
441
|
+
test("List tags page 1") { @client.tags(page: 1) }
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# ============================================================
|
|
445
|
+
# USERS
|
|
446
|
+
# ============================================================
|
|
447
|
+
def test_users
|
|
448
|
+
print_header "9. USERS"
|
|
449
|
+
|
|
450
|
+
# List users
|
|
451
|
+
@users = test("List users") { @client.users }
|
|
452
|
+
|
|
453
|
+
return unless @users&.any?
|
|
454
|
+
|
|
455
|
+
user = @users.first
|
|
456
|
+
puts " First user: #{user['name']} (#{user['id']})"
|
|
457
|
+
|
|
458
|
+
# Get single user
|
|
459
|
+
test("Get single user") { @client.user(user["id"]) }
|
|
460
|
+
|
|
461
|
+
# Update user (skip in destructive mode to avoid changing real data)
|
|
462
|
+
if @skip_destructive
|
|
463
|
+
skip("Update user", "destructive operation")
|
|
464
|
+
skip("Update user with avatar", "destructive operation")
|
|
465
|
+
skip("Deactivate user", "destructive operation")
|
|
466
|
+
else
|
|
467
|
+
# These would modify real user data, so we skip them by default
|
|
468
|
+
skip("Update user", "would modify real data - use SKIP_DESTRUCTIVE=0 to enable")
|
|
469
|
+
skip("Update user with avatar", "would modify real data")
|
|
470
|
+
skip("Deactivate user", "would modify real data")
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# ============================================================
|
|
475
|
+
# NOTIFICATIONS
|
|
476
|
+
# ============================================================
|
|
477
|
+
def test_notifications
|
|
478
|
+
print_header "10. NOTIFICATIONS"
|
|
479
|
+
|
|
480
|
+
# List notifications
|
|
481
|
+
notifications = test("List notifications") { @client.notifications }
|
|
482
|
+
|
|
483
|
+
if notifications&.any?
|
|
484
|
+
notification = notifications.first
|
|
485
|
+
notification_id = notification["id"]
|
|
486
|
+
|
|
487
|
+
test("Mark notification read") { @client.mark_notification_read(notification_id) }
|
|
488
|
+
test("Mark notification unread") { @client.mark_notification_unread(notification_id) }
|
|
489
|
+
else
|
|
490
|
+
skip("Mark notification read", "no notifications")
|
|
491
|
+
skip("Mark notification unread", "no notifications")
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
# Mark all read (always works, even with no notifications)
|
|
495
|
+
test("Mark all notifications read") { @client.mark_all_notifications_read }
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# ============================================================
|
|
499
|
+
# DIRECT UPLOADS
|
|
500
|
+
# ============================================================
|
|
501
|
+
def test_direct_uploads
|
|
502
|
+
print_header "11. DIRECT UPLOADS"
|
|
503
|
+
|
|
504
|
+
test("Create direct upload") do
|
|
505
|
+
content = "test file content"
|
|
506
|
+
checksum = Base64.strict_encode64(Digest::MD5.digest(content))
|
|
507
|
+
|
|
508
|
+
@client.create_direct_upload(
|
|
509
|
+
filename: "test.txt",
|
|
510
|
+
byte_size: content.bytesize,
|
|
511
|
+
checksum: checksum,
|
|
512
|
+
content_type: "text/plain"
|
|
513
|
+
)
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
test("Upload file helper") do
|
|
517
|
+
Tempfile.create(["test", ".txt"]) do |f|
|
|
518
|
+
f.write("Hello from Fizzy API Client demo!")
|
|
519
|
+
f.rewind
|
|
520
|
+
@client.upload_file(f.path)
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# ============================================================
|
|
526
|
+
# PAGINATION
|
|
527
|
+
# ============================================================
|
|
528
|
+
def test_pagination
|
|
529
|
+
print_header "12. PAGINATION"
|
|
530
|
+
|
|
531
|
+
test("List boards with auto_paginate") do
|
|
532
|
+
@client.boards(auto_paginate: true)
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
test("List cards with auto_paginate") do
|
|
536
|
+
@client.cards(auto_paginate: true)
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
test("List tags with auto_paginate") do
|
|
540
|
+
@client.tags(auto_paginate: true)
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
test("List users with auto_paginate") do
|
|
544
|
+
@client.users(auto_paginate: true)
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# ============================================================
|
|
549
|
+
# ERROR HANDLING
|
|
550
|
+
# ============================================================
|
|
551
|
+
def test_error_handling
|
|
552
|
+
print_header "13. ERROR HANDLING"
|
|
553
|
+
|
|
554
|
+
test("NotFoundError for invalid card") do
|
|
555
|
+
begin
|
|
556
|
+
@client.card(999_999_999)
|
|
557
|
+
raise "Should have raised NotFoundError"
|
|
558
|
+
rescue FizzyApiClient::NotFoundError => e
|
|
559
|
+
"Correctly raised NotFoundError: #{e.message}"
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
test("NotFoundError for invalid board") do
|
|
564
|
+
begin
|
|
565
|
+
@client.board("invalid_board_id_xyz")
|
|
566
|
+
raise "Should have raised NotFoundError"
|
|
567
|
+
rescue FizzyApiClient::NotFoundError => e
|
|
568
|
+
"Correctly raised NotFoundError: #{e.message}"
|
|
569
|
+
end
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
test("ValidationError for invalid color") do
|
|
573
|
+
begin
|
|
574
|
+
FizzyApiClient::Colors.resolve(:invalid_color)
|
|
575
|
+
raise "Should have raised ArgumentError"
|
|
576
|
+
rescue ArgumentError => e
|
|
577
|
+
"Correctly raised ArgumentError: #{e.message}"
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
# ============================================================
|
|
583
|
+
# CLEANUP
|
|
584
|
+
# ============================================================
|
|
585
|
+
def cleanup
|
|
586
|
+
print_header "14. CLEANUP"
|
|
587
|
+
|
|
588
|
+
# Delete test card
|
|
589
|
+
if @card_number
|
|
590
|
+
test("Delete test card ##{@card_number}") do
|
|
591
|
+
@client.delete_card(@card_number)
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
# Delete any additional cards created during testing
|
|
596
|
+
(@cards_to_cleanup || []).each do |card_number|
|
|
597
|
+
test("Delete card ##{card_number}") do
|
|
598
|
+
@client.delete_card(card_number)
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
# Delete test column
|
|
603
|
+
if @test_column && @test_board
|
|
604
|
+
test("Delete test column") do
|
|
605
|
+
@client.delete_column(@test_board["id"], @test_column["id"])
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
# Delete test board
|
|
610
|
+
if @test_board
|
|
611
|
+
test("Delete test board") do
|
|
612
|
+
@client.delete_board(@test_board["id"])
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def print_summary
|
|
618
|
+
puts "\n" + "=" * 60
|
|
619
|
+
puts "DEMO SUMMARY"
|
|
620
|
+
puts "=" * 60
|
|
621
|
+
puts " Passed: #{@passed}"
|
|
622
|
+
puts " Failed: #{@failed}"
|
|
623
|
+
puts " Skipped: #{@skipped}"
|
|
624
|
+
puts "=" * 60
|
|
625
|
+
|
|
626
|
+
if @failed > 0
|
|
627
|
+
puts "Some tests failed!"
|
|
628
|
+
exit 1
|
|
629
|
+
else
|
|
630
|
+
puts "All tests passed!"
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
# Run the demo
|
|
636
|
+
Demo.new.run
|
data/examples/sydney.jpg
ADDED
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/fizzy_api_client/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "fizzy-api-client"
|
|
7
|
+
spec.version = FizzyApiClient::VERSION
|
|
8
|
+
spec.authors = ["Fizzy Team"]
|
|
9
|
+
spec.email = ["support@fizzy.do"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Ruby client for the Fizzy API"
|
|
12
|
+
spec.description = "A clean, idiomatic Ruby interface to the Fizzy API with minimal dependencies"
|
|
13
|
+
spec.homepage = "https://github.com/robzolkos/fizzy-api-client"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
20
|
+
|
|
21
|
+
spec.files = Dir.glob("{lib,examples}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) } +
|
|
22
|
+
%w[LICENSE.txt README.md CHANGELOG.md fizzy-api-client.gemspec]
|
|
23
|
+
spec.require_paths = ["lib"]
|
|
24
|
+
|
|
25
|
+
# Required for Ruby 3.4+ (moved from stdlib to bundled gems)
|
|
26
|
+
spec.add_dependency "base64"
|
|
27
|
+
|
|
28
|
+
# Required for Ruby 3.5+ (will be moved from stdlib to bundled gems)
|
|
29
|
+
spec.add_dependency "ostruct"
|
|
30
|
+
spec.add_dependency "logger"
|
|
31
|
+
end
|