basecamp-sdk 0.4.0 → 0.6.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/lib/basecamp/ambiguous_error.rb +23 -0
  4. data/lib/basecamp/api_error.rb +27 -0
  5. data/lib/basecamp/auth_error.rb +16 -0
  6. data/lib/basecamp/auth_strategy.rb +0 -18
  7. data/lib/basecamp/bearer_auth.rb +21 -0
  8. data/lib/basecamp/client.rb +126 -0
  9. data/lib/basecamp/download_result.rb +10 -0
  10. data/lib/basecamp/error.rb +86 -0
  11. data/lib/basecamp/error_code.rb +16 -0
  12. data/lib/basecamp/exit_code.rb +17 -0
  13. data/lib/basecamp/forbidden_error.rb +20 -0
  14. data/lib/basecamp/generated/metadata.json +12 -1
  15. data/lib/basecamp/{services → generated/services}/authorization_service.rb +1 -1
  16. data/lib/basecamp/generated/services/automation_service.rb +19 -0
  17. data/lib/basecamp/{services → generated/services}/base_service.rb +0 -1
  18. data/lib/basecamp/generated/services/card_steps_service.rb +9 -0
  19. data/lib/basecamp/generated/services/messages_service.rb +5 -2
  20. data/lib/basecamp/generated/services/search_service.rb +1 -1
  21. data/lib/basecamp/generated/services/tools_service.rb +3 -2
  22. data/lib/basecamp/generated/types.rb +3 -3
  23. data/lib/basecamp/http.rb +22 -13
  24. data/lib/basecamp/network_error.rb +16 -0
  25. data/lib/basecamp/not_found_error.rb +20 -0
  26. data/lib/basecamp/oauth/config.rb +30 -0
  27. data/lib/basecamp/oauth/discovery.rb +9 -41
  28. data/lib/basecamp/oauth/exchange.rb +20 -114
  29. data/lib/basecamp/oauth/exchange_request.rb +36 -0
  30. data/lib/basecamp/oauth/{errors.rb → oauth_error.rb} +1 -1
  31. data/lib/basecamp/oauth/refresh_request.rb +30 -0
  32. data/lib/basecamp/oauth/token.rb +52 -0
  33. data/lib/basecamp/oauth.rb +40 -8
  34. data/lib/basecamp/operation_info.rb +0 -7
  35. data/lib/basecamp/operation_result.rb +10 -0
  36. data/lib/basecamp/rate_limit_error.rb +19 -0
  37. data/lib/basecamp/security.rb +1 -1
  38. data/lib/basecamp/usage_error.rb +10 -0
  39. data/lib/basecamp/validation_error.rb +15 -0
  40. data/lib/basecamp/version.rb +1 -1
  41. data/lib/basecamp/webhooks/rack_middleware.rb +0 -2
  42. data/lib/basecamp/webhooks/receiver.rb +0 -4
  43. data/lib/basecamp/webhooks/verification_error.rb +7 -0
  44. data/lib/basecamp.rb +62 -22
  45. data/scripts/generate-services.rb +3 -3
  46. metadata +26 -43
  47. data/lib/basecamp/errors.rb +0 -294
  48. data/lib/basecamp/oauth/types.rb +0 -133
  49. data/lib/basecamp/services/attachments_service.rb +0 -33
  50. data/lib/basecamp/services/campfires_service.rb +0 -141
  51. data/lib/basecamp/services/card_columns_service.rb +0 -106
  52. data/lib/basecamp/services/card_steps_service.rb +0 -86
  53. data/lib/basecamp/services/card_tables_service.rb +0 -23
  54. data/lib/basecamp/services/cards_service.rb +0 -93
  55. data/lib/basecamp/services/checkins_service.rb +0 -127
  56. data/lib/basecamp/services/client_approvals_service.rb +0 -33
  57. data/lib/basecamp/services/client_correspondences_service.rb +0 -33
  58. data/lib/basecamp/services/client_replies_service.rb +0 -35
  59. data/lib/basecamp/services/comments_service.rb +0 -63
  60. data/lib/basecamp/services/documents_service.rb +0 -74
  61. data/lib/basecamp/services/events_service.rb +0 -27
  62. data/lib/basecamp/services/forwards_service.rb +0 -80
  63. data/lib/basecamp/services/lineup_service.rb +0 -67
  64. data/lib/basecamp/services/message_boards_service.rb +0 -24
  65. data/lib/basecamp/services/message_types_service.rb +0 -79
  66. data/lib/basecamp/services/messages_service.rb +0 -133
  67. data/lib/basecamp/services/people_service.rb +0 -73
  68. data/lib/basecamp/services/projects_service.rb +0 -67
  69. data/lib/basecamp/services/recordings_service.rb +0 -127
  70. data/lib/basecamp/services/reports_service.rb +0 -80
  71. data/lib/basecamp/services/schedules_service.rb +0 -156
  72. data/lib/basecamp/services/search_service.rb +0 -36
  73. data/lib/basecamp/services/subscriptions_service.rb +0 -67
  74. data/lib/basecamp/services/templates_service.rb +0 -96
  75. data/lib/basecamp/services/timeline_service.rb +0 -62
  76. data/lib/basecamp/services/timesheet_service.rb +0 -68
  77. data/lib/basecamp/services/todolist_groups_service.rb +0 -100
  78. data/lib/basecamp/services/todolists_service.rb +0 -104
  79. data/lib/basecamp/services/todos_service.rb +0 -156
  80. data/lib/basecamp/services/todosets_service.rb +0 -23
  81. data/lib/basecamp/services/tools_service.rb +0 -89
  82. data/lib/basecamp/services/uploads_service.rb +0 -84
  83. data/lib/basecamp/services/vaults_service.rb +0 -84
  84. data/lib/basecamp/services/webhooks_service.rb +0 -88
@@ -1,294 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Error types and codes for the Basecamp SDK.
4
- module Basecamp
5
- # Error codes for API responses
6
- module ErrorCode
7
- USAGE = "usage"
8
- NOT_FOUND = "not_found"
9
- AUTH = "auth_required"
10
- FORBIDDEN = "forbidden"
11
- RATE_LIMIT = "rate_limit"
12
- NETWORK = "network"
13
- API = "api_error"
14
- AMBIGUOUS = "ambiguous"
15
- VALIDATION = "validation"
16
- end
17
-
18
- # Exit codes for CLI tools
19
- module ExitCode
20
- OK = 0
21
- USAGE = 1
22
- NOT_FOUND = 2
23
- AUTH = 3
24
- FORBIDDEN = 4
25
- RATE_LIMIT = 5
26
- NETWORK = 6
27
- API = 7
28
- AMBIGUOUS = 8
29
- VALIDATION = 9
30
- end
31
-
32
- # Base error class for all Basecamp SDK errors.
33
- # Provides structured error handling with codes, hints, and CLI exit codes.
34
- #
35
- # @example Catching errors
36
- # begin
37
- # client.projects.list
38
- # rescue Basecamp::Error => e
39
- # puts "#{e.code}: #{e.message}"
40
- # puts "Hint: #{e.hint}" if e.hint
41
- # exit e.exit_code
42
- # end
43
- class Error < StandardError
44
- # @return [String] error category code
45
- attr_reader :code
46
-
47
- # @return [String, nil] user-friendly hint for resolving the error
48
- attr_reader :hint
49
-
50
- # @return [Integer, nil] HTTP status code that caused the error
51
- attr_reader :http_status
52
-
53
- # @return [Boolean] whether the operation can be retried
54
- attr_reader :retryable
55
-
56
- # @return [Integer, nil] seconds to wait before retrying (for rate limits)
57
- attr_reader :retry_after
58
-
59
- # @return [String, nil] X-Request-Id from the response
60
- attr_reader :request_id
61
-
62
- # @return [Exception, nil] original error that caused this error
63
- attr_reader :cause
64
-
65
- # @param code [String] error category code
66
- # @param message [String] error message
67
- # @param hint [String, nil] user-friendly hint
68
- # @param http_status [Integer, nil] HTTP status code
69
- # @param retryable [Boolean] whether operation can be retried
70
- # @param retry_after [Integer, nil] seconds to wait before retry
71
- # @param request_id [String, nil] X-Request-Id from response
72
- # @param cause [Exception, nil] underlying cause
73
- def initialize(code:, message:, hint: nil, http_status: nil, retryable: false, retry_after: nil, request_id: nil, cause: nil)
74
- super(message)
75
- @code = code
76
- @hint = hint
77
- @http_status = http_status
78
- @retryable = retryable
79
- @retry_after = retry_after
80
- @request_id = request_id
81
- @cause = cause
82
- end
83
-
84
- # Returns the exit code for CLI applications.
85
- # @return [Integer]
86
- def exit_code
87
- self.class.exit_code_for(@code)
88
- end
89
-
90
- # Returns whether this error can be retried.
91
- # @return [Boolean]
92
- def retryable?
93
- @retryable
94
- end
95
-
96
- # Maps error codes to exit codes.
97
- # @param code [String]
98
- # @return [Integer]
99
- def self.exit_code_for(code)
100
- case code
101
- when ErrorCode::USAGE then ExitCode::USAGE
102
- when ErrorCode::NOT_FOUND then ExitCode::NOT_FOUND
103
- when ErrorCode::AUTH then ExitCode::AUTH
104
- when ErrorCode::FORBIDDEN then ExitCode::FORBIDDEN
105
- when ErrorCode::RATE_LIMIT then ExitCode::RATE_LIMIT
106
- when ErrorCode::NETWORK then ExitCode::NETWORK
107
- when ErrorCode::API then ExitCode::API
108
- when ErrorCode::AMBIGUOUS then ExitCode::AMBIGUOUS
109
- when ErrorCode::VALIDATION then ExitCode::VALIDATION
110
- else ExitCode::API
111
- end
112
- end
113
- end
114
-
115
- # Raised when there's a usage error (invalid arguments, missing config).
116
- class UsageError < Error
117
- def initialize(message, hint: nil)
118
- super(code: ErrorCode::USAGE, message: message, hint: hint)
119
- end
120
- end
121
-
122
- # Raised when a resource is not found (404).
123
- class NotFoundError < Error
124
- def initialize(resource, identifier, hint: nil)
125
- super(
126
- code: ErrorCode::NOT_FOUND,
127
- message: "#{resource} not found: #{identifier}",
128
- hint: hint,
129
- http_status: 404
130
- )
131
- end
132
- end
133
-
134
- # Raised when authentication fails (401).
135
- class AuthError < Error
136
- def initialize(message = "Authentication required", hint: nil, cause: nil)
137
- super(
138
- code: ErrorCode::AUTH,
139
- message: message,
140
- hint: hint || "Check your access token or refresh it if expired",
141
- http_status: 401,
142
- cause: cause
143
- )
144
- end
145
- end
146
-
147
- # Raised when access is denied (403).
148
- class ForbiddenError < Error
149
- def initialize(message = "Access denied", hint: nil)
150
- super(
151
- code: ErrorCode::FORBIDDEN,
152
- message: message,
153
- hint: hint || "You do not have permission to access this resource",
154
- http_status: 403
155
- )
156
- end
157
-
158
- # Creates a forbidden error due to insufficient OAuth scope.
159
- def self.insufficient_scope
160
- new("Access denied: insufficient scope", hint: "Re-authenticate with full scope")
161
- end
162
- end
163
-
164
- # Raised when rate limited (429).
165
- class RateLimitError < Error
166
- def initialize(retry_after: nil, cause: nil)
167
- hint = retry_after ? "Try again in #{retry_after} seconds" : "Please slow down requests"
168
- super(
169
- code: ErrorCode::RATE_LIMIT,
170
- message: "Rate limit exceeded",
171
- hint: hint,
172
- http_status: 429,
173
- retryable: true,
174
- retry_after: retry_after,
175
- cause: cause
176
- )
177
- end
178
- end
179
-
180
- # Raised when there's a network error (connection, timeout, DNS).
181
- class NetworkError < Error
182
- def initialize(message = "Network error", cause: nil)
183
- super(
184
- code: ErrorCode::NETWORK,
185
- message: message,
186
- hint: cause&.message || "Check your network connection",
187
- retryable: true,
188
- cause: cause
189
- )
190
- end
191
- end
192
-
193
- # Raised for generic API errors.
194
- class APIError < Error
195
- def initialize(message, http_status: nil, hint: nil, retryable: false, cause: nil)
196
- super(
197
- code: ErrorCode::API,
198
- message: message,
199
- hint: hint,
200
- http_status: http_status,
201
- retryable: retryable,
202
- cause: cause
203
- )
204
- end
205
-
206
- # Creates an APIError from an HTTP status code.
207
- # @param status [Integer] HTTP status code
208
- # @param message [String, nil] optional error message
209
- # @return [APIError]
210
- def self.from_status(status, message = nil)
211
- message ||= "Request failed (HTTP #{status})"
212
- retryable = status >= 500 && status < 600
213
- new(message, http_status: status, retryable: retryable)
214
- end
215
- end
216
-
217
- # Raised when a name/identifier matches multiple resources.
218
- class AmbiguousError < Error
219
- # @return [Array<String>] list of matching resources
220
- attr_reader :matches
221
-
222
- def initialize(resource, matches: [])
223
- @matches = matches
224
- hint = if matches.any? && matches.length <= 5
225
- "Did you mean: #{matches.join(", ")}"
226
- else
227
- "Be more specific"
228
- end
229
- super(
230
- code: ErrorCode::AMBIGUOUS,
231
- message: "Ambiguous #{resource}",
232
- hint: hint
233
- )
234
- end
235
- end
236
-
237
- # Raised for validation errors (400, 422).
238
- class ValidationError < Error
239
- def initialize(message, hint: nil, http_status: 400)
240
- super(
241
- code: ErrorCode::VALIDATION,
242
- message: message,
243
- hint: hint,
244
- http_status: http_status
245
- )
246
- end
247
- end
248
-
249
- # Maps an HTTP response to the appropriate error class.
250
- #
251
- # @param status [Integer] HTTP status code
252
- # @param body [String, nil] response body (will attempt JSON parse)
253
- # @param retry_after [Integer, nil] Retry-After header value
254
- # @return [Error]
255
- def self.error_from_response(status, body = nil, retry_after: nil)
256
- message = parse_error_message(body) || "Request failed"
257
-
258
- case status
259
- when 400, 422
260
- ValidationError.new(message, http_status: status)
261
- when 401
262
- AuthError.new(message)
263
- when 403
264
- ForbiddenError.new(message)
265
- when 404
266
- NotFoundError.new("Resource", "unknown")
267
- when 429
268
- RateLimitError.new(retry_after: retry_after)
269
- when 500
270
- APIError.new("Server error (500)", http_status: 500, retryable: true)
271
- when 502, 503, 504
272
- APIError.new("Gateway error (#{status})", http_status: status, retryable: true)
273
- else
274
- APIError.from_status(status, message)
275
- end
276
- end
277
-
278
- # Parses error message from response body.
279
- # @param body [String, nil]
280
- # @return [String, nil]
281
- def self.parse_error_message(body)
282
- return nil if body.nil? || body.empty?
283
-
284
- # Guard against oversized error bodies before parsing
285
- Basecamp::Security.check_body_size!(body, Basecamp::Security::MAX_ERROR_BODY_BYTES, "Error")
286
-
287
- data = JSON.parse(body)
288
- msg = data["error"] || data["message"]
289
- msg ? Basecamp::Security.truncate(msg) : nil
290
- rescue JSON::ParserError, Basecamp::APIError
291
- # Return nil on parse errors or oversized bodies to preserve normal error type mapping
292
- nil
293
- end
294
- end
@@ -1,133 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basecamp
4
- module Oauth
5
- # OAuth 2 server configuration from discovery endpoint.
6
- #
7
- # @attr issuer [String] The authorization server's issuer identifier
8
- # @attr authorization_endpoint [String] URL of the authorization endpoint
9
- # @attr token_endpoint [String] URL of the token endpoint
10
- # @attr registration_endpoint [String, nil] URL of the dynamic client registration endpoint
11
- # @attr scopes_supported [Array<String>, nil] List of OAuth 2 scopes supported
12
- Config = Data.define(
13
- :issuer,
14
- :authorization_endpoint,
15
- :token_endpoint,
16
- :registration_endpoint,
17
- :scopes_supported
18
- ) do
19
- def initialize(
20
- issuer:,
21
- authorization_endpoint:,
22
- token_endpoint:,
23
- registration_endpoint: nil,
24
- scopes_supported: nil
25
- )
26
- super
27
- end
28
- end
29
-
30
- # OAuth 2 access token response.
31
- #
32
- # @attr access_token [String] The access token string
33
- # @attr token_type [String] Token type (usually "Bearer")
34
- # @attr refresh_token [String, nil] The refresh token string
35
- # @attr expires_in [Integer, nil] Lifetime of the access token in seconds
36
- # @attr expires_at [Time, nil] Calculated expiration time
37
- # @attr scope [String, nil] OAuth scope granted
38
- Token = Data.define(
39
- :access_token,
40
- :token_type,
41
- :refresh_token,
42
- :expires_in,
43
- :expires_at,
44
- :scope
45
- ) do
46
- def initialize(
47
- access_token:,
48
- token_type: "Bearer",
49
- refresh_token: nil,
50
- expires_in: nil,
51
- expires_at: nil,
52
- scope: nil
53
- )
54
- # Calculate expires_at from expires_in if not provided
55
- calculated_expires_at = expires_at || (expires_in ? Time.now + expires_in : nil)
56
- super(
57
- access_token: access_token,
58
- token_type: token_type,
59
- refresh_token: refresh_token,
60
- expires_in: expires_in,
61
- expires_at: calculated_expires_at,
62
- scope: scope
63
- )
64
- end
65
-
66
- # Checks if the token is expired or about to expire.
67
- #
68
- # @param buffer_seconds [Integer] Buffer time before actual expiration (default: 60)
69
- # @return [Boolean] true if expired or will expire within buffer time
70
- def expired?(buffer_seconds = 60)
71
- return false unless expires_at
72
-
73
- Time.now + buffer_seconds >= expires_at
74
- end
75
- end
76
-
77
- # Parameters for exchanging an authorization code for tokens.
78
- #
79
- # @attr token_endpoint [String] URL of the token endpoint
80
- # @attr code [String] The authorization code received from the authorization server
81
- # @attr redirect_uri [String] The redirect URI used in the authorization request
82
- # @attr client_id [String] The client identifier
83
- # @attr client_secret [String, nil] The client secret (optional for public clients)
84
- # @attr code_verifier [String, nil] PKCE code verifier (optional)
85
- # @attr use_legacy_format [Boolean] Use Launchpad's non-standard token format
86
- ExchangeRequest = Data.define(
87
- :token_endpoint,
88
- :code,
89
- :redirect_uri,
90
- :client_id,
91
- :client_secret,
92
- :code_verifier,
93
- :use_legacy_format
94
- ) do
95
- def initialize(
96
- token_endpoint:,
97
- code:,
98
- redirect_uri:,
99
- client_id:,
100
- client_secret: nil,
101
- code_verifier: nil,
102
- use_legacy_format: false
103
- )
104
- super
105
- end
106
- end
107
-
108
- # Parameters for refreshing an access token.
109
- #
110
- # @attr token_endpoint [String] URL of the token endpoint
111
- # @attr refresh_token [String] The refresh token
112
- # @attr client_id [String, nil] The client identifier (optional)
113
- # @attr client_secret [String, nil] The client secret (optional)
114
- # @attr use_legacy_format [Boolean] Use Launchpad's non-standard token format
115
- RefreshRequest = Data.define(
116
- :token_endpoint,
117
- :refresh_token,
118
- :client_id,
119
- :client_secret,
120
- :use_legacy_format
121
- ) do
122
- def initialize(
123
- token_endpoint:,
124
- refresh_token:,
125
- client_id: nil,
126
- client_secret: nil,
127
- use_legacy_format: false
128
- )
129
- super
130
- end
131
- end
132
- end
133
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "uri"
4
-
5
- module Basecamp
6
- module Services
7
- # Service for attachment operations.
8
- #
9
- # Attachments are used to upload files that can be embedded in rich text
10
- # content like messages, comments, and documents. After uploading, you
11
- # receive an attachable_sgid that can be used to embed the file in HTML.
12
- #
13
- # @example Upload a file
14
- # attachment = account.attachments.create(
15
- # filename: "report.pdf",
16
- # content_type: "application/pdf",
17
- # data: file_content
18
- # )
19
- # # Use in HTML: <bc-attachment sgid="#{attachment["attachable_sgid"]}"></bc-attachment>
20
- class AttachmentsService < BaseService
21
- # Creates an attachment by uploading a file.
22
- # Returns an attachable_sgid for embedding the file in rich text content.
23
- #
24
- # @param filename [String] filename for the uploaded file
25
- # @param content_type [String] MIME content type (e.g., "image/png", "application/pdf")
26
- # @param data [String] file data
27
- # @return [Hash] attachment response with attachable_sgid
28
- def create(filename:, content_type:, data:)
29
- http_post_raw("/attachments.json?name=#{URI.encode_www_form_component(filename)}", body: data, content_type: content_type).json
30
- end
31
- end
32
- end
33
- end
@@ -1,141 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basecamp
4
- module Services
5
- # Service for campfire (chat) operations.
6
- #
7
- # Campfires are real-time chat rooms within Basecamp projects.
8
- # They contain lines (messages) and can have chatbot integrations.
9
- #
10
- # @example List all campfires
11
- # account.campfires.list.each do |campfire|
12
- # puts campfire["title"]
13
- # end
14
- #
15
- # @example Send a message
16
- # line = account.campfires.create_line(
17
- # project_id: 123,
18
- # campfire_id: 456,
19
- # content: "Hello team!"
20
- # )
21
- class CampfiresService < BaseService
22
- # Lists all campfires across the account.
23
- #
24
- # @return [Enumerator<Hash>] campfires
25
- def list
26
- paginate("/chats.json")
27
- end
28
-
29
- # Gets a specific campfire.
30
- #
31
- # @param project_id [Integer, String] project (bucket) ID
32
- # @param campfire_id [Integer, String] campfire ID
33
- # @return [Hash] campfire data
34
- def get(project_id:, campfire_id:)
35
- http_get(bucket_path(project_id, "/chats/#{campfire_id}.json")).json
36
- end
37
-
38
- # Lists all lines (messages) in a campfire.
39
- #
40
- # @param project_id [Integer, String] project (bucket) ID
41
- # @param campfire_id [Integer, String] campfire ID
42
- # @return [Enumerator<Hash>] campfire lines
43
- def list_lines(project_id:, campfire_id:)
44
- paginate(bucket_path(project_id, "/chats/#{campfire_id}/lines.json"))
45
- end
46
-
47
- # Gets a specific line (message) from a campfire.
48
- #
49
- # @param project_id [Integer, String] project (bucket) ID
50
- # @param campfire_id [Integer, String] campfire ID
51
- # @param line_id [Integer, String] line ID
52
- # @return [Hash] campfire line
53
- def get_line(project_id:, campfire_id:, line_id:)
54
- http_get(bucket_path(project_id, "/chats/#{campfire_id}/lines/#{line_id}.json")).json
55
- end
56
-
57
- # Creates a new line (message) in a campfire.
58
- #
59
- # @param project_id [Integer, String] project (bucket) ID
60
- # @param campfire_id [Integer, String] campfire ID
61
- # @param content [String] plain text message content
62
- # @return [Hash] created line
63
- def create_line(project_id:, campfire_id:, content:)
64
- body = { content: content }
65
- http_post(bucket_path(project_id, "/chats/#{campfire_id}/lines.json"), body: body).json
66
- end
67
-
68
- # Deletes a line (message) from a campfire.
69
- #
70
- # @param project_id [Integer, String] project (bucket) ID
71
- # @param campfire_id [Integer, String] campfire ID
72
- # @param line_id [Integer, String] line ID
73
- # @return [void]
74
- def delete_line(project_id:, campfire_id:, line_id:)
75
- http_delete(bucket_path(project_id, "/chats/#{campfire_id}/lines/#{line_id}.json"))
76
- nil
77
- end
78
-
79
- # Lists all chatbots for a campfire.
80
- #
81
- # @param project_id [Integer, String] project (bucket) ID
82
- # @param campfire_id [Integer, String] campfire ID
83
- # @return [Enumerator<Hash>] chatbots
84
- def list_chatbots(project_id:, campfire_id:)
85
- paginate(bucket_path(project_id, "/chats/#{campfire_id}/integrations.json"))
86
- end
87
-
88
- # Gets a specific chatbot.
89
- #
90
- # @param project_id [Integer, String] project (bucket) ID
91
- # @param campfire_id [Integer, String] campfire ID
92
- # @param chatbot_id [Integer, String] chatbot ID
93
- # @return [Hash] chatbot data
94
- def get_chatbot(project_id:, campfire_id:, chatbot_id:)
95
- http_get(bucket_path(project_id, "/chats/#{campfire_id}/integrations/#{chatbot_id}.json")).json
96
- end
97
-
98
- # Creates a new chatbot for a campfire.
99
- #
100
- # @param project_id [Integer, String] project (bucket) ID
101
- # @param campfire_id [Integer, String] campfire ID
102
- # @param service_name [String] chatbot name (no spaces, emoji, or non-word characters)
103
- # @param command_url [String, nil] HTTPS URL for bot callbacks
104
- # @return [Hash] created chatbot with lines_url for posting
105
- def create_chatbot(project_id:, campfire_id:, service_name:, command_url: nil)
106
- body = compact_params(
107
- service_name: service_name,
108
- command_url: command_url
109
- )
110
- http_post(bucket_path(project_id, "/chats/#{campfire_id}/integrations.json"), body: body).json
111
- end
112
-
113
- # Updates an existing chatbot.
114
- #
115
- # @param project_id [Integer, String] project (bucket) ID
116
- # @param campfire_id [Integer, String] campfire ID
117
- # @param chatbot_id [Integer, String] chatbot ID
118
- # @param service_name [String] new chatbot name
119
- # @param command_url [String, nil] new callback URL
120
- # @return [Hash] updated chatbot
121
- def update_chatbot(project_id:, campfire_id:, chatbot_id:, service_name:, command_url: nil)
122
- body = compact_params(
123
- service_name: service_name,
124
- command_url: command_url
125
- )
126
- http_put(bucket_path(project_id, "/chats/#{campfire_id}/integrations/#{chatbot_id}.json"), body: body).json
127
- end
128
-
129
- # Deletes a chatbot.
130
- #
131
- # @param project_id [Integer, String] project (bucket) ID
132
- # @param campfire_id [Integer, String] campfire ID
133
- # @param chatbot_id [Integer, String] chatbot ID
134
- # @return [void]
135
- def delete_chatbot(project_id:, campfire_id:, chatbot_id:)
136
- http_delete(bucket_path(project_id, "/chats/#{campfire_id}/integrations/#{chatbot_id}.json"))
137
- nil
138
- end
139
- end
140
- end
141
- end
@@ -1,106 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basecamp
4
- module Services
5
- # Service for card column operations.
6
- #
7
- # Columns are lists within a card table that contain cards.
8
- #
9
- # @example Create a column
10
- # column = account.card_columns.create(
11
- # project_id: 123,
12
- # card_table_id: 456,
13
- # title: "In Review"
14
- # )
15
- #
16
- # @example Set column color
17
- # account.card_columns.set_color(project_id: 123, column_id: 456, color: "blue")
18
- class CardColumnsService < BaseService
19
- # Gets a column by ID.
20
- #
21
- # @param project_id [Integer, String] project (bucket) ID
22
- # @param column_id [Integer, String] column ID
23
- # @return [Hash] column data
24
- def get(project_id:, column_id:)
25
- http_get(bucket_path(project_id, "/card_tables/columns/#{column_id}.json")).json
26
- end
27
-
28
- # Creates a new column in a card table.
29
- #
30
- # @param project_id [Integer, String] project (bucket) ID
31
- # @param card_table_id [Integer, String] card table ID
32
- # @param title [String] column title
33
- # @param description [String, nil] column description
34
- # @return [Hash] created column
35
- def create(project_id:, card_table_id:, title:, description: nil)
36
- body = compact_params(
37
- title: title,
38
- description: description
39
- )
40
- http_post(bucket_path(project_id, "/card_tables/#{card_table_id}/columns.json"), body: body).json
41
- end
42
-
43
- # Updates an existing column.
44
- #
45
- # @param project_id [Integer, String] project (bucket) ID
46
- # @param column_id [Integer, String] column ID
47
- # @param title [String, nil] new title
48
- # @param description [String, nil] new description
49
- # @return [Hash] updated column
50
- def update(project_id:, column_id:, title: nil, description: nil)
51
- body = compact_params(
52
- title: title,
53
- description: description
54
- )
55
- http_put(bucket_path(project_id, "/card_tables/columns/#{column_id}.json"), body: body).json
56
- end
57
-
58
- # Moves a column within a card table.
59
- #
60
- # @param project_id [Integer, String] project (bucket) ID
61
- # @param card_table_id [Integer, String] card table ID
62
- # @param source_id [Integer, String] column ID to move
63
- # @param target_id [Integer, String] column ID to move relative to
64
- # @param position [Integer, nil] position relative to target
65
- # @return [void]
66
- def move(project_id:, card_table_id:, source_id:, target_id:, position: nil)
67
- body = compact_params(
68
- source_id: source_id,
69
- target_id: target_id,
70
- position: position
71
- )
72
- http_post(bucket_path(project_id, "/card_tables/#{card_table_id}/moves.json"), body: body)
73
- nil
74
- end
75
-
76
- # Sets the color of a column.
77
- #
78
- # @param project_id [Integer, String] project (bucket) ID
79
- # @param column_id [Integer, String] column ID
80
- # @param color [String] color name (white, red, orange, yellow, green, blue, aqua, purple, gray, pink, brown)
81
- # @return [Hash] updated column
82
- def set_color(project_id:, column_id:, color:)
83
- http_put(bucket_path(project_id, "/card_tables/columns/#{column_id}/color.json"),
84
- body: { color: color }).json
85
- end
86
-
87
- # Adds an on-hold section to a column.
88
- #
89
- # @param project_id [Integer, String] project (bucket) ID
90
- # @param column_id [Integer, String] column ID
91
- # @return [Hash] updated column
92
- def enable_on_hold(project_id:, column_id:)
93
- http_post(bucket_path(project_id, "/card_tables/columns/#{column_id}/on_hold.json")).json
94
- end
95
-
96
- # Removes the on-hold section from a column.
97
- #
98
- # @param project_id [Integer, String] project (bucket) ID
99
- # @param column_id [Integer, String] column ID
100
- # @return [Hash] updated column
101
- def disable_on_hold(project_id:, column_id:)
102
- http_delete(bucket_path(project_id, "/card_tables/columns/#{column_id}/on_hold.json")).json
103
- end
104
- end
105
- end
106
- end