superthread 0.7.2

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 (84) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +4 -0
  3. data/LICENSE +21 -0
  4. data/README.md +492 -0
  5. data/exe/suth +19 -0
  6. data/lib/superthread/cli/accounts.rb +240 -0
  7. data/lib/superthread/cli/activity.rb +210 -0
  8. data/lib/superthread/cli/base.rb +355 -0
  9. data/lib/superthread/cli/boards.rb +131 -0
  10. data/lib/superthread/cli/cards.rb +530 -0
  11. data/lib/superthread/cli/checklists.rb +223 -0
  12. data/lib/superthread/cli/comments.rb +86 -0
  13. data/lib/superthread/cli/completion.rb +306 -0
  14. data/lib/superthread/cli/concerns/board_resolvable.rb +70 -0
  15. data/lib/superthread/cli/concerns/confirmable.rb +55 -0
  16. data/lib/superthread/cli/concerns/date_parsable.rb +196 -0
  17. data/lib/superthread/cli/concerns/list_resolvable.rb +53 -0
  18. data/lib/superthread/cli/concerns/space_resolvable.rb +52 -0
  19. data/lib/superthread/cli/concerns/sprint_resolvable.rb +55 -0
  20. data/lib/superthread/cli/concerns/tag_resolvable.rb +49 -0
  21. data/lib/superthread/cli/concerns/user_resolvable.rb +52 -0
  22. data/lib/superthread/cli/concerns/workspace_resolvable.rb +83 -0
  23. data/lib/superthread/cli/config.rb +129 -0
  24. data/lib/superthread/cli/formatter.rb +388 -0
  25. data/lib/superthread/cli/lists.rb +85 -0
  26. data/lib/superthread/cli/main.rb +121 -0
  27. data/lib/superthread/cli/members.rb +19 -0
  28. data/lib/superthread/cli/notes.rb +64 -0
  29. data/lib/superthread/cli/pages.rb +128 -0
  30. data/lib/superthread/cli/projects.rb +124 -0
  31. data/lib/superthread/cli/replies.rb +94 -0
  32. data/lib/superthread/cli/search.rb +34 -0
  33. data/lib/superthread/cli/setup.rb +253 -0
  34. data/lib/superthread/cli/spaces.rb +141 -0
  35. data/lib/superthread/cli/sprints.rb +32 -0
  36. data/lib/superthread/cli/tags.rb +86 -0
  37. data/lib/superthread/cli/ui/gum_prompt.rb +58 -0
  38. data/lib/superthread/cli/ui/plain_prompt.rb +73 -0
  39. data/lib/superthread/cli/ui.rb +263 -0
  40. data/lib/superthread/cli/workspaces.rb +105 -0
  41. data/lib/superthread/cli.rb +12 -0
  42. data/lib/superthread/client.rb +207 -0
  43. data/lib/superthread/configuration.rb +354 -0
  44. data/lib/superthread/connection.rb +57 -0
  45. data/lib/superthread/error.rb +164 -0
  46. data/lib/superthread/mention_formatter.rb +96 -0
  47. data/lib/superthread/model.rb +178 -0
  48. data/lib/superthread/models/board.rb +59 -0
  49. data/lib/superthread/models/card.rb +321 -0
  50. data/lib/superthread/models/checklist.rb +91 -0
  51. data/lib/superthread/models/checklist_item.rb +69 -0
  52. data/lib/superthread/models/comment.rb +71 -0
  53. data/lib/superthread/models/concerns/archivable.rb +32 -0
  54. data/lib/superthread/models/concerns/presentable.rb +113 -0
  55. data/lib/superthread/models/concerns/timestampable.rb +91 -0
  56. data/lib/superthread/models/list.rb +67 -0
  57. data/lib/superthread/models/member.rb +40 -0
  58. data/lib/superthread/models/note.rb +56 -0
  59. data/lib/superthread/models/page.rb +70 -0
  60. data/lib/superthread/models/project.rb +83 -0
  61. data/lib/superthread/models/space.rb +71 -0
  62. data/lib/superthread/models/sprint.rb +53 -0
  63. data/lib/superthread/models/tag.rb +52 -0
  64. data/lib/superthread/models/team.rb +68 -0
  65. data/lib/superthread/models/user.rb +76 -0
  66. data/lib/superthread/models.rb +12 -0
  67. data/lib/superthread/object.rb +285 -0
  68. data/lib/superthread/objects/collection.rb +179 -0
  69. data/lib/superthread/resources/base.rb +204 -0
  70. data/lib/superthread/resources/boards.rb +150 -0
  71. data/lib/superthread/resources/cards.rb +363 -0
  72. data/lib/superthread/resources/comments.rb +163 -0
  73. data/lib/superthread/resources/notes.rb +61 -0
  74. data/lib/superthread/resources/pages.rb +110 -0
  75. data/lib/superthread/resources/projects.rb +117 -0
  76. data/lib/superthread/resources/search.rb +46 -0
  77. data/lib/superthread/resources/spaces.rb +104 -0
  78. data/lib/superthread/resources/sprints.rb +37 -0
  79. data/lib/superthread/resources/tags.rb +52 -0
  80. data/lib/superthread/resources/users.rb +29 -0
  81. data/lib/superthread/version.rb +6 -0
  82. data/lib/superthread/version_checker.rb +174 -0
  83. data/lib/superthread.rb +30 -0
  84. metadata +259 -0
@@ -0,0 +1,363 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Superthread
4
+ module Resources
5
+ # API resource for card (task/issue) operations.
6
+ #
7
+ # Provides methods for creating, updating, listing, and managing cards
8
+ # including their members, checklists, tags, and relationships.
9
+ class Cards < Base
10
+ # Creates a new card on a board or in a sprint.
11
+ #
12
+ # @param workspace_id [String] the workspace identifier
13
+ # @param params [Hash{Symbol => Object}] card creation parameters
14
+ # @option params [String] :title the card title (required)
15
+ # @option params [String] :list_id the list identifier (required)
16
+ # @option params [String] :board_id the board identifier (required unless sprint_id provided)
17
+ # @option params [String] :sprint_id the sprint identifier (required unless board_id provided)
18
+ # @option params [String] :content the card content as HTML
19
+ # @option params [String] :project_id the project/space identifier
20
+ # @option params [Integer] :start_date the start date as Unix timestamp
21
+ # @option params [Integer] :due_date the due date as Unix timestamp
22
+ # @option params [Integer] :priority the priority level (1-4)
23
+ # @option params [Integer] :estimate the story point estimate
24
+ # @option params [String] :parent_card_id the parent card identifier for subtasks
25
+ # @option params [String] :epic_id the epic/project identifier to link to
26
+ # @option params [String] :owner_id the owner user identifier
27
+ # @return [Superthread::Models::Card] the created card
28
+ # @raise [ArgumentError] if neither board_id nor sprint_id is provided
29
+ def create(workspace_id, **params)
30
+ unless params[:board_id] || params[:sprint_id]
31
+ raise ArgumentError, "Either board_id or sprint_id must be provided"
32
+ end
33
+
34
+ ws = safe_id("workspace_id", workspace_id)
35
+ post_object("/#{ws}/cards", body: params,
36
+ object_class: Models::Card, unwrap_key: :card)
37
+ end
38
+
39
+ # Updates an existing card's attributes.
40
+ #
41
+ # @param workspace_id [String] the workspace identifier
42
+ # @param card_id [String] the card identifier
43
+ # @param params [Hash{Symbol => Object}] the attributes to update
44
+ # @option params [String] :title the new card title
45
+ # @option params [String] :list_id the new list identifier to move the card
46
+ # @option params [Integer] :start_date the new start date as Unix timestamp
47
+ # @option params [Integer] :due_date the new due date as Unix timestamp
48
+ # @option params [Integer] :priority the new priority level (1-4)
49
+ # @option params [Integer] :estimate the new story point estimate
50
+ # @option params [String] :sprint_id the destination sprint identifier
51
+ # @option params [String] :project_id the project/space identifier (required with sprint_id)
52
+ # @option params [String] :epic_id the new epic identifier
53
+ # @option params [Boolean] :archived whether the card is archived
54
+ # @return [Superthread::Models::Card] the updated card
55
+ # @note Content cannot be updated via this API; it uses WebSocket collaboration.
56
+ # @note parent_card_id is not supported on update; the API silently ignores it.
57
+ def update(workspace_id, card_id, **params)
58
+ ws = safe_id("workspace_id", workspace_id)
59
+ card = safe_id("card_id", card_id)
60
+ patch_object("/#{ws}/cards/#{card}", body: compact_params(**params),
61
+ object_class: Models::Card, unwrap_key: :card)
62
+ end
63
+
64
+ # Gets a specific card with full details.
65
+ #
66
+ # @param workspace_id [String] the workspace identifier
67
+ # @param card_id [String] the card identifier
68
+ # @return [Superthread::Models::Card] the card with all attributes
69
+ def find(workspace_id, card_id)
70
+ ws = safe_id("workspace_id", workspace_id)
71
+ card = safe_id("card_id", card_id)
72
+ get_object("/#{ws}/cards/#{card}",
73
+ object_class: Models::Card, unwrap_key: :card)
74
+ end
75
+
76
+ # Deletes a card permanently.
77
+ #
78
+ # @param workspace_id [String] the workspace identifier
79
+ # @param card_id [String] the card identifier to delete
80
+ # @return [Superthread::Object] a response object with success: true
81
+ def destroy(workspace_id, card_id)
82
+ ws = safe_id("workspace_id", workspace_id)
83
+ card = safe_id("card_id", card_id)
84
+ http_delete("/#{ws}/cards/#{card}")
85
+ success_response
86
+ end
87
+
88
+ # Duplicates a card.
89
+ #
90
+ # @param workspace_id [String] the workspace identifier
91
+ # @param card_id [String] the card identifier to duplicate
92
+ # @param params [Hash{Symbol => Object}] optional destination parameters
93
+ # @option params [String] :board_id the destination board identifier
94
+ # @option params [String] :list_id the destination list identifier
95
+ # @option params [String] :sprint_id the destination sprint identifier
96
+ # @return [Superthread::Models::Card] the duplicated card
97
+ def duplicate(workspace_id, card_id, **params)
98
+ ws = safe_id("workspace_id", workspace_id)
99
+ card = safe_id("card_id", card_id)
100
+ post_object("/#{ws}/cards/#{card}/copy", body: compact_params(**params),
101
+ object_class: Models::Card, unwrap_key: :card)
102
+ end
103
+
104
+ # Lists cards with optional filters.
105
+ #
106
+ # @param workspace_id [String] the workspace identifier
107
+ # @param filters [Hash{Symbol => Object}] filter options
108
+ # @option filters [String] :board_id filter by board identifier
109
+ # @option filters [String] :sprint_id filter by sprint identifier
110
+ # @option filters [String] :list_id filter by list identifier
111
+ # @option filters [String] :project_id filter by project/space identifier
112
+ # @option filters [Boolean] :archived when true, includes archived cards
113
+ # @return [Superthread::Objects::Collection<Superthread::Models::Card>] the matching cards
114
+ def list(workspace_id, **filters)
115
+ ws = safe_id("workspace_id", workspace_id)
116
+ body = {type: "card", card_filters: build_card_filters(filters)}
117
+
118
+ post_collection("/#{ws}/views/preview", body: body,
119
+ item_class: Models::Card, items_key: :cards)
120
+ end
121
+
122
+ # Gets cards assigned to a user.
123
+ #
124
+ # @param workspace_id [String] the workspace identifier
125
+ # @param user_id [String] the user identifier to filter by assignment
126
+ # @param filters [Hash{Symbol => Object}] optional additional filters
127
+ # @option filters [String] :board_id filter by board identifier
128
+ # @option filters [String] :list_id filter by list identifier
129
+ # @option filters [String] :project_id filter by project/space identifier
130
+ # @option filters [Boolean] :archived when true, includes archived cards
131
+ # @return [Superthread::Objects::Collection<Superthread::Models::Card>] the user's assigned cards
132
+ def assigned(workspace_id, user_id:, **filters)
133
+ ws = safe_id("workspace_id", workspace_id)
134
+ body = {type: "card", card_filters: build_card_filters(filters, members: [user_id])}
135
+
136
+ post_collection("/#{ws}/views/preview", body: body,
137
+ item_class: Models::Card, items_key: :cards)
138
+ end
139
+
140
+ # Links two cards with a relationship.
141
+ #
142
+ # @param workspace_id [String] the workspace identifier
143
+ # @param card_id [String] the source card identifier
144
+ # @param related_card_id [String] the card identifier to link to
145
+ # @param relation_type [String] the relationship type (blocks, blocked_by, related, duplicates)
146
+ # @return [Superthread::Object] the link result
147
+ def add_related(workspace_id, card_id, related_card_id:, relation_type:)
148
+ ws = safe_id("workspace_id", workspace_id)
149
+ card = safe_id("card_id", card_id)
150
+
151
+ post_object("/#{ws}/cards/#{card}/linked_cards", body: {
152
+ card_id: related_card_id,
153
+ linked_card_type: relation_type
154
+ })
155
+ end
156
+
157
+ # Removes a card relationship.
158
+ #
159
+ # @param workspace_id [String] the workspace identifier
160
+ # @param card_id [String] the source card identifier
161
+ # @param linked_card_id [String] the linked card identifier to remove
162
+ # @return [Superthread::Object] a response object with success: true
163
+ def remove_related(workspace_id, card_id, linked_card_id)
164
+ ws = safe_id("workspace_id", workspace_id)
165
+ card = safe_id("card_id", card_id)
166
+ linked = safe_id("linked_card_id", linked_card_id)
167
+ http_delete("/#{ws}/cards/#{card}/linked_cards/#{linked}")
168
+ success_response
169
+ end
170
+
171
+ # Adds a member to a card.
172
+ #
173
+ # @param workspace_id [String] the workspace identifier
174
+ # @param card_id [String] the card identifier
175
+ # @param user_id [String] the user identifier to add as a member
176
+ # @param role [String] the member role (defaults to "member")
177
+ # @return [Superthread::Object] the membership result
178
+ def add_member(workspace_id, card_id, user_id:, role: "member")
179
+ ws = safe_id("workspace_id", workspace_id)
180
+ card = safe_id("card_id", card_id)
181
+ post_object("/#{ws}/cards/#{card}/members", body: {user_id: user_id, role: role})
182
+ end
183
+
184
+ # Removes a member from a card.
185
+ #
186
+ # @param workspace_id [String] the workspace identifier
187
+ # @param card_id [String] the card identifier
188
+ # @param user_id [String] the user identifier to remove
189
+ # @return [Superthread::Object] a response object with success: true
190
+ def remove_member(workspace_id, card_id, user_id)
191
+ ws = safe_id("workspace_id", workspace_id)
192
+ card = safe_id("card_id", card_id)
193
+ user = safe_id("user_id", user_id)
194
+ http_delete("/#{ws}/cards/#{card}/members/#{user}")
195
+ success_response
196
+ end
197
+
198
+ # Creates a checklist on a card.
199
+ #
200
+ # @param workspace_id [String] the workspace identifier
201
+ # @param card_id [String] the card identifier
202
+ # @param title [String] the checklist title
203
+ # @return [Superthread::Models::Checklist] the created checklist
204
+ def create_checklist(workspace_id, card_id, title:)
205
+ ws = safe_id("workspace_id", workspace_id)
206
+ card = safe_id("card_id", card_id)
207
+ post_object("/#{ws}/cards/#{card}/checklists", body: {title: title},
208
+ object_class: Models::Checklist, unwrap_key: :checklist)
209
+ end
210
+
211
+ # Adds an item to a checklist.
212
+ #
213
+ # @param workspace_id [String] the workspace identifier
214
+ # @param card_id [String] the card identifier
215
+ # @param checklist_id [String] the checklist identifier
216
+ # @param title [String] the item title
217
+ # @param checked [Boolean] whether the item is checked (defaults to false)
218
+ # @return [Superthread::Models::ChecklistItem] the created checklist item
219
+ def add_checklist_item(workspace_id, card_id, checklist_id, title:, checked: false)
220
+ ws = safe_id("workspace_id", workspace_id)
221
+ card = safe_id("card_id", card_id)
222
+ checklist = safe_id("checklist_id", checklist_id)
223
+ title = format_mentions(workspace_id, title)
224
+
225
+ post_object("/#{ws}/cards/#{card}/checklists/#{checklist}/items", body: {
226
+ title: title,
227
+ checklist_id: checklist_id,
228
+ checked: checked
229
+ }, object_class: Models::ChecklistItem, unwrap_key: :checklist_item)
230
+ end
231
+
232
+ # Updates a checklist item.
233
+ #
234
+ # @param workspace_id [String] the workspace identifier
235
+ # @param card_id [String] the card identifier
236
+ # @param checklist_id [String] the checklist identifier
237
+ # @param item_id [String] the item identifier
238
+ # @param params [Hash{Symbol => Object}] the attributes to update
239
+ # @option params [String] :title the new item title
240
+ # @option params [Boolean] :checked whether the item is checked
241
+ # @return [Superthread::Models::ChecklistItem] the updated checklist item
242
+ def update_checklist_item(workspace_id, card_id, checklist_id, item_id, **params)
243
+ ws = safe_id("workspace_id", workspace_id)
244
+ card = safe_id("card_id", card_id)
245
+ checklist = safe_id("checklist_id", checklist_id)
246
+ item = safe_id("item_id", item_id)
247
+ params[:title] = format_mentions(workspace_id, params[:title]) if params[:title]
248
+
249
+ patch_object("/#{ws}/cards/#{card}/checklists/#{checklist}/items/#{item}",
250
+ body: compact_params(**params), object_class: Models::ChecklistItem, unwrap_key: :checklist_item)
251
+ end
252
+
253
+ # Deletes a checklist item.
254
+ #
255
+ # @param workspace_id [String] the workspace identifier
256
+ # @param card_id [String] the card identifier
257
+ # @param checklist_id [String] the checklist identifier
258
+ # @param item_id [String] the item identifier to delete
259
+ # @return [Superthread::Object] a response object with success: true
260
+ def delete_checklist_item(workspace_id, card_id, checklist_id, item_id)
261
+ ws = safe_id("workspace_id", workspace_id)
262
+ card = safe_id("card_id", card_id)
263
+ checklist = safe_id("checklist_id", checklist_id)
264
+ item = safe_id("item_id", item_id)
265
+
266
+ http_delete("/#{ws}/cards/#{card}/checklists/#{checklist}/items/#{item}")
267
+ success_response
268
+ end
269
+
270
+ # Updates a checklist's title.
271
+ #
272
+ # @param workspace_id [String] the workspace identifier
273
+ # @param card_id [String] the card identifier
274
+ # @param checklist_id [String] the checklist identifier
275
+ # @param title [String] the new checklist title
276
+ # @return [Superthread::Models::Checklist] the updated checklist
277
+ def update_checklist(workspace_id, card_id, checklist_id, title:)
278
+ ws = safe_id("workspace_id", workspace_id)
279
+ card = safe_id("card_id", card_id)
280
+ checklist = safe_id("checklist_id", checklist_id)
281
+
282
+ patch_object("/#{ws}/cards/#{card}/checklists/#{checklist}", body: {title: title},
283
+ object_class: Models::Checklist, unwrap_key: :checklist)
284
+ end
285
+
286
+ # Deletes a checklist and all its items.
287
+ #
288
+ # @param workspace_id [String] the workspace identifier
289
+ # @param card_id [String] the card identifier
290
+ # @param checklist_id [String] the checklist identifier to delete
291
+ # @return [Superthread::Object] a response object with success: true
292
+ def delete_checklist(workspace_id, card_id, checklist_id)
293
+ ws = safe_id("workspace_id", workspace_id)
294
+ card = safe_id("card_id", card_id)
295
+ checklist = safe_id("checklist_id", checklist_id)
296
+
297
+ http_delete("/#{ws}/cards/#{card}/checklists/#{checklist}")
298
+ success_response
299
+ end
300
+
301
+ # Gets available tags for a workspace.
302
+ #
303
+ # @param workspace_id [String] the workspace identifier
304
+ # @param project_id [String, nil] optional project/space identifier to filter by
305
+ # @param all [Boolean, nil] when true, returns all tags including unused ones
306
+ # @return [Superthread::Objects::Collection<Superthread::Models::Tag>] the available tags
307
+ def tags(workspace_id, project_id: nil, all: nil)
308
+ ws = safe_id("workspace_id", workspace_id)
309
+ params = compact_params(project_id: project_id, all: all)
310
+ get_collection("/#{ws}/tags", params: params,
311
+ item_class: Models::Tag, items_key: :tags)
312
+ end
313
+
314
+ # Adds tags to a card.
315
+ #
316
+ # @param workspace_id [String] the workspace identifier
317
+ # @param card_id [String] the card identifier
318
+ # @param tag_ids [Array<String>, String] the tag identifier(s) to add
319
+ # @return [Superthread::Object] the tagging result
320
+ def add_tags(workspace_id, card_id, tag_ids:)
321
+ ws = safe_id("workspace_id", workspace_id)
322
+ card = safe_id("card_id", card_id)
323
+
324
+ body = tag_ids.is_a?(Array) ? {ids: tag_ids} : {id: tag_ids}
325
+ post_object("/#{ws}/cards/#{card}/tags", body: body)
326
+ end
327
+
328
+ # Removes a tag from a card.
329
+ #
330
+ # @param workspace_id [String] the workspace identifier
331
+ # @param card_id [String] the card identifier
332
+ # @param tag_id [String] the tag identifier to remove
333
+ # @return [Superthread::Object] a response object with success: true
334
+ def remove_tag(workspace_id, card_id, tag_id)
335
+ ws = safe_id("workspace_id", workspace_id)
336
+ card = safe_id("card_id", card_id)
337
+ tag = safe_id("tag_id", tag_id)
338
+ http_delete("/#{ws}/cards/#{card}/tags/#{tag}")
339
+ success_response
340
+ end
341
+
342
+ private
343
+
344
+ # Builds the card_filters hash for the views/preview endpoint.
345
+ #
346
+ # @param filters [Hash{Symbol => Object}] filter options
347
+ # @param members [Array<String>, nil] user IDs to filter by assignment
348
+ # @return [Hash] the card_filters structure for the API request
349
+ def build_card_filters(filters, members: nil)
350
+ includes = {}
351
+ includes[:members] = members if members
352
+ includes[:boards] = [filters[:board_id]] if filters[:board_id]
353
+ includes[:lists] = [filters[:list_id]] if filters[:list_id]
354
+ includes[:projects] = [filters[:project_id]] if filters[:project_id]
355
+ includes[:sprints] = [filters[:sprint_id]] if filters[:sprint_id]
356
+
357
+ card_filters = {include: includes}
358
+ card_filters[:is_archived] = filters[:archived] unless filters[:archived].nil?
359
+ card_filters
360
+ end
361
+ end
362
+ end
363
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Superthread
4
+ module Resources
5
+ # API resource for comment operations.
6
+ #
7
+ # Provides methods for creating, updating, and managing comments
8
+ # on cards and pages, including threaded replies.
9
+ class Comments < Base
10
+ # Lists comments on a card.
11
+ #
12
+ # @param workspace_id [String] the workspace identifier
13
+ # @param card_id [String] the card identifier to list comments for
14
+ # @return [Superthread::Objects::Collection<Superthread::Models::Comment>] the card's comments
15
+ def list(workspace_id, card_id:)
16
+ ws = safe_id("workspace_id", workspace_id)
17
+ params = compact_params(card_id: card_id)
18
+ get_collection("/#{ws}/comments", params: params,
19
+ item_class: Models::Comment, items_key: :comments)
20
+ end
21
+
22
+ # Creates a comment on a card or page.
23
+ #
24
+ # @param workspace_id [String] the workspace identifier
25
+ # @param content [String] the comment content as HTML (max 102400 chars)
26
+ # @param card_id [String, nil] the card identifier (required unless page_id provided)
27
+ # @param page_id [String, nil] the page identifier (required unless card_id provided)
28
+ # @param params [Hash{Symbol => Object}] optional comment parameters
29
+ # @option params [Hash{Symbol => Object}] :schema the content schema for structured content
30
+ # @option params [Hash{Symbol => Object}] :context additional context metadata
31
+ # @return [Superthread::Models::Comment] the created comment
32
+ def create(workspace_id, content:, card_id: nil, page_id: nil, **params)
33
+ ws = safe_id("workspace_id", workspace_id)
34
+ content = format_mentions(workspace_id, content)
35
+ body = compact_params(content: content, card_id: card_id, page_id: page_id, **params)
36
+ post_object("/#{ws}/comments", body: body,
37
+ object_class: Models::Comment, unwrap_key: :comment)
38
+ end
39
+
40
+ # Gets a specific comment.
41
+ #
42
+ # @param workspace_id [String] the workspace identifier
43
+ # @param comment_id [String] the comment identifier
44
+ # @return [Superthread::Models::Comment] the comment with all attributes
45
+ def find(workspace_id, comment_id)
46
+ ws = safe_id("workspace_id", workspace_id)
47
+ comment = safe_id("comment_id", comment_id)
48
+ get_object("/#{ws}/comments/#{comment}",
49
+ object_class: Models::Comment, unwrap_key: :comment)
50
+ end
51
+
52
+ # Updates a comment's attributes.
53
+ #
54
+ # @param workspace_id [String] the workspace identifier
55
+ # @param comment_id [String] the comment identifier
56
+ # @param params [Hash{Symbol => Object}] the attributes to update
57
+ # @option params [String] :content the new comment content as HTML
58
+ # @option params [String] :status the new comment status
59
+ # @option params [Hash{Symbol => Object}] :context updated context metadata
60
+ # @option params [Hash{Symbol => Object}] :schema updated content schema
61
+ # @return [Superthread::Models::Comment] the updated comment
62
+ def update(workspace_id, comment_id, **params)
63
+ ws = safe_id("workspace_id", workspace_id)
64
+ comment = safe_id("comment_id", comment_id)
65
+ params[:content] = format_mentions(workspace_id, params[:content]) if params[:content]
66
+ patch_object("/#{ws}/comments/#{comment}", body: compact_params(**params),
67
+ object_class: Models::Comment, unwrap_key: :comment)
68
+ end
69
+
70
+ # Deletes a comment permanently.
71
+ #
72
+ # @param workspace_id [String] the workspace identifier
73
+ # @param comment_id [String] the comment identifier to delete
74
+ # @return [Superthread::Object] a response object with success: true
75
+ def destroy(workspace_id, comment_id)
76
+ ws = safe_id("workspace_id", workspace_id)
77
+ comment = safe_id("comment_id", comment_id)
78
+ http_delete("/#{ws}/comments/#{comment}")
79
+ success_response
80
+ end
81
+
82
+ # Replies to a comment, creating a threaded response.
83
+ #
84
+ # @param workspace_id [String] the workspace identifier
85
+ # @param comment_id [String] the parent comment identifier
86
+ # @param content [String] the reply content as HTML
87
+ # @param params [Hash{Symbol => Object}] optional reply parameters
88
+ # @option params [Hash{Symbol => Object}] :schema the content schema for structured content
89
+ # @return [Superthread::Models::Comment] the created reply
90
+ def reply(workspace_id, comment_id, content:, **params)
91
+ ws = safe_id("workspace_id", workspace_id)
92
+ comment = safe_id("comment_id", comment_id)
93
+ content = format_mentions(workspace_id, content)
94
+ body = compact_params(content: content, **params)
95
+ post_object("/#{ws}/comments/#{comment}/children", body: body,
96
+ object_class: Models::Comment, unwrap_key: :child_comment)
97
+ end
98
+
99
+ # Gets replies to a comment.
100
+ #
101
+ # @param workspace_id [String] the workspace identifier
102
+ # @param comment_id [String] the parent comment identifier
103
+ # @return [Superthread::Objects::Collection<Superthread::Models::Comment>] the threaded replies
104
+ def replies(workspace_id, comment_id)
105
+ ws = safe_id("workspace_id", workspace_id)
106
+ comment = safe_id("comment_id", comment_id)
107
+ get_collection("/#{ws}/comments/#{comment}/children",
108
+ item_class: Models::Comment, items_key: :child_comments)
109
+ end
110
+
111
+ # Gets a specific reply by listing all replies on the parent comment
112
+ # and finding the matching one.
113
+ #
114
+ # @param workspace_id [String] the workspace identifier
115
+ # @param comment_id [String] the parent comment identifier
116
+ # @param reply_id [String] the reply identifier to find
117
+ # @return [Superthread::Models::Comment] the reply
118
+ # @raise [Superthread::NotFoundError] if the reply is not found
119
+ def find_reply(workspace_id, comment_id, reply_id)
120
+ all_replies = replies(workspace_id, comment_id)
121
+ found = all_replies.find { |r| r.id == reply_id }
122
+ unless found
123
+ raise Superthread::NotFoundError.new(
124
+ "Reply '#{reply_id}' not found on comment '#{comment_id}'"
125
+ )
126
+ end
127
+ found
128
+ end
129
+
130
+ # Updates a reply's attributes.
131
+ #
132
+ # @param workspace_id [String] the workspace identifier
133
+ # @param comment_id [String] the parent comment identifier
134
+ # @param reply_id [String] the reply identifier
135
+ # @param params [Hash{Symbol => Object}] the attributes to update
136
+ # @option params [String] :content the new reply content as HTML
137
+ # @option params [String] :status the new reply status
138
+ # @return [Superthread::Models::Comment] the updated reply
139
+ def update_reply(workspace_id, comment_id, reply_id, **params)
140
+ ws = safe_id("workspace_id", workspace_id)
141
+ comment = safe_id("comment_id", comment_id)
142
+ reply = safe_id("reply_id", reply_id)
143
+ params[:content] = format_mentions(workspace_id, params[:content]) if params[:content]
144
+ patch_object("/#{ws}/comments/#{comment}/children/#{reply}", body: compact_params(**params),
145
+ object_class: Models::Comment, unwrap_key: :child_comment)
146
+ end
147
+
148
+ # Deletes a reply permanently.
149
+ #
150
+ # @param workspace_id [String] the workspace identifier
151
+ # @param comment_id [String] the parent comment identifier
152
+ # @param reply_id [String] the reply identifier to delete
153
+ # @return [Superthread::Object] a response object with success: true
154
+ def delete_reply(workspace_id, comment_id, reply_id)
155
+ ws = safe_id("workspace_id", workspace_id)
156
+ comment = safe_id("comment_id", comment_id)
157
+ reply = safe_id("reply_id", reply_id)
158
+ http_delete("/#{ws}/comments/#{comment}/children/#{reply}")
159
+ success_response
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Superthread
4
+ module Resources
5
+ # API resource for note operations.
6
+ #
7
+ # Provides methods for listing, creating, and managing notes
8
+ # (meeting notes, transcripts) via the Superthread API.
9
+ class Notes < Base
10
+ # Lists all notes in a workspace.
11
+ #
12
+ # @param workspace_id [String] the workspace identifier
13
+ # @return [Superthread::Objects::Collection<Superthread::Models::Note>] the notes in the workspace
14
+ def list(workspace_id)
15
+ ws = safe_id("workspace_id", workspace_id)
16
+ get_collection("/#{ws}/notes",
17
+ item_class: Models::Note, items_key: :notes)
18
+ end
19
+
20
+ # Gets a specific note.
21
+ #
22
+ # @param workspace_id [String] the workspace identifier
23
+ # @param note_id [String] the note identifier
24
+ # @return [Superthread::Models::Note] the note with all attributes
25
+ def find(workspace_id, note_id)
26
+ ws = safe_id("workspace_id", workspace_id)
27
+ note = safe_id("note_id", note_id)
28
+ get_object("/#{ws}/notes/#{note}",
29
+ object_class: Models::Note, unwrap_key: :note)
30
+ end
31
+
32
+ # Creates a new note.
33
+ #
34
+ # @param workspace_id [String] the workspace identifier
35
+ # @param title [String] the note title
36
+ # @param params [Hash{Symbol => Object}] optional note parameters
37
+ # @option params [String] :transcript the meeting transcript content
38
+ # @option params [Array<Hash{Symbol => Object}>] :transcripts multiple transcript segments
39
+ # @option params [String] :user_notes user-added notes content
40
+ # @return [Superthread::Models::Note] the created note
41
+ def create(workspace_id, title:, **params)
42
+ ws = safe_id("workspace_id", workspace_id)
43
+ body = compact_params(title: title, **params)
44
+ post_object("/#{ws}/notes", body: body,
45
+ object_class: Models::Note, unwrap_key: :note)
46
+ end
47
+
48
+ # Deletes a note permanently.
49
+ #
50
+ # @param workspace_id [String] the workspace identifier
51
+ # @param note_id [String] the note identifier to delete
52
+ # @return [Superthread::Object] a response object with success: true
53
+ def destroy(workspace_id, note_id)
54
+ ws = safe_id("workspace_id", workspace_id)
55
+ note = safe_id("note_id", note_id)
56
+ http_delete("/#{ws}/notes/#{note}")
57
+ success_response
58
+ end
59
+ end
60
+ end
61
+ end