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
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3e7d416cc4e5989199b27614c9ea651a86510ffc823efd22837a995b643dd965
|
|
4
|
+
data.tar.gz: 5abf84d0a65f218974ace1361f5ed8d7ca79fb4512c90f260b86a55307881824
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9382ce0f1278a8547d1fe2d9fd13f637cbe7d793ff9df02db8ed45eeec184d52c3752cb62cf4a227ae1d2aeb155159328aa83b2f6722485cf12c122f581acaa9
|
|
7
|
+
data.tar.gz: 974bf8afa1f54d417b906178b4640b8165fabd4ad1f978c97f98fed376263a56f2a04eddf2ceb94f2f0db15c5255d5babf160c8f962847f5f17a92403e1c4fc8
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2025-12-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release of the Fizzy API Client gem
|
|
13
|
+
- Full CRUD support for all Fizzy resources:
|
|
14
|
+
- Identity (account info)
|
|
15
|
+
- Boards (create, read, update, delete)
|
|
16
|
+
- Cards (full lifecycle including close, reopen, postpone, triage, watch, gild)
|
|
17
|
+
- Columns (with named color support: blue, gray, tan, yellow, lime, aqua, violet, purple, pink)
|
|
18
|
+
- Comments (threaded discussions)
|
|
19
|
+
- Steps (checklist items with complete/incomplete)
|
|
20
|
+
- Reactions (emoji reactions)
|
|
21
|
+
- Tags (with toggle support)
|
|
22
|
+
- Users (including avatar upload and deactivation)
|
|
23
|
+
- Notifications (read/unread management)
|
|
24
|
+
- Direct Uploads (ActiveStorage integration)
|
|
25
|
+
- Named colors for columns (`:blue`, `:lime`, `:pink`, etc.) - no need to use CSS variables
|
|
26
|
+
- Golden ticket support (`gild_card`/`ungild_card`)
|
|
27
|
+
- Background image upload for cards
|
|
28
|
+
- Flexible configuration system:
|
|
29
|
+
- Global configuration via `FizzyApiClient.configure`
|
|
30
|
+
- Client-specific configuration
|
|
31
|
+
- Environment variable support (FIZZY_API_TOKEN, FIZZY_ACCOUNT, FIZZY_BASE_URL)
|
|
32
|
+
- Pagination support:
|
|
33
|
+
- Manual page navigation with `page:` parameter
|
|
34
|
+
- Automatic pagination with `auto_paginate: true`
|
|
35
|
+
- Error handling with specific error classes:
|
|
36
|
+
- AuthenticationError (401)
|
|
37
|
+
- ForbiddenError (403)
|
|
38
|
+
- NotFoundError (404)
|
|
39
|
+
- ValidationError (422)
|
|
40
|
+
- ServerError (500+)
|
|
41
|
+
- Comprehensive demo script that tests all features
|
|
42
|
+
- Self-hosted instance support via `base_url` configuration
|
|
43
|
+
- Account slug normalization (strips leading `/` from identity API slugs)
|
|
44
|
+
- Multipart/form-data support for file uploads
|
|
45
|
+
- Optional logger integration for debugging
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Fizzy Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# Fizzy API Client
|
|
2
|
+
|
|
3
|
+
A Ruby gem providing a clean, idiomatic interface to the [Fizzy](https://fizzy.do) API. Fizzy is a task management app from [37signals](https://37signals.com).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'fizzy-api-client'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
$ bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
$ gem install fizzy-api-client
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
require 'fizzy_api_client'
|
|
29
|
+
|
|
30
|
+
# Configure globally
|
|
31
|
+
FizzyApiClient.configure do |config|
|
|
32
|
+
config.api_token = 'your-api-token'
|
|
33
|
+
config.account_slug = 'your-account'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Or use environment variables:
|
|
37
|
+
# FIZZY_API_TOKEN, FIZZY_ACCOUNT, FIZZY_BASE_URL
|
|
38
|
+
|
|
39
|
+
client = FizzyApiClient::Client.new
|
|
40
|
+
|
|
41
|
+
# Get identity
|
|
42
|
+
identity = client.identity
|
|
43
|
+
|
|
44
|
+
# List boards
|
|
45
|
+
boards = client.boards
|
|
46
|
+
|
|
47
|
+
# Create a card (note: uses 'title' field, not 'name')
|
|
48
|
+
card = client.create_card(board_id: 'board_1', title: 'New Task')
|
|
49
|
+
|
|
50
|
+
# Add a step (note: uses 'content' field)
|
|
51
|
+
step = client.create_step(card['number'], content: 'First step')
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Rails Integration
|
|
55
|
+
|
|
56
|
+
### Configuration with Credentials
|
|
57
|
+
|
|
58
|
+
Store your API credentials securely using Rails credentials:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
$ rails credentials:edit
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
# config/credentials.yml.enc
|
|
66
|
+
fizzy:
|
|
67
|
+
api_token: your-api-token
|
|
68
|
+
account_slug: your-account
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Initializer
|
|
72
|
+
|
|
73
|
+
Create an initializer to configure the client:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
# config/initializers/fizzy.rb
|
|
77
|
+
FizzyApiClient.configure do |config|
|
|
78
|
+
config.api_token = Rails.application.credentials.dig(:fizzy, :api_token)
|
|
79
|
+
config.account_slug = Rails.application.credentials.dig(:fizzy, :account_slug)
|
|
80
|
+
config.logger = Rails.logger if Rails.env.development?
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Controller Example
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
# app/controllers/tasks_controller.rb
|
|
88
|
+
class TasksController < ApplicationController
|
|
89
|
+
def index
|
|
90
|
+
@boards = fizzy_client.boards
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def create
|
|
94
|
+
card = fizzy_client.create_card(
|
|
95
|
+
board_id: params[:board_id],
|
|
96
|
+
title: params[:title],
|
|
97
|
+
description: params[:description]
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
redirect_to tasks_path, notice: "Task ##{card['number']} created"
|
|
101
|
+
rescue FizzyApiClient::ApiError => e
|
|
102
|
+
redirect_to tasks_path, alert: "Failed to create task: #{e.message}"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def complete
|
|
106
|
+
fizzy_client.close_card(params[:card_number])
|
|
107
|
+
redirect_to tasks_path, notice: "Task completed"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def fizzy_client
|
|
113
|
+
@fizzy_client ||= FizzyApiClient::Client.new
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Background Job Example
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
# app/jobs/sync_fizzy_cards_job.rb
|
|
122
|
+
class SyncFizzyCardsJob < ApplicationJob
|
|
123
|
+
queue_as :default
|
|
124
|
+
|
|
125
|
+
def perform(board_id)
|
|
126
|
+
client = FizzyApiClient::Client.new
|
|
127
|
+
cards = client.cards(board_ids: [board_id], auto_paginate: true)
|
|
128
|
+
|
|
129
|
+
cards.each do |card_data|
|
|
130
|
+
Task.find_or_initialize_by(fizzy_number: card_data['number']).update!(
|
|
131
|
+
title: card_data['title'],
|
|
132
|
+
status: card_data['closed_at'].present? ? 'completed' : 'open',
|
|
133
|
+
synced_at: Time.current
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Per-Environment Configuration
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
# config/initializers/fizzy.rb
|
|
144
|
+
FizzyApiClient.configure do |config|
|
|
145
|
+
config.api_token = Rails.application.credentials.dig(:fizzy, :api_token)
|
|
146
|
+
config.account_slug = Rails.application.credentials.dig(:fizzy, :account_slug)
|
|
147
|
+
|
|
148
|
+
# Use different base URL for staging/development
|
|
149
|
+
if Rails.env.staging?
|
|
150
|
+
config.base_url = 'https://staging.fizzy.do'
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Enable logging in development
|
|
154
|
+
config.logger = Rails.logger if Rails.env.development?
|
|
155
|
+
|
|
156
|
+
# Shorter timeouts in test environment
|
|
157
|
+
if Rails.env.test?
|
|
158
|
+
config.timeout = 5
|
|
159
|
+
config.open_timeout = 2
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Configuration
|
|
165
|
+
|
|
166
|
+
### Global Configuration
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
FizzyApiClient.configure do |config|
|
|
170
|
+
config.api_token = 'your-api-token'
|
|
171
|
+
config.account_slug = 'your-account'
|
|
172
|
+
config.base_url = 'https://app.fizzy.do' # default
|
|
173
|
+
config.timeout = 30 # seconds
|
|
174
|
+
config.open_timeout = 10 # seconds
|
|
175
|
+
config.logger = Logger.new($stdout) # optional
|
|
176
|
+
end
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Client Configuration
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
client = FizzyApiClient::Client.new(
|
|
183
|
+
api_token: 'your-api-token',
|
|
184
|
+
account_slug: 'your-account'
|
|
185
|
+
)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Environment Variables
|
|
189
|
+
|
|
190
|
+
- `FIZZY_API_TOKEN` - Personal Access Token
|
|
191
|
+
- `FIZZY_ACCOUNT` - Default account slug
|
|
192
|
+
- `FIZZY_BASE_URL` - API base URL (for self-hosted instances)
|
|
193
|
+
|
|
194
|
+
### Account Slug Normalization
|
|
195
|
+
|
|
196
|
+
The identity API returns account slugs with a leading slash (e.g., `/897362094`). This gem automatically normalizes slugs by stripping the leading slash.
|
|
197
|
+
|
|
198
|
+
## Resources
|
|
199
|
+
|
|
200
|
+
### Identity
|
|
201
|
+
```ruby
|
|
202
|
+
client.identity
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Boards
|
|
206
|
+
```ruby
|
|
207
|
+
# List and retrieve
|
|
208
|
+
client.boards
|
|
209
|
+
client.boards(page: 2)
|
|
210
|
+
client.boards(auto_paginate: true)
|
|
211
|
+
client.board('board_id')
|
|
212
|
+
|
|
213
|
+
# Create
|
|
214
|
+
client.create_board(name: 'New Board')
|
|
215
|
+
client.create_board(
|
|
216
|
+
name: 'Team Board',
|
|
217
|
+
all_access: false,
|
|
218
|
+
auto_postpone_period: 7,
|
|
219
|
+
public_description: '<p>Description</p>'
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Update
|
|
223
|
+
client.update_board('board_id', name: 'Updated Name')
|
|
224
|
+
client.update_board('board_id', user_ids: ['user_1', 'user_2']) # when all_access: false
|
|
225
|
+
|
|
226
|
+
# Delete
|
|
227
|
+
client.delete_board('board_id')
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Cards
|
|
231
|
+
Cards use `title` field (not `name`).
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
# List with filters
|
|
235
|
+
client.cards
|
|
236
|
+
client.cards(board_ids: ['board_1', 'board_2'])
|
|
237
|
+
client.cards(tag_ids: ['tag_1'], assignee_ids: ['user_1'])
|
|
238
|
+
client.cards(terms: ['bug', 'fix'])
|
|
239
|
+
client.cards(auto_paginate: true)
|
|
240
|
+
|
|
241
|
+
# Retrieve
|
|
242
|
+
client.card(42)
|
|
243
|
+
|
|
244
|
+
# Create
|
|
245
|
+
client.create_card(board_id: 'board_1', title: 'New Card')
|
|
246
|
+
client.create_card(
|
|
247
|
+
board_id: 'board_1',
|
|
248
|
+
title: 'Full Card',
|
|
249
|
+
description: 'Details here',
|
|
250
|
+
status: 'published',
|
|
251
|
+
tag_ids: ['tag_1', 'tag_2']
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# Create with image
|
|
255
|
+
client.create_card(
|
|
256
|
+
board_id: 'board_1',
|
|
257
|
+
title: 'Card with Image',
|
|
258
|
+
image: '/path/to/image.png' # or File object
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Update
|
|
262
|
+
client.update_card(42, title: 'Updated Title')
|
|
263
|
+
client.update_card(42, image: '/path/to/new_image.png')
|
|
264
|
+
|
|
265
|
+
# Delete
|
|
266
|
+
client.delete_card(42)
|
|
267
|
+
|
|
268
|
+
# State changes
|
|
269
|
+
client.close_card(42)
|
|
270
|
+
client.reopen_card(42)
|
|
271
|
+
client.postpone_card(42) # or client.not_now_card(42)
|
|
272
|
+
|
|
273
|
+
# Triage (move to column)
|
|
274
|
+
client.triage_card(42, column_id: 'col_1')
|
|
275
|
+
client.untriage_card(42)
|
|
276
|
+
|
|
277
|
+
# Assignments and tags
|
|
278
|
+
client.toggle_assignment(42, assignee_id: 'user_1')
|
|
279
|
+
client.toggle_tag(42, tag_title: 'urgent')
|
|
280
|
+
|
|
281
|
+
# Watch/unwatch
|
|
282
|
+
client.watch_card(42)
|
|
283
|
+
client.unwatch_card(42)
|
|
284
|
+
|
|
285
|
+
# Golden ticket (highlight/pin a card)
|
|
286
|
+
client.gild_card(42)
|
|
287
|
+
client.ungild_card(42)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Columns
|
|
291
|
+
```ruby
|
|
292
|
+
# List and retrieve
|
|
293
|
+
client.columns('board_id')
|
|
294
|
+
client.column('board_id', 'column_id')
|
|
295
|
+
|
|
296
|
+
# Create with named color (recommended)
|
|
297
|
+
client.create_column(board_id: 'board_1', name: 'In Progress')
|
|
298
|
+
client.create_column(board_id: 'board_1', name: 'Review', color: :lime)
|
|
299
|
+
client.create_column(board_id: 'board_1', name: 'Urgent', color: :pink)
|
|
300
|
+
|
|
301
|
+
# Update with named color
|
|
302
|
+
client.update_column('board_id', 'column_id', name: 'Done')
|
|
303
|
+
client.update_column('board_id', 'column_id', color: :purple)
|
|
304
|
+
|
|
305
|
+
# Available colors: :blue (default), :gray, :tan, :yellow, :lime, :aqua, :violet, :purple, :pink
|
|
306
|
+
# CSS variable tokens are also supported: 'var(--color-card-3)'
|
|
307
|
+
|
|
308
|
+
# Delete
|
|
309
|
+
client.delete_column('board_id', 'column_id')
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Comments
|
|
313
|
+
```ruby
|
|
314
|
+
# List and retrieve
|
|
315
|
+
client.comments(42)
|
|
316
|
+
client.comment(42, 'comment_id')
|
|
317
|
+
|
|
318
|
+
# Create
|
|
319
|
+
client.create_comment(42, body: 'This is a comment')
|
|
320
|
+
client.create_comment(42, body: 'Backdated comment', created_at: '2025-01-01T00:00:00Z')
|
|
321
|
+
|
|
322
|
+
# Update
|
|
323
|
+
client.update_comment(42, 'comment_id', body: 'Updated comment')
|
|
324
|
+
|
|
325
|
+
# Delete
|
|
326
|
+
client.delete_comment(42, 'comment_id')
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Steps
|
|
330
|
+
Steps use `content` field (not `name`).
|
|
331
|
+
|
|
332
|
+
```ruby
|
|
333
|
+
# Retrieve
|
|
334
|
+
client.step(42, 'step_id')
|
|
335
|
+
|
|
336
|
+
# Create
|
|
337
|
+
client.create_step(42, content: 'Do this first')
|
|
338
|
+
client.create_step(42, content: 'Already done', completed: true)
|
|
339
|
+
|
|
340
|
+
# Update
|
|
341
|
+
client.update_step(42, 'step_id', content: 'Updated step')
|
|
342
|
+
client.update_step(42, 'step_id', completed: true)
|
|
343
|
+
|
|
344
|
+
# Convenience methods
|
|
345
|
+
client.complete_step(42, 'step_id')
|
|
346
|
+
client.incomplete_step(42, 'step_id')
|
|
347
|
+
|
|
348
|
+
# Delete
|
|
349
|
+
client.delete_step(42, 'step_id')
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Reactions
|
|
353
|
+
Reactions use `content` field (not `emoji`).
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
# List reactions on a comment
|
|
357
|
+
client.reactions(42, 'comment_id')
|
|
358
|
+
|
|
359
|
+
# Add reaction
|
|
360
|
+
client.add_reaction(42, 'comment_id', content: ':+1:')
|
|
361
|
+
|
|
362
|
+
# Remove reaction
|
|
363
|
+
client.remove_reaction(42, 'comment_id', 'reaction_id')
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Tags
|
|
367
|
+
Tags return `title` field (not `name`).
|
|
368
|
+
|
|
369
|
+
```ruby
|
|
370
|
+
client.tags
|
|
371
|
+
client.tags(page: 2)
|
|
372
|
+
client.tags(auto_paginate: true)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Users
|
|
376
|
+
```ruby
|
|
377
|
+
# List and retrieve
|
|
378
|
+
client.users
|
|
379
|
+
client.users(auto_paginate: true)
|
|
380
|
+
client.user('user_id')
|
|
381
|
+
|
|
382
|
+
# Update
|
|
383
|
+
client.update_user('user_id', name: 'New Name')
|
|
384
|
+
|
|
385
|
+
# Update with avatar
|
|
386
|
+
client.update_user('user_id', avatar: '/path/to/avatar.png')
|
|
387
|
+
client.update_user('user_id', name: 'New Name', avatar: File.open('avatar.png'))
|
|
388
|
+
|
|
389
|
+
# Deactivate
|
|
390
|
+
client.deactivate_user('user_id')
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Notifications
|
|
394
|
+
```ruby
|
|
395
|
+
# List
|
|
396
|
+
client.notifications
|
|
397
|
+
client.notifications(auto_paginate: true)
|
|
398
|
+
|
|
399
|
+
# Mark as read/unread
|
|
400
|
+
client.mark_notification_read('notification_id')
|
|
401
|
+
client.mark_notification_unread('notification_id')
|
|
402
|
+
|
|
403
|
+
# Mark all as read
|
|
404
|
+
client.mark_all_notifications_read
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Direct Uploads (File Attachments)
|
|
408
|
+
For uploading files via ActiveStorage:
|
|
409
|
+
|
|
410
|
+
```ruby
|
|
411
|
+
# Simple file upload (handles all steps automatically)
|
|
412
|
+
signed_id = client.upload_file('/path/to/document.pdf')
|
|
413
|
+
signed_id = client.upload_file(file_io, filename: 'report.pdf', content_type: 'application/pdf')
|
|
414
|
+
|
|
415
|
+
# Manual direct upload (for advanced use cases)
|
|
416
|
+
upload = client.create_direct_upload(
|
|
417
|
+
filename: 'document.pdf',
|
|
418
|
+
byte_size: 1024,
|
|
419
|
+
checksum: Base64.strict_encode64(Digest::MD5.digest(content)),
|
|
420
|
+
content_type: 'application/pdf'
|
|
421
|
+
)
|
|
422
|
+
# Then PUT to upload['direct_upload']['url'] with the file content
|
|
423
|
+
# Use upload['signed_id'] to reference the uploaded file
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Pagination
|
|
427
|
+
|
|
428
|
+
```ruby
|
|
429
|
+
# First page only (default)
|
|
430
|
+
boards = client.boards
|
|
431
|
+
|
|
432
|
+
# Specific page
|
|
433
|
+
boards = client.boards(page: 2)
|
|
434
|
+
|
|
435
|
+
# All pages (automatically follows pagination)
|
|
436
|
+
boards = client.boards(auto_paginate: true)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Error Handling
|
|
440
|
+
|
|
441
|
+
```ruby
|
|
442
|
+
begin
|
|
443
|
+
client.board('invalid')
|
|
444
|
+
rescue FizzyApiClient::NotFoundError => e
|
|
445
|
+
puts "Not found: #{e.message}"
|
|
446
|
+
rescue FizzyApiClient::AuthenticationError => e
|
|
447
|
+
puts "Auth failed: #{e.message}"
|
|
448
|
+
rescue FizzyApiClient::ForbiddenError => e
|
|
449
|
+
puts "Access denied: #{e.message}"
|
|
450
|
+
rescue FizzyApiClient::ValidationError => e
|
|
451
|
+
puts "Invalid data: #{e.message}"
|
|
452
|
+
rescue FizzyApiClient::ServerError => e
|
|
453
|
+
puts "Server error: #{e.message}"
|
|
454
|
+
rescue FizzyApiClient::ApiError => e
|
|
455
|
+
puts "API error #{e.status}: #{e.message}"
|
|
456
|
+
rescue FizzyApiClient::ConnectionError => e
|
|
457
|
+
puts "Connection failed: #{e.message}"
|
|
458
|
+
rescue FizzyApiClient::TimeoutError => e
|
|
459
|
+
puts "Request timed out: #{e.message}"
|
|
460
|
+
end
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Demo Script
|
|
464
|
+
|
|
465
|
+
A comprehensive demo script is included that tests every feature of the gem. See [examples/README.md](examples/README.md) for usage instructions.
|
|
466
|
+
|
|
467
|
+
## License
|
|
468
|
+
|
|
469
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
|
data/examples/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Demo Script
|
|
2
|
+
|
|
3
|
+
A comprehensive demo script that tests every feature of the fizzy-api-client gem against a live Fizzy instance.
|
|
4
|
+
|
|
5
|
+
## Running the Demo
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Basic usage
|
|
9
|
+
FIZZY_API_TOKEN=your-token FIZZY_ACCOUNT=your-account ruby examples/demo.rb
|
|
10
|
+
|
|
11
|
+
# For self-hosted Fizzy instances
|
|
12
|
+
FIZZY_BASE_URL=https://fizzy.mycompany.com FIZZY_API_TOKEN=your-token ruby examples/demo.rb
|
|
13
|
+
|
|
14
|
+
# With verbose output (shows full API responses)
|
|
15
|
+
VERBOSE=1 FIZZY_API_TOKEN=your-token FIZZY_ACCOUNT=your-account ruby examples/demo.rb
|
|
16
|
+
|
|
17
|
+
# Skip destructive operations (user updates, deactivation)
|
|
18
|
+
SKIP_DESTRUCTIVE=1 FIZZY_API_TOKEN=your-token FIZZY_ACCOUNT=your-account ruby examples/demo.rb
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Environment Variables
|
|
22
|
+
|
|
23
|
+
| Variable | Required | Description |
|
|
24
|
+
|----------|----------|-------------|
|
|
25
|
+
| `FIZZY_API_TOKEN` | Yes | Your Fizzy API token |
|
|
26
|
+
| `FIZZY_ACCOUNT` | No | Account slug (auto-detected if not set) |
|
|
27
|
+
| `FIZZY_BASE_URL` | No | API base URL for self-hosted instances |
|
|
28
|
+
| `VERBOSE` | No | Set to `1` to show full API responses |
|
|
29
|
+
| `SKIP_DESTRUCTIVE` | No | Set to `1` to skip operations that modify existing user data (update_user, deactivate_user) |
|
|
30
|
+
|
|
31
|
+
## What the Demo Tests
|
|
32
|
+
|
|
33
|
+
The demo runs through all gem features:
|
|
34
|
+
|
|
35
|
+
1. **Identity** - Authentication and account discovery
|
|
36
|
+
2. **Boards** - List, get, create, update, delete
|
|
37
|
+
3. **Columns** - List, get, create with named colors, update, delete
|
|
38
|
+
4. **Cards** - Full CRUD, state changes (close/reopen/postpone), triage, watch, tags, assignments, background image upload, golden tickets
|
|
39
|
+
5. **Steps** - Create, get, update, complete/incomplete, delete
|
|
40
|
+
6. **Comments** - Full CRUD operations
|
|
41
|
+
7. **Reactions** - Add, list, remove
|
|
42
|
+
8. **Tags** - List with pagination
|
|
43
|
+
9. **Users** - List, get single user
|
|
44
|
+
10. **Notifications** - List, mark read/unread, mark all read
|
|
45
|
+
11. **Direct Uploads** - File upload via ActiveStorage
|
|
46
|
+
12. **Pagination** - Auto-pagination for all list endpoints
|
|
47
|
+
13. **Error Handling** - Validates proper error responses
|
|
48
|
+
14. **Cleanup** - Removes all test data created during the demo
|
|
49
|
+
|
|
50
|
+
## Test Data
|
|
51
|
+
|
|
52
|
+
The demo creates temporary test data and cleans up after itself:
|
|
53
|
+
|
|
54
|
+
- Creates a test board
|
|
55
|
+
- Creates test columns with various colors
|
|
56
|
+
- Creates test cards (including one with `sydney.jpg` as background image)
|
|
57
|
+
- Creates test steps, comments, and reactions
|
|
58
|
+
- Tests golden ticket (gild/ungild) functionality
|
|
59
|
+
- Deletes all created resources at the end
|
|
60
|
+
|
|
61
|
+
## Test Image
|
|
62
|
+
|
|
63
|
+
The `sydney.jpg` file is included for testing background image uploads on cards.
|
|
64
|
+
|
|
65
|
+
## Expected Output
|
|
66
|
+
|
|
67
|
+
A successful run looks like:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
============================================================
|
|
71
|
+
Fizzy API Client - Comprehensive Demo
|
|
72
|
+
============================================================
|
|
73
|
+
Version: 0.1.0
|
|
74
|
+
Base URL: https://app.fizzy.do
|
|
75
|
+
Skip destructive: false
|
|
76
|
+
============================================================
|
|
77
|
+
|
|
78
|
+
1. IDENTITY
|
|
79
|
+
-----------
|
|
80
|
+
✓ Get identity
|
|
81
|
+
Accounts: /123456
|
|
82
|
+
|
|
83
|
+
2. BOARDS
|
|
84
|
+
---------
|
|
85
|
+
✓ List boards
|
|
86
|
+
✓ Get single board
|
|
87
|
+
✓ Create board
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
============================================================
|
|
91
|
+
DEMO SUMMARY
|
|
92
|
+
============================================================
|
|
93
|
+
Passed: 70
|
|
94
|
+
Failed: 0
|
|
95
|
+
Skipped: 4
|
|
96
|
+
============================================================
|
|
97
|
+
All tests passed!
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Skipped tests are expected for:
|
|
101
|
+
- Toggle assignment (when identity doesn't include current user)
|
|
102
|
+
- Update user, avatar, deactivate (skipped to protect real data)
|