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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +4 -0
- data/LICENSE +21 -0
- data/README.md +492 -0
- data/exe/suth +19 -0
- data/lib/superthread/cli/accounts.rb +240 -0
- data/lib/superthread/cli/activity.rb +210 -0
- data/lib/superthread/cli/base.rb +355 -0
- data/lib/superthread/cli/boards.rb +131 -0
- data/lib/superthread/cli/cards.rb +530 -0
- data/lib/superthread/cli/checklists.rb +223 -0
- data/lib/superthread/cli/comments.rb +86 -0
- data/lib/superthread/cli/completion.rb +306 -0
- data/lib/superthread/cli/concerns/board_resolvable.rb +70 -0
- data/lib/superthread/cli/concerns/confirmable.rb +55 -0
- data/lib/superthread/cli/concerns/date_parsable.rb +196 -0
- data/lib/superthread/cli/concerns/list_resolvable.rb +53 -0
- data/lib/superthread/cli/concerns/space_resolvable.rb +52 -0
- data/lib/superthread/cli/concerns/sprint_resolvable.rb +55 -0
- data/lib/superthread/cli/concerns/tag_resolvable.rb +49 -0
- data/lib/superthread/cli/concerns/user_resolvable.rb +52 -0
- data/lib/superthread/cli/concerns/workspace_resolvable.rb +83 -0
- data/lib/superthread/cli/config.rb +129 -0
- data/lib/superthread/cli/formatter.rb +388 -0
- data/lib/superthread/cli/lists.rb +85 -0
- data/lib/superthread/cli/main.rb +121 -0
- data/lib/superthread/cli/members.rb +19 -0
- data/lib/superthread/cli/notes.rb +64 -0
- data/lib/superthread/cli/pages.rb +128 -0
- data/lib/superthread/cli/projects.rb +124 -0
- data/lib/superthread/cli/replies.rb +94 -0
- data/lib/superthread/cli/search.rb +34 -0
- data/lib/superthread/cli/setup.rb +253 -0
- data/lib/superthread/cli/spaces.rb +141 -0
- data/lib/superthread/cli/sprints.rb +32 -0
- data/lib/superthread/cli/tags.rb +86 -0
- data/lib/superthread/cli/ui/gum_prompt.rb +58 -0
- data/lib/superthread/cli/ui/plain_prompt.rb +73 -0
- data/lib/superthread/cli/ui.rb +263 -0
- data/lib/superthread/cli/workspaces.rb +105 -0
- data/lib/superthread/cli.rb +12 -0
- data/lib/superthread/client.rb +207 -0
- data/lib/superthread/configuration.rb +354 -0
- data/lib/superthread/connection.rb +57 -0
- data/lib/superthread/error.rb +164 -0
- data/lib/superthread/mention_formatter.rb +96 -0
- data/lib/superthread/model.rb +178 -0
- data/lib/superthread/models/board.rb +59 -0
- data/lib/superthread/models/card.rb +321 -0
- data/lib/superthread/models/checklist.rb +91 -0
- data/lib/superthread/models/checklist_item.rb +69 -0
- data/lib/superthread/models/comment.rb +71 -0
- data/lib/superthread/models/concerns/archivable.rb +32 -0
- data/lib/superthread/models/concerns/presentable.rb +113 -0
- data/lib/superthread/models/concerns/timestampable.rb +91 -0
- data/lib/superthread/models/list.rb +67 -0
- data/lib/superthread/models/member.rb +40 -0
- data/lib/superthread/models/note.rb +56 -0
- data/lib/superthread/models/page.rb +70 -0
- data/lib/superthread/models/project.rb +83 -0
- data/lib/superthread/models/space.rb +71 -0
- data/lib/superthread/models/sprint.rb +53 -0
- data/lib/superthread/models/tag.rb +52 -0
- data/lib/superthread/models/team.rb +68 -0
- data/lib/superthread/models/user.rb +76 -0
- data/lib/superthread/models.rb +12 -0
- data/lib/superthread/object.rb +285 -0
- data/lib/superthread/objects/collection.rb +179 -0
- data/lib/superthread/resources/base.rb +204 -0
- data/lib/superthread/resources/boards.rb +150 -0
- data/lib/superthread/resources/cards.rb +363 -0
- data/lib/superthread/resources/comments.rb +163 -0
- data/lib/superthread/resources/notes.rb +61 -0
- data/lib/superthread/resources/pages.rb +110 -0
- data/lib/superthread/resources/projects.rb +117 -0
- data/lib/superthread/resources/search.rb +46 -0
- data/lib/superthread/resources/spaces.rb +104 -0
- data/lib/superthread/resources/sprints.rb +37 -0
- data/lib/superthread/resources/tags.rb +52 -0
- data/lib/superthread/resources/users.rb +29 -0
- data/lib/superthread/version.rb +6 -0
- data/lib/superthread/version_checker.rb +174 -0
- data/lib/superthread.rb +30 -0
- metadata +259 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Superthread
|
|
4
|
+
# Container classes for API response wrappers.
|
|
5
|
+
module Objects
|
|
6
|
+
# Represents a collection of objects returned from list endpoints.
|
|
7
|
+
# Wraps arrays with additional metadata and convenience methods.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# cards = client.cards.assigned(workspace_id, user_id: user_id)
|
|
11
|
+
# cards.each { |card| puts card.title }
|
|
12
|
+
# cards.count # => 5
|
|
13
|
+
# cards.empty? # => false
|
|
14
|
+
# cards.to_a # => [#<Superthread::Models::Card ...>, ...]
|
|
15
|
+
class Collection
|
|
16
|
+
include Enumerable
|
|
17
|
+
|
|
18
|
+
# @return [Array<Superthread::Object, Superthread::Model>] the items in the collection
|
|
19
|
+
attr_reader :items
|
|
20
|
+
|
|
21
|
+
# @return [Hash{Symbol => Object}] raw response data
|
|
22
|
+
attr_reader :data
|
|
23
|
+
|
|
24
|
+
# Creates a new collection from API response data.
|
|
25
|
+
#
|
|
26
|
+
# @param data [Hash{Symbol => Object}] the raw API response
|
|
27
|
+
# @param key [Symbol, String, nil] the key containing the items array (auto-detected if nil)
|
|
28
|
+
# @param item_class [Class, nil] the class to use for items (auto-detected if nil)
|
|
29
|
+
def initialize(data, key: nil, item_class: nil)
|
|
30
|
+
@data = data.is_a?(Hash) ? data.transform_keys(&:to_sym) : {}
|
|
31
|
+
@item_class = item_class
|
|
32
|
+
|
|
33
|
+
# Extract items from the response
|
|
34
|
+
items_data = extract_items(key)
|
|
35
|
+
@items = items_data.map { |item| wrap_item(item) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Constructs a collection from an API response.
|
|
39
|
+
#
|
|
40
|
+
# @param data [Hash{Symbol => Object}, Array<Hash>] the API response
|
|
41
|
+
# @param key [Symbol, String, nil] the key containing items (auto-detected if nil)
|
|
42
|
+
# @param item_class [Class, nil] the class for items (auto-detected if nil)
|
|
43
|
+
# @return [Superthread::Objects::Collection] the constructed collection
|
|
44
|
+
def self.from_response(data, key: nil, item_class: nil)
|
|
45
|
+
# If the response is already an array, wrap it
|
|
46
|
+
if data.is_a?(Array)
|
|
47
|
+
new({items: data}, key: :items, item_class: item_class)
|
|
48
|
+
else
|
|
49
|
+
new(data, key: key, item_class: item_class)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Iterates over items in the collection.
|
|
54
|
+
#
|
|
55
|
+
# @param block [Proc] receives each item in the collection
|
|
56
|
+
# @yieldparam item [Superthread::Object, Superthread::Model] an item
|
|
57
|
+
# @return [Enumerator] if no block given
|
|
58
|
+
def each(&block)
|
|
59
|
+
@items.each(&block)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Returns the number of items in the collection.
|
|
63
|
+
#
|
|
64
|
+
# @return [Integer] item count
|
|
65
|
+
def count
|
|
66
|
+
@items.count
|
|
67
|
+
end
|
|
68
|
+
alias_method :size, :count
|
|
69
|
+
alias_method :length, :count
|
|
70
|
+
|
|
71
|
+
# Checks if the collection is empty.
|
|
72
|
+
#
|
|
73
|
+
# @return [Boolean] true if no items
|
|
74
|
+
def empty?
|
|
75
|
+
@items.empty?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Returns the first item.
|
|
79
|
+
#
|
|
80
|
+
# @return [Superthread::Object, Superthread::Model, nil] first item or nil if empty
|
|
81
|
+
def first
|
|
82
|
+
@items.first
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Returns the last item.
|
|
86
|
+
#
|
|
87
|
+
# @return [Superthread::Object, Superthread::Model, nil] last item or nil if empty
|
|
88
|
+
def last
|
|
89
|
+
@items.last
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Accesses an item by index.
|
|
93
|
+
#
|
|
94
|
+
# @param index [Integer] the zero-based index
|
|
95
|
+
# @return [Superthread::Object, Superthread::Model, nil] item at index or nil if out of bounds
|
|
96
|
+
def [](index)
|
|
97
|
+
@items[index]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Converts the collection to an array.
|
|
101
|
+
#
|
|
102
|
+
# @return [Array<Superthread::Object, Superthread::Model>] copy of items array
|
|
103
|
+
def to_a
|
|
104
|
+
@items.dup
|
|
105
|
+
end
|
|
106
|
+
alias_method :to_ary, :to_a
|
|
107
|
+
|
|
108
|
+
# Converts the collection to an array of hashes.
|
|
109
|
+
#
|
|
110
|
+
# @return [Array<Hash{Symbol => Object}>] array of hash representations
|
|
111
|
+
def to_h
|
|
112
|
+
@items.map(&:to_h)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Returns raw response metadata (everything except items).
|
|
116
|
+
#
|
|
117
|
+
# @return [Hash{Symbol => Object}] metadata from the API response
|
|
118
|
+
def metadata
|
|
119
|
+
@data.except(*items_keys)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
# Common keys that contain item arrays in API responses.
|
|
125
|
+
ITEMS_KEYS = %i[items cards boards lists users projects spaces sprints
|
|
126
|
+
pages notes comments tags members results data].freeze
|
|
127
|
+
|
|
128
|
+
# Returns the list of common item keys.
|
|
129
|
+
#
|
|
130
|
+
# @return [Array<Symbol>] keys that typically contain item arrays
|
|
131
|
+
def items_keys
|
|
132
|
+
ITEMS_KEYS
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Extracts items array from the response data.
|
|
136
|
+
#
|
|
137
|
+
# @param key [Symbol, String, nil] the key to extract, or nil to auto-detect
|
|
138
|
+
# @return [Array<Hash>] the extracted items array
|
|
139
|
+
def extract_items(key)
|
|
140
|
+
if key
|
|
141
|
+
@data[key.to_sym] || []
|
|
142
|
+
else
|
|
143
|
+
# Auto-detect items key
|
|
144
|
+
ITEMS_KEYS.each do |k|
|
|
145
|
+
return @data[k] if @data[k].is_a?(Array)
|
|
146
|
+
end
|
|
147
|
+
# If nothing found, check if the response itself has a single array value
|
|
148
|
+
@data.values.find { |v| v.is_a?(Array) } || []
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Wraps a hash item in the appropriate object class.
|
|
153
|
+
#
|
|
154
|
+
# @param item [Hash, Object] the item to wrap
|
|
155
|
+
# @return [Superthread::Object, Superthread::Model] the wrapped item
|
|
156
|
+
def wrap_item(item)
|
|
157
|
+
return item unless item.is_a?(Hash)
|
|
158
|
+
|
|
159
|
+
if @item_class
|
|
160
|
+
if shale_model?(@item_class)
|
|
161
|
+
@item_class.from_response(item)
|
|
162
|
+
else
|
|
163
|
+
@item_class.new(item)
|
|
164
|
+
end
|
|
165
|
+
else
|
|
166
|
+
Superthread::Object.construct_from(item)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Checks if a class is a Shale model.
|
|
171
|
+
#
|
|
172
|
+
# @param klass [Class] the class to check
|
|
173
|
+
# @return [Boolean] true if the class is a Shale model
|
|
174
|
+
def shale_model?(klass)
|
|
175
|
+
Superthread::Model.shale_class?(klass)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Superthread
|
|
4
|
+
# API resource classes for interacting with Superthread endpoints.
|
|
5
|
+
module Resources
|
|
6
|
+
# Base class for all API resources.
|
|
7
|
+
#
|
|
8
|
+
# Provides HTTP helpers, ID validation, and response handling shared
|
|
9
|
+
# by all resource classes. Subclasses should use the protected HTTP
|
|
10
|
+
# methods to interact with the Superthread API.
|
|
11
|
+
class Base
|
|
12
|
+
# Initializes a new resource instance.
|
|
13
|
+
#
|
|
14
|
+
# @param client [Superthread::Client] the API client for making requests
|
|
15
|
+
def initialize(client)
|
|
16
|
+
@client = client
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# Performs a GET request and returns raw response data.
|
|
22
|
+
#
|
|
23
|
+
# @param path [String] the API endpoint path
|
|
24
|
+
# @param params [Hash{Symbol => Object}, nil] optional query parameters
|
|
25
|
+
# @return [Hash{Symbol => Object}] the parsed response body
|
|
26
|
+
def http_get(path, params: nil)
|
|
27
|
+
@client.request(method: :get, path: path, params: params)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Performs a POST request and returns raw response data.
|
|
31
|
+
#
|
|
32
|
+
# @param path [String] the API endpoint path
|
|
33
|
+
# @param body [Hash{Symbol => Object}, nil] optional request body
|
|
34
|
+
# @return [Hash{Symbol => Object}] the parsed response body
|
|
35
|
+
def http_post(path, body: nil)
|
|
36
|
+
@client.request(method: :post, path: path, body: body)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Performs a PATCH request and returns raw response data.
|
|
40
|
+
#
|
|
41
|
+
# @param path [String] the API endpoint path
|
|
42
|
+
# @param body [Hash{Symbol => Object}, nil] optional request body
|
|
43
|
+
# @return [Hash{Symbol => Object}] the parsed response body
|
|
44
|
+
def http_patch(path, body: nil)
|
|
45
|
+
@client.request(method: :patch, path: path, body: body)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Performs a DELETE request and returns raw response data.
|
|
49
|
+
#
|
|
50
|
+
# @param path [String] the API endpoint path
|
|
51
|
+
# @return [Hash{Symbol => Object}] the parsed response body
|
|
52
|
+
def http_delete(path)
|
|
53
|
+
@client.request(method: :delete, path: path)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Performs a GET request and returns a typed object.
|
|
57
|
+
#
|
|
58
|
+
# @param path [String] the API endpoint path
|
|
59
|
+
# @param params [Hash{Symbol => Object}, nil] optional query parameters
|
|
60
|
+
# @param object_class [Class, nil] the model class to instantiate
|
|
61
|
+
# @param unwrap_key [Symbol, nil] key to extract from response before wrapping
|
|
62
|
+
# @return [Superthread::Object] the response wrapped in an object
|
|
63
|
+
def get_object(path, params: nil, object_class: nil, unwrap_key: nil)
|
|
64
|
+
@client.request_object(
|
|
65
|
+
method: :get, path: path, params: params,
|
|
66
|
+
object_class: object_class, unwrap_key: unwrap_key
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Performs a POST request and returns a typed object.
|
|
71
|
+
#
|
|
72
|
+
# @param path [String] the API endpoint path
|
|
73
|
+
# @param body [Hash{Symbol => Object}, nil] optional request body
|
|
74
|
+
# @param object_class [Class, nil] the model class to instantiate
|
|
75
|
+
# @param unwrap_key [Symbol, nil] key to extract from response before wrapping
|
|
76
|
+
# @return [Superthread::Object] the response wrapped in an object
|
|
77
|
+
def post_object(path, body: nil, object_class: nil, unwrap_key: nil)
|
|
78
|
+
@client.request_object(
|
|
79
|
+
method: :post, path: path, body: body,
|
|
80
|
+
object_class: object_class, unwrap_key: unwrap_key
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Performs a PATCH request and returns a typed object.
|
|
85
|
+
#
|
|
86
|
+
# @param path [String] the API endpoint path
|
|
87
|
+
# @param body [Hash{Symbol => Object}, nil] optional request body
|
|
88
|
+
# @param object_class [Class, nil] the model class to instantiate
|
|
89
|
+
# @param unwrap_key [Symbol, nil] key to extract from response before wrapping
|
|
90
|
+
# @return [Superthread::Object] the response wrapped in an object
|
|
91
|
+
def patch_object(path, body: nil, object_class: nil, unwrap_key: nil)
|
|
92
|
+
@client.request_object(
|
|
93
|
+
method: :patch, path: path, body: body,
|
|
94
|
+
object_class: object_class, unwrap_key: unwrap_key
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Performs a DELETE request and returns a typed object.
|
|
99
|
+
#
|
|
100
|
+
# @param path [String] the API endpoint path
|
|
101
|
+
# @param object_class [Class, nil] the model class to instantiate
|
|
102
|
+
# @param unwrap_key [Symbol, nil] key to extract from response before wrapping
|
|
103
|
+
# @return [Superthread::Object] the response wrapped in an object
|
|
104
|
+
def delete_object(path, object_class: nil, unwrap_key: nil)
|
|
105
|
+
@client.request_object(
|
|
106
|
+
method: :delete, path: path,
|
|
107
|
+
object_class: object_class, unwrap_key: unwrap_key
|
|
108
|
+
)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Performs a GET request and returns a collection of objects.
|
|
112
|
+
#
|
|
113
|
+
# @param path [String] the API endpoint path
|
|
114
|
+
# @param params [Hash{Symbol => Object}, nil] optional query parameters
|
|
115
|
+
# @param item_class [Class, nil] the model class for collection items
|
|
116
|
+
# @param items_key [Symbol, nil] key containing the array in response
|
|
117
|
+
# @return [Superthread::Objects::Collection] the collection of items
|
|
118
|
+
def get_collection(path, params: nil, item_class: nil, items_key: nil)
|
|
119
|
+
@client.request_collection(
|
|
120
|
+
method: :get, path: path, params: params,
|
|
121
|
+
item_class: item_class, items_key: items_key
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Performs a POST request and returns a collection of objects.
|
|
126
|
+
#
|
|
127
|
+
# @param path [String] the API endpoint path
|
|
128
|
+
# @param body [Hash{Symbol => Object}, nil] optional request body
|
|
129
|
+
# @param item_class [Class, nil] the model class for collection items
|
|
130
|
+
# @param items_key [Symbol, nil] key containing the array in response
|
|
131
|
+
# @return [Superthread::Objects::Collection] the collection of items
|
|
132
|
+
def post_collection(path, body: nil, item_class: nil, items_key: nil)
|
|
133
|
+
@client.request_collection(
|
|
134
|
+
method: :post, path: path, body: body,
|
|
135
|
+
item_class: item_class, items_key: items_key
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Validates and sanitizes an ID to prevent path traversal attacks.
|
|
140
|
+
#
|
|
141
|
+
# Only allows alphanumeric characters, hyphens, and underscores.
|
|
142
|
+
#
|
|
143
|
+
# @param name [String] descriptive name for error messages (e.g., "workspace_id")
|
|
144
|
+
# @param value [String] the ID value to validate
|
|
145
|
+
# @return [String] the sanitized ID
|
|
146
|
+
# @raise [Superthread::PathValidationError] if the value is nil, empty, or contains invalid characters
|
|
147
|
+
# @example
|
|
148
|
+
# safe_id("workspace_id", "ws-123_abc") # => "ws-123_abc"
|
|
149
|
+
# safe_id("card_id", "../evil") # raises PathValidationError
|
|
150
|
+
def safe_id(name, value)
|
|
151
|
+
raise Superthread::PathValidationError, "#{name} must be a non-empty string" if value.nil? || value.to_s.empty?
|
|
152
|
+
|
|
153
|
+
cleaned = value.to_s.strip.gsub(/[^a-zA-Z0-9_-]/, "")
|
|
154
|
+
|
|
155
|
+
if cleaned.empty?
|
|
156
|
+
raise Superthread::PathValidationError,
|
|
157
|
+
"#{name} must contain only letters, numbers, hyphen, or underscore"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
cleaned
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Filters nil values from a params hash.
|
|
164
|
+
#
|
|
165
|
+
# Accepts arbitrary keyword arguments and returns only non-nil values.
|
|
166
|
+
# Used internally to clean up API request parameters.
|
|
167
|
+
#
|
|
168
|
+
# @param args [Hash{Symbol => Object}] arbitrary key-value pairs to filter
|
|
169
|
+
# @option args [Object] :any any key-value pair (nil values will be removed)
|
|
170
|
+
# @return [Hash{Symbol => Object}] hash with nil values removed
|
|
171
|
+
def compact_params(**args)
|
|
172
|
+
args.compact
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Formats {{@mentions}} in content to HTML user-mention tags.
|
|
176
|
+
#
|
|
177
|
+
# @param workspace_id [String] the workspace identifier for member lookup
|
|
178
|
+
# @param content [String, nil] text that may contain {{@Name}} patterns
|
|
179
|
+
# @return [String, nil] content with mentions converted to HTML tags
|
|
180
|
+
def format_mentions(workspace_id, content)
|
|
181
|
+
return content if content.nil?
|
|
182
|
+
|
|
183
|
+
MentionFormatter.new(@client, workspace_id).format(content)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Builds an API path prefixed with the workspace ID.
|
|
187
|
+
#
|
|
188
|
+
# @param workspace_id [String] the workspace identifier
|
|
189
|
+
# @param path [String] additional path segments to append
|
|
190
|
+
# @return [String] the full API path (e.g., "/ws-123/cards")
|
|
191
|
+
def workspace_path(workspace_id, path = "")
|
|
192
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
193
|
+
"/#{ws}#{path}"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Returns a success response object for delete operations.
|
|
197
|
+
#
|
|
198
|
+
# @return [Superthread::Object] a response object with success: true
|
|
199
|
+
def success_response
|
|
200
|
+
Superthread::Object.new(success: true)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Superthread
|
|
4
|
+
module Resources
|
|
5
|
+
# API resource for board operations.
|
|
6
|
+
#
|
|
7
|
+
# Provides methods for creating, listing, updating, and deleting boards
|
|
8
|
+
# and their lists (columns) via the Superthread API.
|
|
9
|
+
class Boards < Base
|
|
10
|
+
# Creates a new board in a space.
|
|
11
|
+
#
|
|
12
|
+
# @param workspace_id [String] the workspace identifier
|
|
13
|
+
# @param space_id [String] the space identifier (maps to project_id in API)
|
|
14
|
+
# @param title [String] the board title
|
|
15
|
+
# @param params [Hash{Symbol => Object}] optional board parameters
|
|
16
|
+
# @option params [String] :content the board description content
|
|
17
|
+
# @option params [String] :icon the board icon identifier
|
|
18
|
+
# @option params [String] :color the board color hex code
|
|
19
|
+
# @option params [String] :layout the board layout type
|
|
20
|
+
# @return [Superthread::Models::Board] the created board
|
|
21
|
+
def create(workspace_id, space_id:, title:, **params)
|
|
22
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
23
|
+
body = compact_params(title: title, project_id: space_id, **params)
|
|
24
|
+
post_object("/#{ws}/boards", body: body,
|
|
25
|
+
object_class: Models::Board, unwrap_key: :board)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Lists all boards in a space.
|
|
29
|
+
#
|
|
30
|
+
# @param workspace_id [String] the workspace identifier
|
|
31
|
+
# @param space_id [String] the space identifier to filter by
|
|
32
|
+
# @param bookmarked [Boolean, nil] when true, returns only bookmarked boards
|
|
33
|
+
# @param archived [Boolean, nil] when true, includes archived boards
|
|
34
|
+
# @return [Superthread::Objects::Collection<Superthread::Models::Board>] the boards in the space
|
|
35
|
+
def list(workspace_id, space_id:, bookmarked: nil, archived: nil)
|
|
36
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
37
|
+
params = compact_params(project_id: space_id, bookmarked: bookmarked, archived: archived)
|
|
38
|
+
get_collection("/#{ws}/boards", params: params,
|
|
39
|
+
item_class: Models::Board, items_key: :boards)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Gets a specific board with its lists and cards.
|
|
43
|
+
#
|
|
44
|
+
# @param workspace_id [String] the workspace identifier
|
|
45
|
+
# @param board_id [String] the board identifier
|
|
46
|
+
# @return [Superthread::Models::Board] the board with nested lists and cards
|
|
47
|
+
def find(workspace_id, board_id)
|
|
48
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
49
|
+
board = safe_id("board_id", board_id)
|
|
50
|
+
get_object("/#{ws}/boards/#{board}",
|
|
51
|
+
object_class: Models::Board, unwrap_key: :board)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Updates a board's attributes.
|
|
55
|
+
#
|
|
56
|
+
# @param workspace_id [String] the workspace identifier
|
|
57
|
+
# @param board_id [String] the board identifier
|
|
58
|
+
# @param params [Hash{Symbol => Object}] the attributes to update
|
|
59
|
+
# @option params [String] :title the new board title
|
|
60
|
+
# @option params [String] :content the new board description
|
|
61
|
+
# @option params [String] :icon the new board icon identifier
|
|
62
|
+
# @option params [String] :color the new board color hex code
|
|
63
|
+
# @option params [Boolean] :archived whether the board is archived
|
|
64
|
+
# @return [Superthread::Models::Board] the updated board
|
|
65
|
+
def update(workspace_id, board_id, **params)
|
|
66
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
67
|
+
board = safe_id("board_id", board_id)
|
|
68
|
+
patch_object("/#{ws}/boards/#{board}", body: compact_params(**params),
|
|
69
|
+
object_class: Models::Board, unwrap_key: :board)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Duplicates a board to a destination space.
|
|
73
|
+
#
|
|
74
|
+
# @param workspace_id [String] the workspace identifier
|
|
75
|
+
# @param board_id [String] the board identifier to duplicate
|
|
76
|
+
# @param space_id [String] the destination space identifier
|
|
77
|
+
# @param title [String, nil] the title for the duplicated board (defaults to original)
|
|
78
|
+
# @param copy_cards [Boolean, nil] when true, copies all cards to the new board
|
|
79
|
+
# @param create_missing_tags [Boolean, nil] when true, creates tags that do not exist in target space
|
|
80
|
+
# @return [Superthread::Models::Board] the duplicated board
|
|
81
|
+
def duplicate(workspace_id, board_id, space_id:, title: nil, copy_cards: nil, create_missing_tags: nil)
|
|
82
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
83
|
+
board = safe_id("board_id", board_id)
|
|
84
|
+
body = compact_params(project_id: space_id, title: title, copy_cards: copy_cards, create_missing_tags: create_missing_tags)
|
|
85
|
+
post_object("/#{ws}/boards/#{board}/copy", body: body,
|
|
86
|
+
object_class: Models::Board, unwrap_key: :board)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Deletes a board permanently.
|
|
90
|
+
#
|
|
91
|
+
# @param workspace_id [String] the workspace identifier
|
|
92
|
+
# @param board_id [String] the board identifier to delete
|
|
93
|
+
# @return [Superthread::Object] a response object with success: true
|
|
94
|
+
def destroy(workspace_id, board_id)
|
|
95
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
96
|
+
board = safe_id("board_id", board_id)
|
|
97
|
+
http_delete("/#{ws}/boards/#{board}")
|
|
98
|
+
success_response
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Creates a list (column) on a board.
|
|
102
|
+
#
|
|
103
|
+
# @param workspace_id [String] the workspace identifier
|
|
104
|
+
# @param board_id [String] the board identifier to add the list to
|
|
105
|
+
# @param title [String] the list title
|
|
106
|
+
# @param params [Hash{Symbol => Object}] optional list parameters
|
|
107
|
+
# @option params [String] :content the list description content
|
|
108
|
+
# @option params [String] :icon the list icon identifier
|
|
109
|
+
# @option params [String] :color the list color hex code
|
|
110
|
+
# @option params [String] :behavior the list behavior type (e.g., "done")
|
|
111
|
+
# @return [Superthread::Models::List] the created list
|
|
112
|
+
def create_list(workspace_id, board_id:, title:, **params)
|
|
113
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
114
|
+
body = compact_params(board_id: board_id, title: title, **params)
|
|
115
|
+
post_object("/#{ws}/lists", body: body,
|
|
116
|
+
object_class: Models::List, unwrap_key: :list)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Updates a list's attributes.
|
|
120
|
+
#
|
|
121
|
+
# @param workspace_id [String] the workspace identifier
|
|
122
|
+
# @param list_id [String] the list identifier
|
|
123
|
+
# @param params [Hash{Symbol => Object}] the attributes to update
|
|
124
|
+
# @option params [String] :title the new list title
|
|
125
|
+
# @option params [String] :content the new list description
|
|
126
|
+
# @option params [String] :icon the new list icon identifier
|
|
127
|
+
# @option params [String] :color the new list color hex code
|
|
128
|
+
# @option params [String] :behavior the new list behavior type
|
|
129
|
+
# @return [Superthread::Models::List] the updated list
|
|
130
|
+
def update_list(workspace_id, list_id, **params)
|
|
131
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
132
|
+
list = safe_id("list_id", list_id)
|
|
133
|
+
patch_object("/#{ws}/lists/#{list}", body: compact_params(**params),
|
|
134
|
+
object_class: Models::List, unwrap_key: :list)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Deletes a list permanently.
|
|
138
|
+
#
|
|
139
|
+
# @param workspace_id [String] the workspace identifier
|
|
140
|
+
# @param list_id [String] the list identifier to delete
|
|
141
|
+
# @return [Superthread::Object] a response object with success: true
|
|
142
|
+
def delete_list(workspace_id, list_id)
|
|
143
|
+
ws = safe_id("workspace_id", workspace_id)
|
|
144
|
+
list = safe_id("list_id", list_id)
|
|
145
|
+
http_delete("/#{ws}/lists/#{list}")
|
|
146
|
+
success_response
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|