monday_ruby 1.1.0 → 1.2.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.env +1 -1
  3. data/.rubocop.yml +2 -1
  4. data/CHANGELOG.md +14 -0
  5. data/CONTRIBUTING.md +104 -0
  6. data/README.md +146 -142
  7. data/docs/.vitepress/config.mjs +255 -0
  8. data/docs/.vitepress/theme/index.js +4 -0
  9. data/docs/.vitepress/theme/style.css +43 -0
  10. data/docs/README.md +80 -0
  11. data/docs/explanation/architecture.md +507 -0
  12. data/docs/explanation/best-practices/errors.md +478 -0
  13. data/docs/explanation/best-practices/performance.md +1084 -0
  14. data/docs/explanation/best-practices/rate-limiting.md +630 -0
  15. data/docs/explanation/best-practices/testing.md +820 -0
  16. data/docs/explanation/column-values.md +857 -0
  17. data/docs/explanation/design.md +795 -0
  18. data/docs/explanation/graphql.md +356 -0
  19. data/docs/explanation/migration/v1.md +808 -0
  20. data/docs/explanation/pagination.md +447 -0
  21. data/docs/guides/advanced/batch.md +1274 -0
  22. data/docs/guides/advanced/complex-queries.md +1114 -0
  23. data/docs/guides/advanced/errors.md +818 -0
  24. data/docs/guides/advanced/pagination.md +934 -0
  25. data/docs/guides/advanced/rate-limiting.md +981 -0
  26. data/docs/guides/authentication.md +286 -0
  27. data/docs/guides/boards/create.md +386 -0
  28. data/docs/guides/boards/delete.md +405 -0
  29. data/docs/guides/boards/duplicate.md +511 -0
  30. data/docs/guides/boards/query.md +530 -0
  31. data/docs/guides/boards/update.md +453 -0
  32. data/docs/guides/columns/create.md +452 -0
  33. data/docs/guides/columns/metadata.md +492 -0
  34. data/docs/guides/columns/query.md +455 -0
  35. data/docs/guides/columns/update-multiple.md +459 -0
  36. data/docs/guides/columns/update-values.md +509 -0
  37. data/docs/guides/files/add-to-column.md +40 -0
  38. data/docs/guides/files/add-to-update.md +37 -0
  39. data/docs/guides/files/clear-column.md +33 -0
  40. data/docs/guides/first-request.md +285 -0
  41. data/docs/guides/folders/manage.md +750 -0
  42. data/docs/guides/groups/items.md +626 -0
  43. data/docs/guides/groups/manage.md +501 -0
  44. data/docs/guides/installation.md +169 -0
  45. data/docs/guides/items/create.md +493 -0
  46. data/docs/guides/items/delete.md +514 -0
  47. data/docs/guides/items/query.md +605 -0
  48. data/docs/guides/items/subitems.md +483 -0
  49. data/docs/guides/items/update.md +699 -0
  50. data/docs/guides/updates/manage.md +619 -0
  51. data/docs/guides/use-cases/dashboard.md +1421 -0
  52. data/docs/guides/use-cases/import.md +1962 -0
  53. data/docs/guides/use-cases/task-management.md +1381 -0
  54. data/docs/guides/workspaces/manage.md +502 -0
  55. data/docs/index.md +69 -0
  56. data/docs/package-lock.json +2468 -0
  57. data/docs/package.json +13 -0
  58. data/docs/reference/client.md +540 -0
  59. data/docs/reference/configuration.md +586 -0
  60. data/docs/reference/errors.md +693 -0
  61. data/docs/reference/resources/account.md +208 -0
  62. data/docs/reference/resources/activity-log.md +369 -0
  63. data/docs/reference/resources/board-view.md +359 -0
  64. data/docs/reference/resources/board.md +393 -0
  65. data/docs/reference/resources/column.md +543 -0
  66. data/docs/reference/resources/file.md +236 -0
  67. data/docs/reference/resources/folder.md +386 -0
  68. data/docs/reference/resources/group.md +507 -0
  69. data/docs/reference/resources/item.md +348 -0
  70. data/docs/reference/resources/subitem.md +267 -0
  71. data/docs/reference/resources/update.md +259 -0
  72. data/docs/reference/resources/workspace.md +213 -0
  73. data/docs/reference/response.md +560 -0
  74. data/docs/tutorial/first-integration.md +713 -0
  75. data/lib/monday/client.rb +24 -0
  76. data/lib/monday/configuration.rb +5 -0
  77. data/lib/monday/request.rb +15 -0
  78. data/lib/monday/resources/base.rb +4 -0
  79. data/lib/monday/resources/file.rb +56 -0
  80. data/lib/monday/util.rb +1 -0
  81. data/lib/monday/version.rb +1 -1
  82. metadata +87 -4
@@ -0,0 +1,560 @@
1
+ # Response
2
+
3
+ The `Monday::Response` class wraps HTTP responses from the monday.com API.
4
+
5
+ ## Overview
6
+
7
+ Every request to monday.com returns a `Monday::Response` object that encapsulates the HTTP response status, parsed JSON body, and headers. This wrapper provides convenient access to response data and helps detect errors.
8
+
9
+ All resource methods (`client.board.query`, `client.item.create`, etc.) return a `Monday::Response` object.
10
+
11
+ ## Attributes
12
+
13
+ All attributes are read-only (`attr_reader`).
14
+
15
+ | Attribute | Type | Description |
16
+ |-----------|------|-------------|
17
+ | `status` | Integer | HTTP status code (200, 400, 401, 404, 500, etc.) |
18
+ | `body` | Hash | Parsed JSON response body containing GraphQL data |
19
+ | `headers` | Hash | HTTP response headers as key-value pairs |
20
+
21
+ **Example:**
22
+
23
+ ```ruby
24
+ response = client.board.query(args: { ids: [123] })
25
+
26
+ response.status # => 200
27
+ response.body # => { "data" => { "boards" => [...] } }
28
+ response.headers # => { "content-type" => "application/json", ... }
29
+ ```
30
+
31
+ ## Methods
32
+
33
+ ### success?
34
+
35
+ Determines if the request was successful.
36
+
37
+ ```ruby
38
+ response.success? # => true or false
39
+ ```
40
+
41
+ **Returns:** Boolean
42
+
43
+ ::: warning <span style="display: inline-flex; align-items: center; gap: 6px;"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>monday.com API Quirk</span>
44
+ monday.com returns HTTP 200 status codes even for some GraphQL errors. The `success?` method checks BOTH the status code (200-299) AND the response body for error keys.
45
+ :::
46
+
47
+ **Error Detection:**
48
+
49
+ The method returns `false` if:
50
+ - HTTP status code is outside 200-299 range, OR
51
+ - Response body contains any of these keys: `"errors"`, `"error_code"`, `"error_message"`
52
+
53
+ **Example:**
54
+
55
+ ```ruby
56
+ response = client.board.query(args: { ids: [123] })
57
+
58
+ if response.success?
59
+ # Safe to access data
60
+ boards = response.body.dig("data", "boards")
61
+ else
62
+ # Handle error
63
+ errors = response.body["errors"]
64
+ end
65
+ ```
66
+
67
+ ## Response Structure
68
+
69
+ ### Successful GraphQL Response
70
+
71
+ All successful GraphQL responses follow this structure:
72
+
73
+ ```ruby
74
+ {
75
+ "data" => {
76
+ "boards" => [
77
+ { "id" => "123", "name" => "My Board" }
78
+ ]
79
+ },
80
+ "account_id" => 12345678
81
+ }
82
+ ```
83
+
84
+ The actual data is nested under the `"data"` key, with the resource name as the next level.
85
+
86
+ **Common GraphQL response patterns:**
87
+
88
+ | Operation | Path to data |
89
+ |-----------|--------------|
90
+ | Query boards | `response.body.dig("data", "boards")` |
91
+ | Create board | `response.body.dig("data", "create_board")` |
92
+ | Query items | `response.body.dig("data", "items")` |
93
+ | Create item | `response.body.dig("data", "create_item")` |
94
+ | Duplicate board | `response.body.dig("data", "duplicate_board", "board")` |
95
+
96
+ ### Error Response
97
+
98
+ Error responses contain an `"errors"` array:
99
+
100
+ ```ruby
101
+ {
102
+ "errors" => [
103
+ {
104
+ "message" => "User unauthorized to perform action",
105
+ "extensions" => {
106
+ "code" => "AuthorizationException",
107
+ "error_code" => "AuthorizationException"
108
+ }
109
+ }
110
+ ],
111
+ "account_id" => 12345678
112
+ }
113
+ ```
114
+
115
+ Alternative error formats:
116
+
117
+ ```ruby
118
+ # Simple error
119
+ {
120
+ "error_message" => "Invalid token",
121
+ "error_code" => "InvalidTokenException"
122
+ }
123
+
124
+ # Generic error
125
+ {
126
+ "errors" => "Some error message"
127
+ }
128
+ ```
129
+
130
+ ## Accessing Data
131
+
132
+ ### Using dig for Safe Access
133
+
134
+ Always use `dig` to safely navigate nested response data:
135
+
136
+ ```ruby
137
+ response = client.board.query(args: { ids: [123] })
138
+
139
+ # Safe - returns nil if any key is missing
140
+ boards = response.body.dig("data", "boards")
141
+
142
+ # Unsafe - raises error if key is missing
143
+ boards = response.body["data"]["boards"] # Don't do this
144
+ ```
145
+
146
+ ### Accessing Nested Data
147
+
148
+ **Single board:**
149
+
150
+ ```ruby
151
+ response = client.board.query(args: { ids: [123] })
152
+
153
+ boards = response.body.dig("data", "boards")
154
+ board = boards&.first
155
+
156
+ puts board["name"] # => "My Board"
157
+ puts board["id"] # => "123"
158
+ ```
159
+
160
+ **Board with items:**
161
+
162
+ ```ruby
163
+ response = client.board.query(
164
+ args: { ids: [123] },
165
+ select: ["id", "name", { items: ["id", "name"] }]
166
+ )
167
+
168
+ board = response.body.dig("data", "boards", 0)
169
+ items = board["items"]
170
+
171
+ items.each do |item|
172
+ puts item["name"]
173
+ end
174
+ ```
175
+
176
+ **Paginated items:**
177
+
178
+ ```ruby
179
+ response = client.board.items_page(
180
+ board_ids: 123,
181
+ limit: 50
182
+ )
183
+
184
+ items_page = response.body.dig("data", "boards", 0, "items_page")
185
+ items = items_page["items"]
186
+ cursor = items_page["cursor"]
187
+
188
+ puts "Retrieved #{items.length} items"
189
+ puts "Next cursor: #{cursor}"
190
+ ```
191
+
192
+ ### Handling Missing Data
193
+
194
+ ```ruby
195
+ response = client.board.query(args: { ids: [999] })
196
+
197
+ boards = response.body.dig("data", "boards")
198
+
199
+ if boards.nil? || boards.empty?
200
+ puts "No boards found"
201
+ else
202
+ puts "Found #{boards.length} boards"
203
+ end
204
+ ```
205
+
206
+ ## Error Responses
207
+
208
+ ### Checking for Errors
209
+
210
+ ```ruby
211
+ response = client.board.query(args: { ids: [123] })
212
+
213
+ unless response.success?
214
+ if response.body["errors"]
215
+ errors = response.body["errors"]
216
+ errors.each do |error|
217
+ puts "Error: #{error['message']}"
218
+ end
219
+ elsif response.body["error_message"]
220
+ puts "Error: #{response.body['error_message']}"
221
+ end
222
+ end
223
+ ```
224
+
225
+ ### Error Response Examples
226
+
227
+ **Authorization Error (HTTP 200 with errors in body):**
228
+
229
+ ```ruby
230
+ {
231
+ "errors" => [
232
+ {
233
+ "message" => "User unauthorized to perform action",
234
+ "extensions" => {
235
+ "code" => "AuthorizationException"
236
+ }
237
+ }
238
+ ],
239
+ "account_id" => 12345678
240
+ }
241
+
242
+ response.status # => 200
243
+ response.success? # => false (because errors key exists)
244
+ ```
245
+
246
+ **Invalid Token (HTTP 401):**
247
+
248
+ ```ruby
249
+ {
250
+ "error_message" => "Invalid token",
251
+ "error_code" => "InvalidTokenException"
252
+ }
253
+
254
+ response.status # => 401
255
+ response.success? # => false
256
+ ```
257
+
258
+ **Rate Limit (HTTP 429):**
259
+
260
+ ```ruby
261
+ {
262
+ "error_message" => "You have reached the rate limit",
263
+ "error_code" => "ComplexityException"
264
+ }
265
+
266
+ response.status # => 429
267
+ response.success? # => false
268
+ ```
269
+
270
+ **Invalid Query (HTTP 200 with errors in body):**
271
+
272
+ ```ruby
273
+ {
274
+ "errors" => [
275
+ {
276
+ "message" => "Field 'invalid_field' doesn't exist on type 'Board'",
277
+ "locations" => [{ "line" => 1, "column" => 20 }]
278
+ }
279
+ ]
280
+ }
281
+
282
+ response.status # => 200
283
+ response.success? # => false (because errors key exists)
284
+ ```
285
+
286
+ ## Usage Examples
287
+
288
+ ### Basic Query
289
+
290
+ ```ruby
291
+ response = client.board.query(args: { ids: [123] })
292
+
293
+ if response.success?
294
+ boards = response.body.dig("data", "boards")
295
+ puts "Found #{boards.length} boards"
296
+ else
297
+ puts "Request failed with status #{response.status}"
298
+ end
299
+ ```
300
+
301
+ ### Create Operation
302
+
303
+ ```ruby
304
+ response = client.board.create(
305
+ args: {
306
+ board_name: "New Board",
307
+ board_kind: :public
308
+ }
309
+ )
310
+
311
+ if response.success?
312
+ board = response.body.dig("data", "create_board")
313
+ puts "Created board #{board['id']}: #{board['name']}"
314
+ else
315
+ errors = response.body["errors"]
316
+ puts "Failed to create board: #{errors}"
317
+ end
318
+ ```
319
+
320
+ ### Handling Nested Data
321
+
322
+ ```ruby
323
+ response = client.item.query(
324
+ args: { ids: [456] },
325
+ select: ["id", "name", { column_values: ["id", "text", "value"] }]
326
+ )
327
+
328
+ if response.success?
329
+ items = response.body.dig("data", "items")
330
+ item = items&.first
331
+
332
+ if item
333
+ puts "Item: #{item['name']}"
334
+
335
+ column_values = item["column_values"]
336
+ column_values&.each do |cv|
337
+ puts " #{cv['id']}: #{cv['text']}"
338
+ end
339
+ end
340
+ end
341
+ ```
342
+
343
+ ### Checking Response Headers
344
+
345
+ ```ruby
346
+ response = client.board.query(args: { ids: [123] })
347
+
348
+ puts "Status: #{response.status}"
349
+ puts "Content-Type: #{response.headers['content-type']}"
350
+ puts "Rate Limit: #{response.headers['x-ratelimit-remaining']}"
351
+ ```
352
+
353
+ ### Processing Multiple Results
354
+
355
+ ```ruby
356
+ response = client.board.query(
357
+ args: { ids: [123, 456, 789] }
358
+ )
359
+
360
+ if response.success?
361
+ boards = response.body.dig("data", "boards") || []
362
+
363
+ boards.each do |board|
364
+ puts "Board #{board['id']}: #{board['name']}"
365
+ end
366
+
367
+ puts "\nTotal: #{boards.length} boards"
368
+ else
369
+ puts "Failed to fetch boards"
370
+ end
371
+ ```
372
+
373
+ ## Best Practices
374
+
375
+ ### Always Check success?
376
+
377
+ Never assume a request succeeded. Always check before accessing data:
378
+
379
+ ```ruby
380
+ # Good
381
+ response = client.board.query(args: { ids: [123] })
382
+
383
+ if response.success?
384
+ boards = response.body.dig("data", "boards")
385
+ # Work with boards
386
+ else
387
+ # Handle error
388
+ end
389
+
390
+ # Bad - will raise error if request fails
391
+ boards = client.board.query(args: { ids: [123] }).body["data"]["boards"]
392
+ ```
393
+
394
+ ### Use dig for Safe Navigation
395
+
396
+ Use `dig` to safely access nested data:
397
+
398
+ ```ruby
399
+ # Good - returns nil if any key is missing
400
+ board = response.body.dig("data", "boards", 0)
401
+ name = board&.dig("name")
402
+
403
+ # Bad - raises error if key is missing
404
+ name = response.body["data"]["boards"][0]["name"]
405
+ ```
406
+
407
+ ### Handle nil Results
408
+
409
+ Always check for nil before iterating:
410
+
411
+ ```ruby
412
+ # Good
413
+ boards = response.body.dig("data", "boards") || []
414
+ boards.each { |board| puts board["name"] }
415
+
416
+ # Or
417
+ boards = response.body.dig("data", "boards")
418
+ if boards
419
+ boards.each { |board| puts board["name"] }
420
+ end
421
+
422
+ # Bad - raises error if boards is nil
423
+ boards = response.body.dig("data", "boards")
424
+ boards.each { |board| puts board["name"] }
425
+ ```
426
+
427
+ ### Store Response Data
428
+
429
+ Extract data from the response before working with it:
430
+
431
+ ```ruby
432
+ # Good
433
+ response = client.board.query(args: { ids: [123] })
434
+
435
+ if response.success?
436
+ boards = response.body.dig("data", "boards")
437
+ # Now work with boards array
438
+ end
439
+
440
+ # Less efficient - accessing response.body multiple times
441
+ if response.success?
442
+ response.body.dig("data", "boards").each do |board|
443
+ # ...
444
+ end
445
+ end
446
+ ```
447
+
448
+ ### Check Both success? and Data Presence
449
+
450
+ Some queries may succeed but return empty results:
451
+
452
+ ```ruby
453
+ response = client.board.query(args: { ids: [999] })
454
+
455
+ if response.success?
456
+ boards = response.body.dig("data", "boards")
457
+
458
+ if boards && !boards.empty?
459
+ # Process boards
460
+ else
461
+ puts "No boards found with ID 999"
462
+ end
463
+ else
464
+ puts "Request failed: #{response.status}"
465
+ end
466
+ ```
467
+
468
+ ## GraphQL Response Format
469
+
470
+ ### Data Wrapper
471
+
472
+ All successful GraphQL responses wrap data in a `"data"` key:
473
+
474
+ ```ruby
475
+ {
476
+ "data" => {
477
+ # Actual response data here
478
+ },
479
+ "account_id" => 12345678
480
+ }
481
+ ```
482
+
483
+ ### Query Responses
484
+
485
+ Query responses use plural resource names:
486
+
487
+ ```ruby
488
+ # boards query
489
+ {
490
+ "data" => {
491
+ "boards" => [...]
492
+ }
493
+ }
494
+
495
+ # items query
496
+ {
497
+ "data" => {
498
+ "items" => [...]
499
+ }
500
+ }
501
+ ```
502
+
503
+ ### Mutation Responses
504
+
505
+ Mutation responses use the mutation name:
506
+
507
+ ```ruby
508
+ # create_board
509
+ {
510
+ "data" => {
511
+ "create_board" => { "id" => "123", "name" => "New Board" }
512
+ }
513
+ }
514
+
515
+ # update_board (returns JSON string)
516
+ {
517
+ "data" => {
518
+ "update_board" => '{"success":true,"undo_data":"..."}'
519
+ }
520
+ }
521
+
522
+ # duplicate_board (nested under "board")
523
+ {
524
+ "data" => {
525
+ "duplicate_board" => {
526
+ "board" => { "id" => "456", "name" => "Duplicated Board" }
527
+ }
528
+ }
529
+ }
530
+ ```
531
+
532
+ ### Error Structure
533
+
534
+ Errors use the `"errors"` array:
535
+
536
+ ```ruby
537
+ {
538
+ "errors" => [
539
+ {
540
+ "message" => "Error description",
541
+ "extensions" => {
542
+ "code" => "ErrorCode"
543
+ }
544
+ }
545
+ ],
546
+ "account_id" => 12345678
547
+ }
548
+ ```
549
+
550
+ ## Related Documentation
551
+
552
+ - [Client](/reference/client) - Making API requests
553
+ - [Error Handling](/guides/advanced/errors) - Handling exceptions
554
+ - [Board Resource](/reference/resources/board) - Example resource usage
555
+ - [Item Resource](/reference/resources/item) - Example resource usage
556
+
557
+ ## External References
558
+
559
+ - [monday.com GraphQL API](https://developer.monday.com/api-reference/docs/introduction-to-graphql)
560
+ - [GraphQL Response Format](https://graphql.org/learn/serving-over-http/)