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
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FizzyApiClient
|
|
4
|
+
module Resources
|
|
5
|
+
module Cards
|
|
6
|
+
def cards(page: nil, auto_paginate: false, account_slug: nil, **filters)
|
|
7
|
+
params = build_card_filters(filters)
|
|
8
|
+
paginate("/cards", params: params, account_slug: account_slug, auto_paginate: auto_paginate, page: page)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def card(card_number, account_slug: nil)
|
|
12
|
+
slug = effective_account_slug(account_slug)
|
|
13
|
+
response = connection.get("/#{slug}/cards/#{card_number}")
|
|
14
|
+
response.body
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create_card(board_id:, title:, description: nil, status: nil, tag_ids: nil,
|
|
18
|
+
created_at: nil, last_active_at: nil, image: nil, account_slug: nil)
|
|
19
|
+
slug = effective_account_slug(account_slug)
|
|
20
|
+
|
|
21
|
+
payload = { title: title }
|
|
22
|
+
payload[:description] = description unless description.nil?
|
|
23
|
+
payload[:status] = status unless status.nil?
|
|
24
|
+
payload[:tag_ids] = tag_ids unless tag_ids.nil?
|
|
25
|
+
payload[:created_at] = created_at unless created_at.nil?
|
|
26
|
+
payload[:last_active_at] = last_active_at unless last_active_at.nil?
|
|
27
|
+
|
|
28
|
+
if image
|
|
29
|
+
response = create_card_with_image(slug, board_id, payload, image)
|
|
30
|
+
else
|
|
31
|
+
response = connection.post("/#{slug}/boards/#{board_id}/cards", body: { card: payload })
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
response = connection.follow_location(response)
|
|
35
|
+
response.body
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def update_card(card_number, title: nil, description: nil, status: nil, tag_ids: nil,
|
|
39
|
+
last_active_at: nil, image: nil, account_slug: nil)
|
|
40
|
+
slug = effective_account_slug(account_slug)
|
|
41
|
+
|
|
42
|
+
payload = {}
|
|
43
|
+
payload[:title] = title unless title.nil?
|
|
44
|
+
payload[:description] = description unless description.nil?
|
|
45
|
+
payload[:status] = status unless status.nil?
|
|
46
|
+
payload[:tag_ids] = tag_ids unless tag_ids.nil?
|
|
47
|
+
payload[:last_active_at] = last_active_at unless last_active_at.nil?
|
|
48
|
+
|
|
49
|
+
if image
|
|
50
|
+
response = update_card_with_image(slug, card_number, payload, image)
|
|
51
|
+
else
|
|
52
|
+
response = connection.put("/#{slug}/cards/#{card_number}", body: { card: payload })
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
response.no_content? ? nil : response.body
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def delete_card(card_number, account_slug: nil)
|
|
59
|
+
slug = effective_account_slug(account_slug)
|
|
60
|
+
response = connection.delete("/#{slug}/cards/#{card_number}")
|
|
61
|
+
response.no_content? ? nil : response.body
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def close_card(card_number, account_slug: nil)
|
|
65
|
+
slug = effective_account_slug(account_slug)
|
|
66
|
+
response = connection.post("/#{slug}/cards/#{card_number}/closure")
|
|
67
|
+
response.no_content? ? nil : response.body
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def reopen_card(card_number, account_slug: nil)
|
|
71
|
+
slug = effective_account_slug(account_slug)
|
|
72
|
+
response = connection.delete("/#{slug}/cards/#{card_number}/closure")
|
|
73
|
+
response.no_content? ? nil : response.body
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def postpone_card(card_number, account_slug: nil)
|
|
77
|
+
slug = effective_account_slug(account_slug)
|
|
78
|
+
response = connection.post("/#{slug}/cards/#{card_number}/not_now")
|
|
79
|
+
response.no_content? ? nil : response.body
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
alias not_now_card postpone_card
|
|
83
|
+
|
|
84
|
+
def triage_card(card_number, column_id:, account_slug: nil)
|
|
85
|
+
slug = effective_account_slug(account_slug)
|
|
86
|
+
response = connection.post("/#{slug}/cards/#{card_number}/triage",
|
|
87
|
+
body: { column_id: column_id })
|
|
88
|
+
response.no_content? ? nil : response.body
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def untriage_card(card_number, account_slug: nil)
|
|
92
|
+
slug = effective_account_slug(account_slug)
|
|
93
|
+
response = connection.delete("/#{slug}/cards/#{card_number}/triage")
|
|
94
|
+
response.no_content? ? nil : response.body
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def toggle_assignment(card_number, assignee_id:, account_slug: nil)
|
|
98
|
+
slug = effective_account_slug(account_slug)
|
|
99
|
+
response = connection.post("/#{slug}/cards/#{card_number}/assignments",
|
|
100
|
+
body: { assignee_id: assignee_id })
|
|
101
|
+
response.no_content? ? nil : response.body
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def toggle_tag(card_number, tag_title:, account_slug: nil)
|
|
105
|
+
slug = effective_account_slug(account_slug)
|
|
106
|
+
response = connection.post("/#{slug}/cards/#{card_number}/taggings",
|
|
107
|
+
body: { tag_title: tag_title })
|
|
108
|
+
response.no_content? ? nil : response.body
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def watch_card(card_number, account_slug: nil)
|
|
112
|
+
slug = effective_account_slug(account_slug)
|
|
113
|
+
response = connection.post("/#{slug}/cards/#{card_number}/watch")
|
|
114
|
+
response.no_content? ? nil : response.body
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def unwatch_card(card_number, account_slug: nil)
|
|
118
|
+
slug = effective_account_slug(account_slug)
|
|
119
|
+
response = connection.delete("/#{slug}/cards/#{card_number}/watch")
|
|
120
|
+
response.no_content? ? nil : response.body
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Makes a card a "golden ticket" (highlighted/pinned card)
|
|
124
|
+
def gild_card(card_number, account_slug: nil)
|
|
125
|
+
slug = effective_account_slug(account_slug)
|
|
126
|
+
response = connection.post("/#{slug}/cards/#{card_number}/goldness")
|
|
127
|
+
response.no_content? ? nil : response.body
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Removes "golden ticket" status from a card
|
|
131
|
+
def ungild_card(card_number, account_slug: nil)
|
|
132
|
+
slug = effective_account_slug(account_slug)
|
|
133
|
+
response = connection.delete("/#{slug}/cards/#{card_number}/goldness")
|
|
134
|
+
response.no_content? ? nil : response.body
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
def build_card_filters(filters)
|
|
140
|
+
params = {}
|
|
141
|
+
|
|
142
|
+
# Array filters - encode with []
|
|
143
|
+
%i[board_ids tag_ids assignee_ids creator_ids closer_ids card_ids terms].each do |key|
|
|
144
|
+
params[key] = filters[key] if filters[key]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Scalar filters
|
|
148
|
+
%i[indexed_by sorted_by assignment_status creation closure].each do |key|
|
|
149
|
+
params[key] = filters[key] if filters[key]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
params
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def create_card_with_image(slug, board_id, payload, image)
|
|
156
|
+
fields = build_multipart_fields("card", payload)
|
|
157
|
+
files = { "card[image]" => build_file_info(image) }
|
|
158
|
+
|
|
159
|
+
connection.post_multipart("/#{slug}/boards/#{board_id}/cards", fields: fields, files: files)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def update_card_with_image(slug, card_number, payload, image)
|
|
163
|
+
fields = build_multipart_fields("card", payload)
|
|
164
|
+
files = { "card[image]" => build_file_info(image) }
|
|
165
|
+
|
|
166
|
+
connection.put_multipart("/#{slug}/cards/#{card_number}", fields: fields, files: files)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def build_multipart_fields(prefix, payload)
|
|
170
|
+
fields = {}
|
|
171
|
+
payload.each do |key, value|
|
|
172
|
+
if value.is_a?(Array)
|
|
173
|
+
value.each_with_index do |v, i|
|
|
174
|
+
fields["#{prefix}[#{key}][]"] = v.to_s
|
|
175
|
+
end
|
|
176
|
+
else
|
|
177
|
+
fields["#{prefix}[#{key}]"] = value.to_s
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
fields
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def build_file_info(file)
|
|
184
|
+
if file.is_a?(String)
|
|
185
|
+
# File path
|
|
186
|
+
{
|
|
187
|
+
filename: File.basename(file),
|
|
188
|
+
content: File.read(file),
|
|
189
|
+
content_type: guess_content_type(file)
|
|
190
|
+
}
|
|
191
|
+
else
|
|
192
|
+
# IO object
|
|
193
|
+
{
|
|
194
|
+
filename: file.respond_to?(:path) ? File.basename(file.path) : "upload",
|
|
195
|
+
content: file.read,
|
|
196
|
+
content_type: "application/octet-stream"
|
|
197
|
+
}
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def guess_content_type(path)
|
|
202
|
+
case File.extname(path).downcase
|
|
203
|
+
when ".png" then "image/png"
|
|
204
|
+
when ".jpg", ".jpeg" then "image/jpeg"
|
|
205
|
+
when ".gif" then "image/gif"
|
|
206
|
+
when ".webp" then "image/webp"
|
|
207
|
+
else "application/octet-stream"
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FizzyApiClient
|
|
4
|
+
module Resources
|
|
5
|
+
module Columns
|
|
6
|
+
def columns(board_id, account_slug: nil)
|
|
7
|
+
slug = effective_account_slug(account_slug)
|
|
8
|
+
response = connection.get("/#{slug}/boards/#{board_id}/columns")
|
|
9
|
+
response.body
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def column(board_id, column_id, account_slug: nil)
|
|
13
|
+
slug = effective_account_slug(account_slug)
|
|
14
|
+
response = connection.get("/#{slug}/boards/#{board_id}/columns/#{column_id}")
|
|
15
|
+
response.body
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Creates a column on a board.
|
|
19
|
+
#
|
|
20
|
+
# @param board_id [String] the board ID
|
|
21
|
+
# @param name [String] the column name
|
|
22
|
+
# @param color [Symbol, String, nil] the column color - accepts named colors
|
|
23
|
+
# (:blue, :gray, :tan, :yellow, :lime, :aqua, :violet, :purple, :pink)
|
|
24
|
+
# or CSS variable strings
|
|
25
|
+
# @param account_slug [String, nil] optional account slug override
|
|
26
|
+
# @return [Hash] the created column
|
|
27
|
+
#
|
|
28
|
+
# @example Create with named color
|
|
29
|
+
# client.create_column(board_id: 'board_1', name: 'Review', color: :lime)
|
|
30
|
+
#
|
|
31
|
+
# @example Create with CSS variable (still supported)
|
|
32
|
+
# client.create_column(board_id: 'board_1', name: 'Review', color: 'var(--color-card-4)')
|
|
33
|
+
#
|
|
34
|
+
def create_column(board_id:, name:, color: nil, account_slug: nil)
|
|
35
|
+
slug = effective_account_slug(account_slug)
|
|
36
|
+
|
|
37
|
+
payload = { name: name }
|
|
38
|
+
payload[:color] = Colors.resolve(color) unless color.nil?
|
|
39
|
+
|
|
40
|
+
response = connection.post("/#{slug}/boards/#{board_id}/columns", body: { column: payload })
|
|
41
|
+
response = connection.follow_location(response)
|
|
42
|
+
response.body
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Updates a column.
|
|
46
|
+
#
|
|
47
|
+
# @param board_id [String] the board ID
|
|
48
|
+
# @param column_id [String] the column ID
|
|
49
|
+
# @param name [String, nil] the new column name
|
|
50
|
+
# @param color [Symbol, String, nil] the new column color - accepts named colors
|
|
51
|
+
# (:blue, :gray, :tan, :yellow, :lime, :aqua, :violet, :purple, :pink)
|
|
52
|
+
# or CSS variable strings
|
|
53
|
+
# @param account_slug [String, nil] optional account slug override
|
|
54
|
+
# @return [Hash, nil] the updated column or nil if 204 response
|
|
55
|
+
#
|
|
56
|
+
# @example Update with named color
|
|
57
|
+
# client.update_column('board_id', 'col_id', color: :purple)
|
|
58
|
+
#
|
|
59
|
+
def update_column(board_id, column_id, name: nil, color: nil, account_slug: nil)
|
|
60
|
+
slug = effective_account_slug(account_slug)
|
|
61
|
+
|
|
62
|
+
payload = {}
|
|
63
|
+
payload[:name] = name unless name.nil?
|
|
64
|
+
payload[:color] = Colors.resolve(color) unless color.nil?
|
|
65
|
+
|
|
66
|
+
response = connection.put("/#{slug}/boards/#{board_id}/columns/#{column_id}",
|
|
67
|
+
body: { column: payload })
|
|
68
|
+
response.no_content? ? nil : response.body
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def delete_column(board_id, column_id, account_slug: nil)
|
|
72
|
+
slug = effective_account_slug(account_slug)
|
|
73
|
+
response = connection.delete("/#{slug}/boards/#{board_id}/columns/#{column_id}")
|
|
74
|
+
response.no_content? ? nil : response.body
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FizzyApiClient
|
|
4
|
+
module Resources
|
|
5
|
+
module Comments
|
|
6
|
+
def comments(card_number, account_slug: nil)
|
|
7
|
+
slug = effective_account_slug(account_slug)
|
|
8
|
+
response = connection.get("/#{slug}/cards/#{card_number}/comments")
|
|
9
|
+
response.body
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def comment(card_number, comment_id, account_slug: nil)
|
|
13
|
+
slug = effective_account_slug(account_slug)
|
|
14
|
+
response = connection.get("/#{slug}/cards/#{card_number}/comments/#{comment_id}")
|
|
15
|
+
response.body
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_comment(card_number, body:, created_at: nil, account_slug: nil)
|
|
19
|
+
slug = effective_account_slug(account_slug)
|
|
20
|
+
|
|
21
|
+
payload = { body: body }
|
|
22
|
+
payload[:created_at] = created_at unless created_at.nil?
|
|
23
|
+
|
|
24
|
+
response = connection.post("/#{slug}/cards/#{card_number}/comments",
|
|
25
|
+
body: { comment: payload })
|
|
26
|
+
response = connection.follow_location(response)
|
|
27
|
+
response.body
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def update_comment(card_number, comment_id, body:, account_slug: nil)
|
|
31
|
+
slug = effective_account_slug(account_slug)
|
|
32
|
+
|
|
33
|
+
response = connection.put("/#{slug}/cards/#{card_number}/comments/#{comment_id}",
|
|
34
|
+
body: { comment: { body: body } })
|
|
35
|
+
response.no_content? ? nil : response.body
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def delete_comment(card_number, comment_id, account_slug: nil)
|
|
39
|
+
slug = effective_account_slug(account_slug)
|
|
40
|
+
response = connection.delete("/#{slug}/cards/#{card_number}/comments/#{comment_id}")
|
|
41
|
+
response.no_content? ? nil : response.body
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FizzyApiClient
|
|
4
|
+
module Resources
|
|
5
|
+
module DirectUploads
|
|
6
|
+
def create_direct_upload(filename:, byte_size:, checksum:, content_type:, account_slug: nil)
|
|
7
|
+
slug = effective_account_slug(account_slug)
|
|
8
|
+
|
|
9
|
+
payload = {
|
|
10
|
+
filename: filename,
|
|
11
|
+
byte_size: byte_size,
|
|
12
|
+
checksum: checksum,
|
|
13
|
+
content_type: content_type
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
response = connection.post("/#{slug}/rails/active_storage/direct_uploads",
|
|
17
|
+
body: { blob: payload })
|
|
18
|
+
response.body
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def upload_file(file_path_or_io, filename: nil, content_type: nil, account_slug: nil)
|
|
22
|
+
file_content, file_name, file_size = read_file_info(file_path_or_io, filename)
|
|
23
|
+
checksum = calculate_checksum(file_content)
|
|
24
|
+
content_type ||= "application/octet-stream"
|
|
25
|
+
|
|
26
|
+
# Step 1: Create direct upload
|
|
27
|
+
result = create_direct_upload(
|
|
28
|
+
filename: file_name,
|
|
29
|
+
byte_size: file_size,
|
|
30
|
+
checksum: checksum,
|
|
31
|
+
content_type: content_type,
|
|
32
|
+
account_slug: account_slug
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Step 2: Upload to storage URL
|
|
36
|
+
upload_url = result["direct_upload"]["url"]
|
|
37
|
+
upload_headers = result["direct_upload"]["headers"] || {}
|
|
38
|
+
|
|
39
|
+
put_to_external_url(upload_url, file_content, upload_headers)
|
|
40
|
+
|
|
41
|
+
# Step 3: Return signed_id
|
|
42
|
+
result["signed_id"]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def read_file_info(file_path_or_io, filename)
|
|
48
|
+
if file_path_or_io.is_a?(String)
|
|
49
|
+
content = File.binread(file_path_or_io)
|
|
50
|
+
name = filename || File.basename(file_path_or_io)
|
|
51
|
+
size = content.bytesize
|
|
52
|
+
else
|
|
53
|
+
content = file_path_or_io.read
|
|
54
|
+
name = filename || (file_path_or_io.respond_to?(:path) ? File.basename(file_path_or_io.path) : "upload")
|
|
55
|
+
size = content.bytesize
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
[content, name, size]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def calculate_checksum(content)
|
|
62
|
+
Base64.strict_encode64(Digest::MD5.digest(content))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def put_to_external_url(url, body, headers)
|
|
66
|
+
uri = URI.parse(url)
|
|
67
|
+
|
|
68
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
69
|
+
http.use_ssl = uri.scheme == "https"
|
|
70
|
+
|
|
71
|
+
request = Net::HTTP::Put.new(uri.request_uri)
|
|
72
|
+
headers.each { |key, value| request[key] = value }
|
|
73
|
+
request.body = body
|
|
74
|
+
|
|
75
|
+
response = http.request(request)
|
|
76
|
+
|
|
77
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
78
|
+
raise ApiError.new(
|
|
79
|
+
message: "Failed to upload file to storage",
|
|
80
|
+
status: response.code.to_i
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
response
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FizzyApiClient
|
|
4
|
+
module Resources
|
|
5
|
+
module Notifications
|
|
6
|
+
def notifications(page: nil, auto_paginate: false, account_slug: nil)
|
|
7
|
+
paginate("/notifications", account_slug: account_slug, auto_paginate: auto_paginate, page: page)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def mark_notification_read(notification_id, account_slug: nil)
|
|
11
|
+
slug = effective_account_slug(account_slug)
|
|
12
|
+
response = connection.post("/#{slug}/notifications/#{notification_id}/reading")
|
|
13
|
+
response.no_content? ? nil : response.body
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def mark_notification_unread(notification_id, account_slug: nil)
|
|
17
|
+
slug = effective_account_slug(account_slug)
|
|
18
|
+
response = connection.delete("/#{slug}/notifications/#{notification_id}/reading")
|
|
19
|
+
response.no_content? ? nil : response.body
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def mark_all_notifications_read(account_slug: nil)
|
|
23
|
+
slug = effective_account_slug(account_slug)
|
|
24
|
+
response = connection.post("/#{slug}/notifications/bulk_reading")
|
|
25
|
+
response.no_content? ? nil : response.body
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FizzyApiClient
|
|
4
|
+
module Resources
|
|
5
|
+
module Reactions
|
|
6
|
+
def reactions(card_number, comment_id, account_slug: nil)
|
|
7
|
+
slug = effective_account_slug(account_slug)
|
|
8
|
+
response = connection.get("/#{slug}/cards/#{card_number}/comments/#{comment_id}/reactions")
|
|
9
|
+
response.body
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_reaction(card_number, comment_id, content:, account_slug: nil)
|
|
13
|
+
slug = effective_account_slug(account_slug)
|
|
14
|
+
response = connection.post(
|
|
15
|
+
"/#{slug}/cards/#{card_number}/comments/#{comment_id}/reactions",
|
|
16
|
+
body: { reaction: { content: content } }
|
|
17
|
+
)
|
|
18
|
+
response = connection.follow_location(response)
|
|
19
|
+
response.body
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def remove_reaction(card_number, comment_id, reaction_id, account_slug: nil)
|
|
23
|
+
slug = effective_account_slug(account_slug)
|
|
24
|
+
response = connection.delete(
|
|
25
|
+
"/#{slug}/cards/#{card_number}/comments/#{comment_id}/reactions/#{reaction_id}"
|
|
26
|
+
)
|
|
27
|
+
response.no_content? ? nil : response.body
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FizzyApiClient
|
|
4
|
+
module Resources
|
|
5
|
+
module Steps
|
|
6
|
+
def step(card_number, step_id, account_slug: nil)
|
|
7
|
+
slug = effective_account_slug(account_slug)
|
|
8
|
+
response = connection.get("/#{slug}/cards/#{card_number}/steps/#{step_id}")
|
|
9
|
+
response.body
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create_step(card_number, content:, completed: nil, account_slug: nil)
|
|
13
|
+
slug = effective_account_slug(account_slug)
|
|
14
|
+
|
|
15
|
+
payload = { content: content }
|
|
16
|
+
payload[:completed] = completed unless completed.nil?
|
|
17
|
+
|
|
18
|
+
response = connection.post("/#{slug}/cards/#{card_number}/steps",
|
|
19
|
+
body: { step: payload })
|
|
20
|
+
response = connection.follow_location(response)
|
|
21
|
+
response.body
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def update_step(card_number, step_id, content: nil, completed: nil, account_slug: nil)
|
|
25
|
+
slug = effective_account_slug(account_slug)
|
|
26
|
+
|
|
27
|
+
payload = {}
|
|
28
|
+
payload[:content] = content unless content.nil?
|
|
29
|
+
payload[:completed] = completed unless completed.nil?
|
|
30
|
+
|
|
31
|
+
response = connection.put("/#{slug}/cards/#{card_number}/steps/#{step_id}",
|
|
32
|
+
body: { step: payload })
|
|
33
|
+
response.no_content? ? nil : response.body
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def complete_step(card_number, step_id, account_slug: nil)
|
|
37
|
+
update_step(card_number, step_id, completed: true, account_slug: account_slug)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def incomplete_step(card_number, step_id, account_slug: nil)
|
|
41
|
+
update_step(card_number, step_id, completed: false, account_slug: account_slug)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def delete_step(card_number, step_id, account_slug: nil)
|
|
45
|
+
slug = effective_account_slug(account_slug)
|
|
46
|
+
response = connection.delete("/#{slug}/cards/#{card_number}/steps/#{step_id}")
|
|
47
|
+
response.no_content? ? nil : response.body
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FizzyApiClient
|
|
4
|
+
module Resources
|
|
5
|
+
module Tags
|
|
6
|
+
def tags(page: nil, auto_paginate: false, account_slug: nil)
|
|
7
|
+
paginate("/tags", account_slug: account_slug, auto_paginate: auto_paginate, page: page)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FizzyApiClient
|
|
4
|
+
module Resources
|
|
5
|
+
module Users
|
|
6
|
+
def users(page: nil, auto_paginate: false, account_slug: nil)
|
|
7
|
+
paginate("/users", account_slug: account_slug, auto_paginate: auto_paginate, page: page)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def user(user_id, account_slug: nil)
|
|
11
|
+
slug = effective_account_slug(account_slug)
|
|
12
|
+
response = connection.get("/#{slug}/users/#{user_id}")
|
|
13
|
+
response.body
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def update_user(user_id, name: nil, avatar: nil, account_slug: nil)
|
|
17
|
+
slug = effective_account_slug(account_slug)
|
|
18
|
+
|
|
19
|
+
if avatar
|
|
20
|
+
response = update_user_with_avatar(slug, user_id, name, avatar)
|
|
21
|
+
else
|
|
22
|
+
payload = {}
|
|
23
|
+
payload[:name] = name unless name.nil?
|
|
24
|
+
|
|
25
|
+
response = connection.put("/#{slug}/users/#{user_id}", body: { user: payload })
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
response.no_content? ? nil : response.body
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def deactivate_user(user_id, account_slug: nil)
|
|
32
|
+
slug = effective_account_slug(account_slug)
|
|
33
|
+
response = connection.delete("/#{slug}/users/#{user_id}")
|
|
34
|
+
response.no_content? ? nil : response.body
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def update_user_with_avatar(slug, user_id, name, avatar)
|
|
40
|
+
fields = {}
|
|
41
|
+
fields["user[name]"] = name if name
|
|
42
|
+
|
|
43
|
+
files = { "user[avatar]" => build_user_file_info(avatar) }
|
|
44
|
+
|
|
45
|
+
connection.put_multipart("/#{slug}/users/#{user_id}", fields: fields, files: files)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def build_user_file_info(file)
|
|
49
|
+
if file.is_a?(String)
|
|
50
|
+
{
|
|
51
|
+
filename: File.basename(file),
|
|
52
|
+
content: File.read(file),
|
|
53
|
+
content_type: guess_user_content_type(file)
|
|
54
|
+
}
|
|
55
|
+
else
|
|
56
|
+
{
|
|
57
|
+
filename: file.respond_to?(:path) ? File.basename(file.path) : "avatar",
|
|
58
|
+
content: file.read,
|
|
59
|
+
content_type: "application/octet-stream"
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def guess_user_content_type(path)
|
|
65
|
+
case File.extname(path).downcase
|
|
66
|
+
when ".png" then "image/png"
|
|
67
|
+
when ".jpg", ".jpeg" then "image/jpeg"
|
|
68
|
+
when ".gif" then "image/gif"
|
|
69
|
+
when ".webp" then "image/webp"
|
|
70
|
+
else "application/octet-stream"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|