fizzy-sdk 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/fizzy/config.rb +1 -1
- data/lib/fizzy/generated/metadata.json +29 -78
- data/lib/fizzy/generated/services/boards_service.rb +4 -4
- data/lib/fizzy/generated/services/cards_service.rb +30 -35
- data/lib/fizzy/generated/services/columns_service.rb +3 -3
- data/lib/fizzy/generated/services/identity_service.rb +2 -2
- data/lib/fizzy/generated/services/miscellaneous_service.rb +21 -21
- data/lib/fizzy/generated/services/notifications_service.rb +5 -7
- data/lib/fizzy/generated/services/sessions_service.rb +5 -5
- data/lib/fizzy/generated/services/steps_service.rb +1 -1
- data/lib/fizzy/generated/services/users_service.rb +2 -2
- data/lib/fizzy/generated/services/webhooks_service.rb +6 -7
- data/lib/fizzy/generated/types.rb +7 -13
- data/lib/fizzy/http.rb +26 -13
- data/lib/fizzy/magic_link_flow.rb +93 -29
- data/lib/fizzy/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5cb258ecc0bb6152a3736dd03883662cf809277bc85444ed698c386ae641360a
|
|
4
|
+
data.tar.gz: 3da686f5810a68724d669fb3c4f07cac37799be99c0e1d5cc07ed235330b3679
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fd2608ea08ec97413c39d9f22ed1fca328fe917b4f3cb7fdde4a1da1d0b47725eb75cd2a2f8786b8df3a1bc929cf95c1fd20bf658e340b90372c34e5fe530f61
|
|
7
|
+
data.tar.gz: 3e32b0a281961f7c7c779b0bb0a70fcff3550aa04f732e4779ed33ce15826134034d3d56deaff1a8ad7820f6b0813177456c8a6e2ae941f8c993209a8917e13f
|
data/lib/fizzy/config.rb
CHANGED
|
@@ -48,7 +48,7 @@ module Fizzy
|
|
|
48
48
|
#
|
|
49
49
|
# @param base_url [String] API base URL
|
|
50
50
|
# @param timeout [Integer] request timeout in seconds
|
|
51
|
-
# @param max_retries [Integer]
|
|
51
|
+
# @param max_retries [Integer] total attempts per retryable request, floored at one
|
|
52
52
|
# @param base_delay [Float] initial backoff delay
|
|
53
53
|
# @param max_jitter [Float] maximum jitter
|
|
54
54
|
# @param max_pages [Integer] maximum pages to fetch
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://fizzy.do/schemas/sdk-metadata.json",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"generated": "2026-
|
|
4
|
+
"generated": "2026-10-17T23:45:49Z",
|
|
5
5
|
"operations": {
|
|
6
6
|
"ListAccessTokens": {
|
|
7
7
|
"retry": {
|
|
@@ -154,17 +154,10 @@
|
|
|
154
154
|
},
|
|
155
155
|
"ResetJoinCode": {
|
|
156
156
|
"retry": {
|
|
157
|
-
"maxAttempts":
|
|
158
|
-
"baseDelayMs":
|
|
159
|
-
"backoff":
|
|
160
|
-
"retryOn":
|
|
161
|
-
429,
|
|
162
|
-
500,
|
|
163
|
-
503
|
|
164
|
-
]
|
|
165
|
-
},
|
|
166
|
-
"idempotent": {
|
|
167
|
-
"natural": true
|
|
157
|
+
"maxAttempts": 1,
|
|
158
|
+
"baseDelayMs": null,
|
|
159
|
+
"backoff": null,
|
|
160
|
+
"retryOn": null
|
|
168
161
|
}
|
|
169
162
|
},
|
|
170
163
|
"GetAccountSettings": {
|
|
@@ -834,17 +827,10 @@
|
|
|
834
827
|
},
|
|
835
828
|
"PostponeCard": {
|
|
836
829
|
"retry": {
|
|
837
|
-
"maxAttempts":
|
|
838
|
-
"baseDelayMs":
|
|
839
|
-
"backoff":
|
|
840
|
-
"retryOn":
|
|
841
|
-
429,
|
|
842
|
-
500,
|
|
843
|
-
503
|
|
844
|
-
]
|
|
845
|
-
},
|
|
846
|
-
"idempotent": {
|
|
847
|
-
"natural": true
|
|
830
|
+
"maxAttempts": 1,
|
|
831
|
+
"baseDelayMs": null,
|
|
832
|
+
"backoff": null,
|
|
833
|
+
"retryOn": null
|
|
848
834
|
}
|
|
849
835
|
},
|
|
850
836
|
"PinCard": {
|
|
@@ -864,17 +850,10 @@
|
|
|
864
850
|
},
|
|
865
851
|
"UnpinCard": {
|
|
866
852
|
"retry": {
|
|
867
|
-
"maxAttempts":
|
|
868
|
-
"baseDelayMs":
|
|
869
|
-
"backoff":
|
|
870
|
-
"retryOn":
|
|
871
|
-
429,
|
|
872
|
-
500,
|
|
873
|
-
503
|
|
874
|
-
]
|
|
875
|
-
},
|
|
876
|
-
"idempotent": {
|
|
877
|
-
"natural": true
|
|
853
|
+
"maxAttempts": 1,
|
|
854
|
+
"baseDelayMs": null,
|
|
855
|
+
"backoff": null,
|
|
856
|
+
"retryOn": null
|
|
878
857
|
}
|
|
879
858
|
},
|
|
880
859
|
"PublishCard": {
|
|
@@ -1030,32 +1009,18 @@
|
|
|
1030
1009
|
},
|
|
1031
1010
|
"TriageCard": {
|
|
1032
1011
|
"retry": {
|
|
1033
|
-
"maxAttempts":
|
|
1034
|
-
"baseDelayMs":
|
|
1035
|
-
"backoff":
|
|
1036
|
-
"retryOn":
|
|
1037
|
-
429,
|
|
1038
|
-
500,
|
|
1039
|
-
503
|
|
1040
|
-
]
|
|
1041
|
-
},
|
|
1042
|
-
"idempotent": {
|
|
1043
|
-
"natural": true
|
|
1012
|
+
"maxAttempts": 1,
|
|
1013
|
+
"baseDelayMs": null,
|
|
1014
|
+
"backoff": null,
|
|
1015
|
+
"retryOn": null
|
|
1044
1016
|
}
|
|
1045
1017
|
},
|
|
1046
1018
|
"UnTriageCard": {
|
|
1047
1019
|
"retry": {
|
|
1048
|
-
"maxAttempts":
|
|
1049
|
-
"baseDelayMs":
|
|
1050
|
-
"backoff":
|
|
1051
|
-
"retryOn":
|
|
1052
|
-
429,
|
|
1053
|
-
500,
|
|
1054
|
-
503
|
|
1055
|
-
]
|
|
1056
|
-
},
|
|
1057
|
-
"idempotent": {
|
|
1058
|
-
"natural": true
|
|
1020
|
+
"maxAttempts": 1,
|
|
1021
|
+
"baseDelayMs": null,
|
|
1022
|
+
"backoff": null,
|
|
1023
|
+
"retryOn": null
|
|
1059
1024
|
}
|
|
1060
1025
|
},
|
|
1061
1026
|
"WatchCard": {
|
|
@@ -1090,32 +1055,18 @@
|
|
|
1090
1055
|
},
|
|
1091
1056
|
"MoveColumnLeft": {
|
|
1092
1057
|
"retry": {
|
|
1093
|
-
"maxAttempts":
|
|
1094
|
-
"baseDelayMs":
|
|
1095
|
-
"backoff":
|
|
1096
|
-
"retryOn":
|
|
1097
|
-
429,
|
|
1098
|
-
500,
|
|
1099
|
-
503
|
|
1100
|
-
]
|
|
1101
|
-
},
|
|
1102
|
-
"idempotent": {
|
|
1103
|
-
"natural": true
|
|
1058
|
+
"maxAttempts": 1,
|
|
1059
|
+
"baseDelayMs": null,
|
|
1060
|
+
"backoff": null,
|
|
1061
|
+
"retryOn": null
|
|
1104
1062
|
}
|
|
1105
1063
|
},
|
|
1106
1064
|
"MoveColumnRight": {
|
|
1107
1065
|
"retry": {
|
|
1108
|
-
"maxAttempts":
|
|
1109
|
-
"baseDelayMs":
|
|
1110
|
-
"backoff":
|
|
1111
|
-
"retryOn":
|
|
1112
|
-
429,
|
|
1113
|
-
500,
|
|
1114
|
-
503
|
|
1115
|
-
]
|
|
1116
|
-
},
|
|
1117
|
-
"idempotent": {
|
|
1118
|
-
"natural": true
|
|
1066
|
+
"maxAttempts": 1,
|
|
1067
|
+
"baseDelayMs": null,
|
|
1068
|
+
"backoff": null,
|
|
1069
|
+
"retryOn": null
|
|
1119
1070
|
}
|
|
1120
1071
|
},
|
|
1121
1072
|
"RegisterDevice": {
|
|
@@ -20,7 +20,7 @@ module Fizzy
|
|
|
20
20
|
# @param account_id [String] account id ID
|
|
21
21
|
# @param name [String] name
|
|
22
22
|
# @param all_access [Boolean, nil] all access
|
|
23
|
-
# @param auto_postpone_period_in_days [Integer, nil] auto
|
|
23
|
+
# @param auto_postpone_period_in_days [Integer, nil] Days of inactivity before a card is auto-postponed. One of 3, 7, 11, 30, 90 or 365 (Entropy::AUTO_POSTPONE_PERIODS_IN_DAYS); any other value is rejected with 422.
|
|
24
24
|
# @param public_description [String, nil] public description
|
|
25
25
|
# @return [Hash] response data
|
|
26
26
|
def create(account_id:, name:, all_access: nil, auto_postpone_period_in_days: nil, public_description: nil)
|
|
@@ -39,14 +39,14 @@ module Fizzy
|
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
#
|
|
42
|
+
# Updates a board. Send at least one of name, all_access, auto_postpone_period_in_days or public_description: the controller expects a `board` object, so a body without any of them (including one carrying only user_ids) is a 400.
|
|
43
43
|
# @param account_id [String] account id ID
|
|
44
44
|
# @param board_id [String] board id ID
|
|
45
45
|
# @param name [String, nil] name
|
|
46
46
|
# @param all_access [Boolean, nil] all access
|
|
47
|
-
# @param auto_postpone_period_in_days [Integer, nil] auto
|
|
47
|
+
# @param auto_postpone_period_in_days [Integer, nil] Days of inactivity before a card is auto-postponed. One of 3, 7, 11, 30, 90 or 365 (Entropy::AUTO_POSTPONE_PERIODS_IN_DAYS); any other value is rejected with 422.
|
|
48
48
|
# @param public_description [String, nil] public description
|
|
49
|
-
# @param user_ids [Array, nil]
|
|
49
|
+
# @param user_ids [Array, nil] Replaces the board's membership: users listed are granted access and every current member not listed loses it. Omit it to leave access unchanged. It can't be sent alone (see the operation).
|
|
50
50
|
# @return [Hash] response data
|
|
51
51
|
def update(account_id:, board_id:, name: nil, all_access: nil, auto_postpone_period_in_days: nil, public_description: nil, user_ids: nil)
|
|
52
52
|
with_operation(service: "boards", operation: "UpdateBoard", is_mutation: true, resource_id: board_id) do
|
|
@@ -69,11 +69,11 @@ module Fizzy
|
|
|
69
69
|
# @param closer_ids [Array, nil] closer ids
|
|
70
70
|
# @param card_ids [Array, nil] card ids
|
|
71
71
|
# @param column_ids [Array, nil] column ids
|
|
72
|
-
# @param indexed_by [String, nil]
|
|
73
|
-
# @param sorted_by [String, nil]
|
|
74
|
-
# @param assignment_status [String, nil]
|
|
75
|
-
# @param creation [String, nil]
|
|
76
|
-
# @param closure [String, nil]
|
|
72
|
+
# @param indexed_by [String, nil] Which cards to list: "all" (default, open cards), "maybe" (awaiting triage), "closed", "not_now", "stalled", "postponing_soon" or "golden". Any other value lists the same cards as "all".
|
|
73
|
+
# @param sorted_by [String, nil] "latest" (default, most recently active first), "newest" or "oldest" (by creation). Any other value sorts as "latest".
|
|
74
|
+
# @param assignment_status [String, nil] "unassigned" lists only cards with no assignee; any other value is ignored.
|
|
75
|
+
# @param creation [String, nil] Created within: "today", "yesterday", "thisweek", "lastweek", "thismonth", "lastmonth", "thisyear" or "lastyear" (TimeWindowParser); any other value is ignored.
|
|
76
|
+
# @param closure [String, nil] Closed within: "today", "yesterday", "thisweek", "lastweek", "thismonth", "lastmonth", "thisyear" or "lastyear" (TimeWindowParser); any other value is ignored.
|
|
77
77
|
# @param terms [Array, nil] terms
|
|
78
78
|
# @return [Enumerator<Hash>] paginated results
|
|
79
79
|
def list(account_id:, board_ids: nil, tag_ids: nil, assignee_ids: nil, creator_ids: nil, closer_ids: nil, card_ids: nil, column_ids: nil, indexed_by: nil, sorted_by: nil, assignment_status: nil, creation: nil, closure: nil, terms: nil)
|
|
@@ -86,18 +86,15 @@ module Fizzy
|
|
|
86
86
|
# create operation
|
|
87
87
|
# @param account_id [String] account id ID
|
|
88
88
|
# @param title [String] title
|
|
89
|
-
# @param board_id [String
|
|
90
|
-
# @param column_id [String, nil] column id
|
|
89
|
+
# @param board_id [String] The board to create the card on. Required: CardsController finds it unconditionally.
|
|
91
90
|
# @param description [String, nil] description
|
|
92
|
-
# @param
|
|
93
|
-
# @param tag_names [Array, nil] tag names
|
|
94
|
-
# @param image [String, nil] image
|
|
91
|
+
# @param image [String, nil] The signed blob id of an image uploaded with CreateDirectUpload. A caller that can't make the upload has no value to send here.
|
|
95
92
|
# @param created_at [String, nil] created at
|
|
96
93
|
# @param last_active_at [String, nil] last active at
|
|
97
94
|
# @return [Hash] response data
|
|
98
|
-
def create(account_id:, title:, board_id
|
|
95
|
+
def create(account_id:, title:, board_id:, description: nil, image: nil, created_at: nil, last_active_at: nil)
|
|
99
96
|
with_operation(service: "cards", operation: "CreateCard", is_mutation: true, resource_id: account_id) do
|
|
100
|
-
http_post("/#{account_id}/cards.json", body: compact_params(title: title, board_id: board_id,
|
|
97
|
+
http_post("/#{account_id}/cards.json", body: compact_params(title: title, board_id: board_id, description: description, image: image, created_at: created_at, last_active_at: last_active_at)).json
|
|
101
98
|
end
|
|
102
99
|
end
|
|
103
100
|
|
|
@@ -111,18 +108,17 @@ module Fizzy
|
|
|
111
108
|
end
|
|
112
109
|
end
|
|
113
110
|
|
|
114
|
-
#
|
|
111
|
+
# Updates a card. Send at least one of title, description, image or created_at: the controller expects a `card` object, so an empty body is a 400. Moving a card between columns is TriageCard, and between boards MoveCard.
|
|
115
112
|
# @param account_id [String] account id ID
|
|
116
113
|
# @param card_number [Integer] card number ID
|
|
117
114
|
# @param title [String, nil] title
|
|
118
115
|
# @param description [String, nil] description
|
|
119
|
-
# @param
|
|
120
|
-
# @param image [String, nil] image
|
|
116
|
+
# @param image [String, nil] The signed blob id of an image uploaded with CreateDirectUpload. A caller that can't make the upload has no value to send here.
|
|
121
117
|
# @param created_at [String, nil] created at
|
|
122
118
|
# @return [Hash] response data
|
|
123
|
-
def update(account_id:, card_number:, title: nil, description: nil,
|
|
119
|
+
def update(account_id:, card_number:, title: nil, description: nil, image: nil, created_at: nil)
|
|
124
120
|
with_operation(service: "cards", operation: "UpdateCard", is_mutation: true, resource_id: card_number) do
|
|
125
|
-
http_patch("/#{account_id}/cards/#{card_number}", body: compact_params(title: title, description: description,
|
|
121
|
+
http_patch("/#{account_id}/cards/#{card_number}", body: compact_params(title: title, description: description, image: image, created_at: created_at)).json
|
|
126
122
|
end
|
|
127
123
|
end
|
|
128
124
|
|
|
@@ -137,7 +133,7 @@ module Fizzy
|
|
|
137
133
|
end
|
|
138
134
|
end
|
|
139
135
|
|
|
140
|
-
#
|
|
136
|
+
# Assigns the user to the card, or unassigns them if they are already assigned. This is a toggle: calling it again undoes it, and both outcomes return 204, so a retry or a repeat reverses the first call. Check the card first when the outcome matters.
|
|
141
137
|
# @param account_id [String] account id ID
|
|
142
138
|
# @param card_number [Integer] card number ID
|
|
143
139
|
# @param assignee_id [String] assignee id
|
|
@@ -152,12 +148,11 @@ module Fizzy
|
|
|
152
148
|
# move operation
|
|
153
149
|
# @param account_id [String] account id ID
|
|
154
150
|
# @param card_number [Integer] card number ID
|
|
155
|
-
# @param board_id [String] board
|
|
156
|
-
# @param column_id [String, nil] column id
|
|
151
|
+
# @param board_id [String] The destination board. Cards::BoardsController reads no column.
|
|
157
152
|
# @return [Hash] response data
|
|
158
|
-
def move(account_id:, card_number:, board_id
|
|
153
|
+
def move(account_id:, card_number:, board_id:)
|
|
159
154
|
with_operation(service: "cards", operation: "MoveCard", is_mutation: true, resource_id: card_number) do
|
|
160
|
-
http_patch("/#{account_id}/cards/#{card_number}/board.json", body: compact_params(board_id: board_id
|
|
155
|
+
http_patch("/#{account_id}/cards/#{card_number}/board.json", body: compact_params(board_id: board_id)).json
|
|
161
156
|
end
|
|
162
157
|
end
|
|
163
158
|
|
|
@@ -216,13 +211,13 @@ module Fizzy
|
|
|
216
211
|
end
|
|
217
212
|
end
|
|
218
213
|
|
|
219
|
-
#
|
|
214
|
+
# Moves the card to Not Now. Each call records an event, which notifies webhooks and adds a system comment to the card, even when the card's state doesn't change, so it is not safe to retry.
|
|
220
215
|
# @param account_id [String] account id ID
|
|
221
216
|
# @param card_number [Integer] card number ID
|
|
222
217
|
# @return [void]
|
|
223
218
|
def postpone(account_id:, card_number:)
|
|
224
219
|
with_operation(service: "cards", operation: "PostponeCard", is_mutation: true, resource_id: card_number) do
|
|
225
|
-
http_post("/#{account_id}/cards/#{card_number}/not_now.json"
|
|
220
|
+
http_post("/#{account_id}/cards/#{card_number}/not_now.json")
|
|
226
221
|
nil
|
|
227
222
|
end
|
|
228
223
|
end
|
|
@@ -238,13 +233,13 @@ module Fizzy
|
|
|
238
233
|
end
|
|
239
234
|
end
|
|
240
235
|
|
|
241
|
-
#
|
|
236
|
+
# Unpins the card for the caller. Unpinning a card the caller hasn't pinned currently fails with a 500 (Card::Pinnable#unpin_by destroys a pin it didn't find), so a retry after a success fails: not idempotent.
|
|
242
237
|
# @param account_id [String] account id ID
|
|
243
238
|
# @param card_number [Integer] card number ID
|
|
244
239
|
# @return [void]
|
|
245
240
|
def unpin(account_id:, card_number:)
|
|
246
241
|
with_operation(service: "cards", operation: "UnpinCard", is_mutation: true, resource_id: card_number) do
|
|
247
|
-
http_delete("/#{account_id}/cards/#{card_number}/pin.json")
|
|
242
|
+
http_delete("/#{account_id}/cards/#{card_number}/pin.json", retryable: false)
|
|
248
243
|
nil
|
|
249
244
|
end
|
|
250
245
|
end
|
|
@@ -260,7 +255,7 @@ module Fizzy
|
|
|
260
255
|
end
|
|
261
256
|
end
|
|
262
257
|
|
|
263
|
-
#
|
|
258
|
+
# Assigns the caller to the card, or unassigns them if they are already assigned. This is a toggle: calling it again undoes it, and both outcomes return 204, so a retry or a repeat reverses the first call. Check the card first when the outcome matters.
|
|
264
259
|
# @param account_id [String] account id ID
|
|
265
260
|
# @param card_number [Integer] card number ID
|
|
266
261
|
# @return [void]
|
|
@@ -271,10 +266,10 @@ module Fizzy
|
|
|
271
266
|
end
|
|
272
267
|
end
|
|
273
268
|
|
|
274
|
-
# tag
|
|
269
|
+
# Tags the card, or untags it if it already has the tag. The tag is created when the account has none with that title. This is a toggle: calling it again undoes it, and both outcomes return 204, so a retry or a repeat reverses the first call. Check the card first when the outcome matters.
|
|
275
270
|
# @param account_id [String] account id ID
|
|
276
271
|
# @param card_number [Integer] card number ID
|
|
277
|
-
# @param tag_title [String] tag title
|
|
272
|
+
# @param tag_title [String] The tag's title; surrounding whitespace and a leading "#" are stripped.
|
|
278
273
|
# @return [void]
|
|
279
274
|
def tag(account_id:, card_number:, tag_title:)
|
|
280
275
|
with_operation(service: "cards", operation: "TagCard", is_mutation: true, resource_id: card_number) do
|
|
@@ -283,25 +278,25 @@ module Fizzy
|
|
|
283
278
|
end
|
|
284
279
|
end
|
|
285
280
|
|
|
286
|
-
#
|
|
281
|
+
# Triages the card into a column of its board. Each call records an event, which notifies webhooks and adds a system comment to the card, even when the card's state doesn't change, so it is not safe to retry.
|
|
287
282
|
# @param account_id [String] account id ID
|
|
288
283
|
# @param card_number [Integer] card number ID
|
|
289
|
-
# @param column_id [String
|
|
284
|
+
# @param column_id [String] A column on the card's board. Required: the controller finds it unconditionally.
|
|
290
285
|
# @return [void]
|
|
291
|
-
def triage(account_id:, card_number:, column_id:
|
|
286
|
+
def triage(account_id:, card_number:, column_id:)
|
|
292
287
|
with_operation(service: "cards", operation: "TriageCard", is_mutation: true, resource_id: card_number) do
|
|
293
|
-
http_post("/#{account_id}/cards/#{card_number}/triage.json", body: compact_params(column_id: column_id)
|
|
288
|
+
http_post("/#{account_id}/cards/#{card_number}/triage.json", body: compact_params(column_id: column_id))
|
|
294
289
|
nil
|
|
295
290
|
end
|
|
296
291
|
end
|
|
297
292
|
|
|
298
|
-
#
|
|
293
|
+
# Sends the card back to triage (Maybe?). Each call records an event, which notifies webhooks and adds a system comment to the card, even when the card's state doesn't change, so it is not safe to retry.
|
|
299
294
|
# @param account_id [String] account id ID
|
|
300
295
|
# @param card_number [Integer] card number ID
|
|
301
296
|
# @return [void]
|
|
302
297
|
def untriage(account_id:, card_number:)
|
|
303
298
|
with_operation(service: "cards", operation: "UnTriageCard", is_mutation: true, resource_id: card_number) do
|
|
304
|
-
http_delete("/#{account_id}/cards/#{card_number}/triage.json")
|
|
299
|
+
http_delete("/#{account_id}/cards/#{card_number}/triage.json", retryable: false)
|
|
305
300
|
nil
|
|
306
301
|
end
|
|
307
302
|
end
|
|
@@ -21,7 +21,7 @@ module Fizzy
|
|
|
21
21
|
# @param account_id [String] account id ID
|
|
22
22
|
# @param board_id [String] board id ID
|
|
23
23
|
# @param name [String] name
|
|
24
|
-
# @param color [String, nil] color
|
|
24
|
+
# @param color [String, nil] The column color, as its CSS value: "var(--color-card-default)" (Blue), "var(--color-card-1)" (Gray), "var(--color-card-2)" (Tan), "var(--color-card-3)" (Yellow), "var(--color-card-4)" (Lime), "var(--color-card-5)" (Aqua), "var(--color-card-6)" (Violet), "var(--color-card-7)" (Purple) or "var(--color-card-8)" (Pink) (Color::COLORS). Any other value is stored but reads back as Blue.
|
|
25
25
|
# @return [Hash] response data
|
|
26
26
|
def create(account_id:, board_id:, name:, color: nil)
|
|
27
27
|
with_operation(service: "columns", operation: "CreateColumn", is_mutation: true, resource_id: board_id) do
|
|
@@ -40,12 +40,12 @@ module Fizzy
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
#
|
|
43
|
+
# Updates a column. Send at least one of name or color: the controller expects a `column` object, so an empty body is a 400.
|
|
44
44
|
# @param account_id [String] account id ID
|
|
45
45
|
# @param board_id [String] board id ID
|
|
46
46
|
# @param column_id [String] column id ID
|
|
47
47
|
# @param name [String, nil] name
|
|
48
|
-
# @param color [String, nil] color
|
|
48
|
+
# @param color [String, nil] The column color, as its CSS value: "var(--color-card-default)" (Blue), "var(--color-card-1)" (Gray), "var(--color-card-2)" (Tan), "var(--color-card-3)" (Yellow), "var(--color-card-4)" (Lime), "var(--color-card-5)" (Aqua), "var(--color-card-6)" (Violet), "var(--color-card-7)" (Purple) or "var(--color-card-8)" (Pink) (Color::COLORS). Any other value is stored but reads back as Blue.
|
|
49
49
|
# @return [Hash] response data
|
|
50
50
|
def update(account_id:, board_id:, column_id:, name: nil, color: nil)
|
|
51
51
|
with_operation(service: "columns", operation: "UpdateColumn", is_mutation: true, resource_id: column_id) do
|
|
@@ -15,9 +15,9 @@ module Fizzy
|
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
#
|
|
18
|
+
# Sets the caller's time zone. The name isn't validated: one that ActiveSupport::TimeZone doesn't recognize is stored, succeeds with 204, and reads back as UTC.
|
|
19
19
|
# @param account_id [String] account id ID
|
|
20
|
-
# @param timezone_name [String]
|
|
20
|
+
# @param timezone_name [String] An IANA or Rails time zone name, such as "America/Chicago" or "Central Time (US & Canada)".
|
|
21
21
|
# @return [void]
|
|
22
22
|
def update_timezone(account_id:, timezone_name:)
|
|
23
23
|
with_operation(service: "identity", operation: "UpdateMyTimezone", is_mutation: true, resource_id: account_id) do
|
|
@@ -37,9 +37,9 @@ module Fizzy
|
|
|
37
37
|
|
|
38
38
|
# update_account_entropy operation
|
|
39
39
|
# @param account_id [String] account id ID
|
|
40
|
-
# @param auto_postpone_period_in_days [Integer
|
|
40
|
+
# @param auto_postpone_period_in_days [Integer] Days of inactivity before a card is auto-postponed. One of 3, 7, 11, 30, 90 or 365 (Entropy::AUTO_POSTPONE_PERIODS_IN_DAYS); any other value is rejected with 422. Required: an empty body is a 400.
|
|
41
41
|
# @return [Hash] response data
|
|
42
|
-
def update_account_entropy(account_id:, auto_postpone_period_in_days:
|
|
42
|
+
def update_account_entropy(account_id:, auto_postpone_period_in_days:)
|
|
43
43
|
with_operation(service: "miscellaneous", operation: "UpdateAccountEntropy", is_mutation: true, resource_id: account_id) do
|
|
44
44
|
http_put("/#{account_id}/account/entropy.json", body: compact_params(auto_postpone_period_in_days: auto_postpone_period_in_days)).json
|
|
45
45
|
end
|
|
@@ -75,21 +75,21 @@ module Fizzy
|
|
|
75
75
|
|
|
76
76
|
# update_join_code operation
|
|
77
77
|
# @param account_id [String] account id ID
|
|
78
|
-
# @param usage_limit [Integer,
|
|
78
|
+
# @param usage_limit [Integer] How many times the join code can be redeemed. Fizzy accepts up to 10,000,000,000, but this 32-bit field holds at most 2,147,483,647. Required: an empty body is a 400.
|
|
79
79
|
# @return [void]
|
|
80
|
-
def update_join_code(account_id:, usage_limit:
|
|
80
|
+
def update_join_code(account_id:, usage_limit:)
|
|
81
81
|
with_operation(service: "miscellaneous", operation: "UpdateJoinCode", is_mutation: true, resource_id: account_id) do
|
|
82
82
|
http_patch("/#{account_id}/account/join_code.json", body: compact_params(usage_limit: usage_limit))
|
|
83
83
|
nil
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
#
|
|
87
|
+
# Replaces the join code with a newly generated one and resets its usage count. Each call mints a different code and invalidates the previous one, so a retry invalidates the code the first call issued: not idempotent.
|
|
88
88
|
# @param account_id [String] account id ID
|
|
89
89
|
# @return [void]
|
|
90
90
|
def reset_join_code(account_id:)
|
|
91
91
|
with_operation(service: "miscellaneous", operation: "ResetJoinCode", is_mutation: true, resource_id: account_id) do
|
|
92
|
-
http_delete("/#{account_id}/account/join_code.json")
|
|
92
|
+
http_delete("/#{account_id}/account/join_code.json", retryable: false)
|
|
93
93
|
nil
|
|
94
94
|
end
|
|
95
95
|
end
|
|
@@ -105,9 +105,9 @@ module Fizzy
|
|
|
105
105
|
|
|
106
106
|
# update_account_settings operation
|
|
107
107
|
# @param account_id [String] account id ID
|
|
108
|
-
# @param name [String,
|
|
108
|
+
# @param name [String] The account name. Required: the controller expects an `account` object, so an empty body is a 400.
|
|
109
109
|
# @return [void]
|
|
110
|
-
def update_account_settings(account_id:, name:
|
|
110
|
+
def update_account_settings(account_id:, name:)
|
|
111
111
|
with_operation(service: "miscellaneous", operation: "UpdateAccountSettings", is_mutation: true, resource_id: account_id) do
|
|
112
112
|
http_patch("/#{account_id}/account/settings.json", body: compact_params(name: name))
|
|
113
113
|
nil
|
|
@@ -117,9 +117,9 @@ module Fizzy
|
|
|
117
117
|
# update_board_entropy operation
|
|
118
118
|
# @param account_id [String] account id ID
|
|
119
119
|
# @param board_id [String] board id ID
|
|
120
|
-
# @param auto_postpone_period_in_days [Integer
|
|
120
|
+
# @param auto_postpone_period_in_days [Integer] Days of inactivity before a card is auto-postponed. One of 3, 7, 11, 30, 90 or 365 (Entropy::AUTO_POSTPONE_PERIODS_IN_DAYS); any other value is rejected with 422. Required: an empty body is a 400.
|
|
121
121
|
# @return [Hash] response data
|
|
122
|
-
def update_board_entropy(account_id:, board_id:, auto_postpone_period_in_days:
|
|
122
|
+
def update_board_entropy(account_id:, board_id:, auto_postpone_period_in_days:)
|
|
123
123
|
with_operation(service: "miscellaneous", operation: "UpdateBoardEntropy", is_mutation: true, resource_id: board_id) do
|
|
124
124
|
http_put("/#{account_id}/boards/#{board_id}/entropy.json", body: compact_params(auto_postpone_period_in_days: auto_postpone_period_in_days)).json
|
|
125
125
|
end
|
|
@@ -128,16 +128,16 @@ module Fizzy
|
|
|
128
128
|
# update_board_involvement operation
|
|
129
129
|
# @param account_id [String] account id ID
|
|
130
130
|
# @param board_id [String] board id ID
|
|
131
|
-
# @param involvement [String
|
|
131
|
+
# @param involvement [String] The caller's involvement with the board: "access_only" or "watching" (Access#involvement). Required: the controller assigns it unconditionally.
|
|
132
132
|
# @return [void]
|
|
133
|
-
def update_board_involvement(account_id:, board_id:, involvement:
|
|
133
|
+
def update_board_involvement(account_id:, board_id:, involvement:)
|
|
134
134
|
with_operation(service: "miscellaneous", operation: "UpdateBoardInvolvement", is_mutation: true, resource_id: board_id) do
|
|
135
135
|
http_patch("/#{account_id}/boards/#{board_id}/involvement.json", body: compact_params(involvement: involvement))
|
|
136
136
|
nil
|
|
137
137
|
end
|
|
138
138
|
end
|
|
139
139
|
|
|
140
|
-
#
|
|
140
|
+
# Marks the caller's notification for the card read. When the caller has no notification for the card, the call succeeds without changing anything.
|
|
141
141
|
# @param account_id [String] account id ID
|
|
142
142
|
# @param card_number [Integer] card number ID
|
|
143
143
|
# @return [void]
|
|
@@ -148,7 +148,7 @@ module Fizzy
|
|
|
148
148
|
end
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
-
#
|
|
151
|
+
# Marks the caller's notification for the card unread. When the caller has no notification for the card, the call succeeds without changing anything.
|
|
152
152
|
# @param account_id [String] account id ID
|
|
153
153
|
# @param card_number [Integer] card number ID
|
|
154
154
|
# @return [void]
|
|
@@ -159,24 +159,24 @@ module Fizzy
|
|
|
159
159
|
end
|
|
160
160
|
end
|
|
161
161
|
|
|
162
|
-
#
|
|
162
|
+
# Swaps the column with its left neighbor. Each call moves it again, so it is not idempotent; at the leftmost edge there is no neighbor and the call succeeds without changing anything.
|
|
163
163
|
# @param account_id [String] account id ID
|
|
164
164
|
# @param column_id [String] column id ID
|
|
165
165
|
# @return [void]
|
|
166
166
|
def move_column_left(account_id:, column_id:)
|
|
167
167
|
with_operation(service: "miscellaneous", operation: "MoveColumnLeft", is_mutation: true, resource_id: column_id) do
|
|
168
|
-
http_post("/#{account_id}/columns/#{column_id}/left_position.json"
|
|
168
|
+
http_post("/#{account_id}/columns/#{column_id}/left_position.json")
|
|
169
169
|
nil
|
|
170
170
|
end
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
-
#
|
|
173
|
+
# Swaps the column with its right neighbor. Each call moves it again, so it is not idempotent; at the rightmost edge there is no neighbor and the call succeeds without changing anything.
|
|
174
174
|
# @param account_id [String] account id ID
|
|
175
175
|
# @param column_id [String] column id ID
|
|
176
176
|
# @return [void]
|
|
177
177
|
def move_column_right(account_id:, column_id:)
|
|
178
178
|
with_operation(service: "miscellaneous", operation: "MoveColumnRight", is_mutation: true, resource_id: column_id) do
|
|
179
|
-
http_post("/#{account_id}/columns/#{column_id}/right_position.json"
|
|
179
|
+
http_post("/#{account_id}/columns/#{column_id}/right_position.json")
|
|
180
180
|
nil
|
|
181
181
|
end
|
|
182
182
|
end
|
|
@@ -192,9 +192,9 @@ module Fizzy
|
|
|
192
192
|
|
|
193
193
|
# update_notification_settings operation
|
|
194
194
|
# @param account_id [String] account id ID
|
|
195
|
-
# @param bundle_email_frequency [String,
|
|
195
|
+
# @param bundle_email_frequency [String] "never", "every_few_hours", "daily" or "weekly" (User::Settings#bundle_email_frequency). Required: an empty body is a 400.
|
|
196
196
|
# @return [void]
|
|
197
|
-
def update_notification_settings(account_id:, bundle_email_frequency:
|
|
197
|
+
def update_notification_settings(account_id:, bundle_email_frequency:)
|
|
198
198
|
with_operation(service: "miscellaneous", operation: "UpdateNotificationSettings", is_mutation: true, resource_id: account_id) do
|
|
199
199
|
http_patch("/#{account_id}/notifications/settings.json", body: compact_params(bundle_email_frequency: bundle_email_frequency))
|
|
200
200
|
nil
|
|
@@ -241,7 +241,7 @@ module Fizzy
|
|
|
241
241
|
# update_user_role operation
|
|
242
242
|
# @param account_id [String] account id ID
|
|
243
243
|
# @param user_id [String] user id ID
|
|
244
|
-
# @param role [String] role
|
|
244
|
+
# @param role [String] "member" or "admin". Any other value, including "owner", sets the role to "member" (Users::RolesController#role_params), so a typo demotes.
|
|
245
245
|
# @return [void]
|
|
246
246
|
def update_user_role(account_id:, user_id:, role:)
|
|
247
247
|
with_operation(service: "miscellaneous", operation: "UpdateUserRole", is_mutation: true, resource_id: user_id) do
|
|
@@ -7,20 +7,18 @@ module Fizzy
|
|
|
7
7
|
# @generated from OpenAPI spec
|
|
8
8
|
class NotificationsService < BaseService
|
|
9
9
|
|
|
10
|
-
#
|
|
10
|
+
# Lists the caller's notifications. It takes no read/unread filter: the first page holds up to 100 unread notifications followed by a page of read ones, and later pages hold read ones only.
|
|
11
11
|
# @param account_id [String] account id ID
|
|
12
|
-
# @param read [Boolean, nil] read
|
|
13
12
|
# @return [Enumerator<Hash>] paginated results
|
|
14
|
-
def list(account_id
|
|
13
|
+
def list(account_id:)
|
|
15
14
|
wrap_paginated(service: "notifications", operation: "ListNotifications", is_mutation: false, resource_id: account_id) do
|
|
16
|
-
|
|
17
|
-
paginate("/#{account_id}/notifications.json", params: params)
|
|
15
|
+
paginate("/#{account_id}/notifications.json")
|
|
18
16
|
end
|
|
19
17
|
end
|
|
20
18
|
|
|
21
|
-
#
|
|
19
|
+
# Marks every unread notification of the caller read. It takes no selection.
|
|
22
20
|
# @param account_id [String] account id ID
|
|
23
|
-
# @param notification_ids [Array, nil] notification
|
|
21
|
+
# @param notification_ids [Array, nil] Ignored: Notifications::BulkReadingsController reads no ids and marks every unread notification read whatever this holds.
|
|
24
22
|
# @return [void]
|
|
25
23
|
def bulk_read(account_id:, notification_ids: nil)
|
|
26
24
|
with_operation(service: "notifications", operation: "BulkReadNotifications", is_mutation: true, resource_id: account_id) do
|
|
@@ -7,7 +7,7 @@ module Fizzy
|
|
|
7
7
|
# @generated from OpenAPI spec
|
|
8
8
|
class SessionsService < BaseService
|
|
9
9
|
|
|
10
|
-
#
|
|
10
|
+
# Starts a magic-link sign-in and answers the pending authentication token.
|
|
11
11
|
# @param email_address [String] email address
|
|
12
12
|
# @return [Hash] response data
|
|
13
13
|
def create(email_address:)
|
|
@@ -25,12 +25,12 @@ module Fizzy
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
# @param
|
|
28
|
+
# Redeems the code from a magic-link email for a session token.
|
|
29
|
+
# @param code [String] The code from the magic-link email.
|
|
30
30
|
# @return [Hash] response data
|
|
31
|
-
def redeem_magic_link(
|
|
31
|
+
def redeem_magic_link(code:)
|
|
32
32
|
with_operation(service: "sessions", operation: "RedeemMagicLink", is_mutation: true) do
|
|
33
|
-
http_post("/session/magic_link.json", body: compact_params(
|
|
33
|
+
http_post("/session/magic_link.json", body: compact_params(code: code)).json
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -40,7 +40,7 @@ module Fizzy
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
#
|
|
43
|
+
# Updates a step. Send at least one of content or completed: the controller expects a `step` object, so an empty body is a 400.
|
|
44
44
|
# @param account_id [String] account id ID
|
|
45
45
|
# @param card_number [Integer] card number ID
|
|
46
46
|
# @param step_id [String] step id ID
|
|
@@ -29,9 +29,9 @@ module Fizzy
|
|
|
29
29
|
# update operation
|
|
30
30
|
# @param account_id [String] account id ID
|
|
31
31
|
# @param user_id [String] user id ID
|
|
32
|
-
# @param name [String,
|
|
32
|
+
# @param name [String] Required: the controller expects a `user` object, so an empty body is a 400.
|
|
33
33
|
# @return [Hash] response data
|
|
34
|
-
def update(account_id:, user_id:, name:
|
|
34
|
+
def update(account_id:, user_id:, name:)
|
|
35
35
|
with_operation(service: "users", operation: "UpdateUser", is_mutation: true, resource_id: user_id) do
|
|
36
36
|
http_patch("/#{account_id}/users/#{user_id}", body: compact_params(name: name)).json
|
|
37
37
|
end
|
|
@@ -7,7 +7,7 @@ module Fizzy
|
|
|
7
7
|
# @generated from OpenAPI spec
|
|
8
8
|
class WebhooksService < BaseService
|
|
9
9
|
|
|
10
|
-
# list operation
|
|
10
|
+
# Lists the board's webhooks. Fizzy pages this list (a Link header with rel="next"); this operation returns the first page.
|
|
11
11
|
# @param account_id [String] account id ID
|
|
12
12
|
# @param board_id [String] board id ID
|
|
13
13
|
# @return [Hash] response data
|
|
@@ -22,7 +22,7 @@ module Fizzy
|
|
|
22
22
|
# @param board_id [String] board id ID
|
|
23
23
|
# @param name [String] name
|
|
24
24
|
# @param url [String] url
|
|
25
|
-
# @param subscribed_actions [Array, nil]
|
|
25
|
+
# @param subscribed_actions [Array, nil] The events to deliver, from: "card_assigned", "card_closed", "card_postponed", "card_auto_postponed", "card_board_changed", "card_published", "card_reopened", "card_sent_back_to_triage", "card_triaged", "card_unassigned" and "comment_created" (Webhook::PERMITTED_ACTIONS). Unknown actions are dropped silently. A webhook created without any receives no deliveries.
|
|
26
26
|
# @return [Hash] response data
|
|
27
27
|
def create(account_id:, board_id:, name:, url:, subscribed_actions: nil)
|
|
28
28
|
with_operation(service: "webhooks", operation: "CreateWebhook", is_mutation: true, resource_id: board_id) do
|
|
@@ -41,17 +41,16 @@ module Fizzy
|
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
#
|
|
44
|
+
# Updates a webhook's name or subscribed actions. Send at least one: the controller expects a `webhook` object, so an empty body is a 400. The URL can't change after creation; delete the webhook and create a new one.
|
|
45
45
|
# @param account_id [String] account id ID
|
|
46
46
|
# @param board_id [String] board id ID
|
|
47
47
|
# @param webhook_id [String] webhook id ID
|
|
48
48
|
# @param name [String, nil] name
|
|
49
|
-
# @param
|
|
50
|
-
# @param subscribed_actions [Array, nil] subscribed actions
|
|
49
|
+
# @param subscribed_actions [Array, nil] The events to deliver, from: "card_assigned", "card_closed", "card_postponed", "card_auto_postponed", "card_board_changed", "card_published", "card_reopened", "card_sent_back_to_triage", "card_triaged", "card_unassigned" and "comment_created" (Webhook::PERMITTED_ACTIONS). Unknown actions are dropped silently. The list replaces the current subscription.
|
|
51
50
|
# @return [Hash] response data
|
|
52
|
-
def update(account_id:, board_id:, webhook_id:, name: nil,
|
|
51
|
+
def update(account_id:, board_id:, webhook_id:, name: nil, subscribed_actions: nil)
|
|
53
52
|
with_operation(service: "webhooks", operation: "UpdateWebhook", is_mutation: true, resource_id: webhook_id) do
|
|
54
|
-
http_patch("/#{account_id}/boards/#{board_id}/webhooks/#{webhook_id}", body: compact_params(name: name,
|
|
53
|
+
http_patch("/#{account_id}/boards/#{board_id}/webhooks/#{webhook_id}", body: compact_params(name: name, subscribed_actions: subscribed_actions)).json
|
|
55
54
|
end
|
|
56
55
|
end
|
|
57
56
|
|
|
@@ -365,16 +365,13 @@ module Fizzy
|
|
|
365
365
|
CreateCardReactionResponseContent = Data.define
|
|
366
366
|
|
|
367
367
|
# @generated
|
|
368
|
-
CreateCardRequestContent = Data.define(:title, :board_id, :
|
|
368
|
+
CreateCardRequestContent = Data.define(:title, :board_id, :description, :image, :created_at, :last_active_at) do
|
|
369
369
|
# @param data [Hash] raw JSON response
|
|
370
370
|
def self.from_json(data)
|
|
371
371
|
new(
|
|
372
372
|
title: data["title"],
|
|
373
373
|
board_id: data["board_id"],
|
|
374
|
-
column_id: data["column_id"],
|
|
375
374
|
description: data["description"],
|
|
376
|
-
assignee_ids: data["assignee_ids"],
|
|
377
|
-
tag_names: data["tag_names"],
|
|
378
375
|
image: data["image"],
|
|
379
376
|
created_at: data["created_at"],
|
|
380
377
|
last_active_at: data["last_active_at"]
|
|
@@ -700,12 +697,11 @@ module Fizzy
|
|
|
700
697
|
ListWebhooksResponseContent = Data.define
|
|
701
698
|
|
|
702
699
|
# @generated
|
|
703
|
-
MoveCardRequestContent = Data.define(:board_id
|
|
700
|
+
MoveCardRequestContent = Data.define(:board_id) do
|
|
704
701
|
# @param data [Hash] raw JSON response
|
|
705
702
|
def self.from_json(data)
|
|
706
703
|
new(
|
|
707
|
-
board_id: data["board_id"]
|
|
708
|
-
column_id: data["column_id"]
|
|
704
|
+
board_id: data["board_id"]
|
|
709
705
|
)
|
|
710
706
|
end
|
|
711
707
|
end
|
|
@@ -805,11 +801,11 @@ module Fizzy
|
|
|
805
801
|
end
|
|
806
802
|
|
|
807
803
|
# @generated
|
|
808
|
-
RedeemMagicLinkRequestContent = Data.define(:
|
|
804
|
+
RedeemMagicLinkRequestContent = Data.define(:code) do
|
|
809
805
|
# @param data [Hash] raw JSON response
|
|
810
806
|
def self.from_json(data)
|
|
811
807
|
new(
|
|
812
|
-
|
|
808
|
+
code: data["code"]
|
|
813
809
|
)
|
|
814
810
|
end
|
|
815
811
|
end
|
|
@@ -986,13 +982,12 @@ module Fizzy
|
|
|
986
982
|
UpdateBoardResponseContent = Data.define
|
|
987
983
|
|
|
988
984
|
# @generated
|
|
989
|
-
UpdateCardRequestContent = Data.define(:title, :description, :
|
|
985
|
+
UpdateCardRequestContent = Data.define(:title, :description, :image, :created_at) do
|
|
990
986
|
# @param data [Hash] raw JSON response
|
|
991
987
|
def self.from_json(data)
|
|
992
988
|
new(
|
|
993
989
|
title: data["title"],
|
|
994
990
|
description: data["description"],
|
|
995
|
-
column_id: data["column_id"],
|
|
996
991
|
image: data["image"],
|
|
997
992
|
created_at: data["created_at"]
|
|
998
993
|
)
|
|
@@ -1097,12 +1092,11 @@ module Fizzy
|
|
|
1097
1092
|
end
|
|
1098
1093
|
|
|
1099
1094
|
# @generated
|
|
1100
|
-
UpdateWebhookRequestContent = Data.define(:name, :
|
|
1095
|
+
UpdateWebhookRequestContent = Data.define(:name, :subscribed_actions) do
|
|
1101
1096
|
# @param data [Hash] raw JSON response
|
|
1102
1097
|
def self.from_json(data)
|
|
1103
1098
|
new(
|
|
1104
1099
|
name: data["name"],
|
|
1105
|
-
url: data["url"],
|
|
1106
1100
|
subscribed_actions: data["subscribed_actions"]
|
|
1107
1101
|
)
|
|
1108
1102
|
end
|
data/lib/fizzy/http.rb
CHANGED
|
@@ -160,12 +160,15 @@ module Fizzy
|
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
def request_with_retry(method, url, params: {}, body: nil)
|
|
163
|
+
# max_retries counts total attempts and is floored at one: whether a
|
|
164
|
+
# request reaches the wire at all must not depend on the retry budget.
|
|
165
|
+
max_attempts = [ @config.max_retries, 1 ].max
|
|
163
166
|
attempt = 0
|
|
164
167
|
last_error = nil
|
|
165
168
|
|
|
166
169
|
loop do
|
|
167
170
|
attempt += 1
|
|
168
|
-
break if attempt >
|
|
171
|
+
break if attempt > max_attempts
|
|
169
172
|
|
|
170
173
|
begin
|
|
171
174
|
return single_request(method, url, params: params, body: body, attempt: attempt)
|
|
@@ -175,7 +178,7 @@ module Fizzy
|
|
|
175
178
|
last_error = e
|
|
176
179
|
|
|
177
180
|
# Don't sleep if this was the last attempt
|
|
178
|
-
break if attempt >=
|
|
181
|
+
break if attempt >= max_attempts
|
|
179
182
|
|
|
180
183
|
delay = calculate_delay(attempt, e.retry_after)
|
|
181
184
|
|
|
@@ -187,7 +190,8 @@ module Fizzy
|
|
|
187
190
|
end
|
|
188
191
|
end
|
|
189
192
|
|
|
190
|
-
|
|
193
|
+
noun = max_attempts == 1 ? "attempt" : "attempts"
|
|
194
|
+
raise last_error || Fizzy::APIError.new("Request failed after #{max_attempts} #{noun}")
|
|
191
195
|
end
|
|
192
196
|
|
|
193
197
|
def single_request(method, url, params:, body:, attempt:, retry_count: 0)
|
|
@@ -210,6 +214,10 @@ module Fizzy
|
|
|
210
214
|
status: response.status,
|
|
211
215
|
headers: response.headers
|
|
212
216
|
)
|
|
217
|
+
rescue Faraday::TimeoutError => e
|
|
218
|
+
# Faraday::TimeoutError < Faraday::ServerError: named before the status
|
|
219
|
+
# clause, or a stalled read classifies as a status-less api_error.
|
|
220
|
+
transport_failure(e, info: info, start_time: start_time)
|
|
213
221
|
rescue Faraday::ServerError, Faraday::ClientError => e
|
|
214
222
|
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
|
215
223
|
error = handle_error(e)
|
|
@@ -229,11 +237,7 @@ module Fizzy
|
|
|
229
237
|
|
|
230
238
|
raise error
|
|
231
239
|
rescue Faraday::Error => e
|
|
232
|
-
|
|
233
|
-
error = Fizzy::NetworkError.new("Connection failed", cause: e)
|
|
234
|
-
result = RequestResult.new(duration: duration, error: error)
|
|
235
|
-
@hooks.on_request_end(info, result)
|
|
236
|
-
raise error
|
|
240
|
+
transport_failure(e, info: info, start_time: start_time)
|
|
237
241
|
end
|
|
238
242
|
end
|
|
239
243
|
|
|
@@ -265,6 +269,8 @@ module Fizzy
|
|
|
265
269
|
status: response.status,
|
|
266
270
|
headers: response.headers
|
|
267
271
|
)
|
|
272
|
+
rescue Faraday::TimeoutError => e
|
|
273
|
+
transport_failure(e, info: info, start_time: start_time)
|
|
268
274
|
rescue Faraday::ServerError, Faraday::ClientError => e
|
|
269
275
|
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
|
270
276
|
error = handle_error(e)
|
|
@@ -277,14 +283,21 @@ module Fizzy
|
|
|
277
283
|
@hooks.on_request_end(info, result)
|
|
278
284
|
raise error
|
|
279
285
|
rescue Faraday::Error => e
|
|
280
|
-
|
|
281
|
-
error = Fizzy::NetworkError.new("Connection failed", cause: e)
|
|
282
|
-
result = RequestResult.new(duration: duration, error: error)
|
|
283
|
-
@hooks.on_request_end(info, result)
|
|
284
|
-
raise error
|
|
286
|
+
transport_failure(e, info: info, start_time: start_time)
|
|
285
287
|
end
|
|
286
288
|
end
|
|
287
289
|
|
|
290
|
+
# A transport failure — no HTTP response, so no status to map — as the
|
|
291
|
+
# network error the caller and on_request_end both see: a timeout by name,
|
|
292
|
+
# anything else as a connection failure, both retryable.
|
|
293
|
+
def transport_failure(exception, info:, start_time:)
|
|
294
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
|
295
|
+
message = exception.is_a?(Faraday::TimeoutError) ? "Request timed out" : "Connection failed"
|
|
296
|
+
error = Fizzy::NetworkError.new(message, cause: exception)
|
|
297
|
+
@hooks.on_request_end(info, RequestResult.new(duration: duration, error: error))
|
|
298
|
+
raise error
|
|
299
|
+
end
|
|
300
|
+
|
|
288
301
|
def handle_error(error)
|
|
289
302
|
status = error.response&.dig(:status)
|
|
290
303
|
body = error.response&.dig(:body)
|
|
@@ -1,56 +1,120 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Fizzy
|
|
4
|
-
#
|
|
4
|
+
# Signs in without a password. Fizzy emails a code to an address and, once the person types
|
|
5
|
+
# it back, hands out a session token for {CookieAuth}.
|
|
5
6
|
#
|
|
6
|
-
# The
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# The two steps are one exchange as far as Fizzy is concerned: creating the session sets a
|
|
8
|
+
# +pending_authentication_token+ cookie naming the address the code went to, and redeeming
|
|
9
|
+
# the code has to send that cookie back or the code is refused. The flow keeps the token
|
|
10
|
+
# between the calls, and hands it out for a caller that finishes in another process (a CLI
|
|
11
|
+
# that prompts for the code on its next run), which picks it up again with +pending_token:+.
|
|
9
12
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
13
|
+
# Both requests go through the generated {Services::SessionsService}, so their paths and
|
|
14
|
+
# bodies are the model's: +POST /session.json+ with +email_address+, then
|
|
15
|
+
# +POST /session/magic_link.json+ with +code+.
|
|
12
16
|
#
|
|
13
17
|
# @example
|
|
14
|
-
# flow = Fizzy::MagicLinkFlow.new
|
|
15
|
-
# flow.
|
|
16
|
-
# #
|
|
17
|
-
# session = flow.redeem(
|
|
18
|
-
# # Use session token for authenticated requests
|
|
18
|
+
# flow = Fizzy::MagicLinkFlow.new
|
|
19
|
+
# flow.create_session(email_address: "jane@example.com")
|
|
20
|
+
# # ... the person reads the code out of their email ...
|
|
21
|
+
# session = flow.redeem(code: "ABC123")
|
|
19
22
|
# client = Fizzy.client(auth: Fizzy::CookieAuth.new(session["session_token"]))
|
|
23
|
+
#
|
|
24
|
+
# @example Finishing in another process
|
|
25
|
+
# pending = Fizzy::MagicLinkFlow.new.tap { _1.create_session(email_address: email) }.pending_token
|
|
26
|
+
# # ... later ...
|
|
27
|
+
# Fizzy::MagicLinkFlow.new(pending_token: pending).redeem(code: code)
|
|
20
28
|
class MagicLinkFlow
|
|
29
|
+
# The cookie a pending sign-in is carried in between creating the session and redeeming
|
|
30
|
+
# the code.
|
|
31
|
+
PENDING_COOKIE = "pending_authentication_token"
|
|
32
|
+
|
|
33
|
+
# The token a pending sign-in is waiting on, for a caller that finishes the flow in
|
|
34
|
+
# another process.
|
|
35
|
+
# @return [String, nil]
|
|
36
|
+
|
|
21
37
|
# @param base_url [String] Fizzy API base URL
|
|
22
38
|
# @param hooks [Hooks, nil] observability hooks
|
|
23
|
-
|
|
39
|
+
# @param pending_token [String, nil] the pending token of a sign-in another flow started
|
|
40
|
+
def initialize(base_url: Config::DEFAULT_BASE_URL, hooks: nil, pending_token: nil)
|
|
41
|
+
@pending_token = pending_token
|
|
42
|
+
@lock = Mutex.new
|
|
24
43
|
@config = Config.new(base_url: base_url)
|
|
25
|
-
@hooks = hooks
|
|
26
|
-
@
|
|
44
|
+
@hooks = hooks
|
|
45
|
+
@client = client_with(PendingCookieAuth.new(nil))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The pending authentication token of the sign-in under way, or nil before one starts.
|
|
49
|
+
# @return [String, nil]
|
|
50
|
+
def pending_token
|
|
51
|
+
@lock.synchronize { @pending_token }
|
|
27
52
|
end
|
|
28
53
|
|
|
29
|
-
#
|
|
54
|
+
# Asks Fizzy to email a sign-in code to the address, and keeps the pending token the
|
|
55
|
+
# answer carries for {#redeem}.
|
|
30
56
|
#
|
|
31
|
-
# @param
|
|
32
|
-
# @return [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
57
|
+
# @param email_address [String] the address to send the code to
|
|
58
|
+
# @return [String] the pending authentication token
|
|
59
|
+
# @raise [APIError] when Fizzy answers without a pending token
|
|
60
|
+
def create_session(email_address:)
|
|
61
|
+
answer = @client.sessions.create(email_address: email_address)
|
|
62
|
+
token = answer.is_a?(Hash) ? answer["pending_authentication_token"] : nil
|
|
63
|
+
raise APIError.new("Fizzy answered no pending authentication token") \
|
|
64
|
+
unless token.is_a?(String) && !token.empty?
|
|
65
|
+
|
|
66
|
+
@lock.synchronize { @pending_token = token }
|
|
36
67
|
end
|
|
37
68
|
|
|
38
|
-
#
|
|
69
|
+
# Redeems the code from the email, sending back the pending token as the cookie Fizzy
|
|
70
|
+
# reads it from.
|
|
39
71
|
#
|
|
40
|
-
# @param
|
|
41
|
-
# @return [Hash]
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
72
|
+
# @param code [String] the code from the magic-link email
|
|
73
|
+
# @return [Hash] +session_token+ and +requires_signup_completion+
|
|
74
|
+
# @raise [UsageError] when no sign-in is pending
|
|
75
|
+
# @raise [AuthError] when Fizzy refuses the code or the pending token
|
|
76
|
+
def redeem(code:)
|
|
77
|
+
pending = pending_token
|
|
78
|
+
raise UsageError.new("no sign-in is pending; call create_session first, or pass pending_token:") \
|
|
79
|
+
if pending.nil? || pending.empty?
|
|
80
|
+
|
|
81
|
+
# The cookie is bound to the token read here, so a sign-in that starts while this one is
|
|
82
|
+
# under way (a hook can yield before the request is signed) cannot pair this code with
|
|
83
|
+
# another's pending token.
|
|
84
|
+
answer = client_with(PendingCookieAuth.new(pending)).sessions.redeem_magic_link(code: code)
|
|
85
|
+
raise APIError.new("Fizzy answered no session token") \
|
|
86
|
+
unless answer.is_a?(Hash) && answer["session_token"].is_a?(String)
|
|
87
|
+
|
|
88
|
+
# The sign-in redeemed is consumed; one a create_session started meanwhile is not.
|
|
89
|
+
@lock.synchronize { @pending_token = nil if @pending_token.equal?(pending) }
|
|
90
|
+
answer.slice("session_token", "requires_signup_completion")
|
|
45
91
|
end
|
|
46
92
|
|
|
47
|
-
#
|
|
93
|
+
# A +Cookie+ header value carrying one cookie, its value percent-encoded as Rack decodes it.
|
|
48
94
|
# @api private
|
|
49
|
-
|
|
95
|
+
def self.cookie(name, value)
|
|
96
|
+
encoded = value.to_s.b.gsub(/[^A-Za-z0-9\-_.~]/n) { |byte| format("%%%02X", byte.ord) }
|
|
97
|
+
"#{name}=#{encoded}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def client_with(auth_strategy)
|
|
103
|
+
Client.new(config: @config, auth_strategy: auth_strategy, hooks: @hooks)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Sends one pending token, when given one, as the cookie Fizzy reads the pending sign-in
|
|
107
|
+
# from, and no other credentials: there are none yet.
|
|
108
|
+
# @api private
|
|
109
|
+
class PendingCookieAuth
|
|
50
110
|
include AuthStrategy
|
|
51
111
|
|
|
112
|
+
def initialize(token)
|
|
113
|
+
@token = token
|
|
114
|
+
end
|
|
115
|
+
|
|
52
116
|
def authenticate(headers)
|
|
53
|
-
|
|
117
|
+
headers["Cookie"] = MagicLinkFlow.cookie(PENDING_COOKIE, @token) unless @token.nil? || @token.empty?
|
|
54
118
|
end
|
|
55
119
|
end
|
|
56
120
|
end
|
data/lib/fizzy/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fizzy-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Basecamp
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|