basecamp-sdk 0.8.0 → 0.9.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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/lib/basecamp/client.rb +5 -0
  4. data/lib/basecamp/generated/metadata.json +54 -1
  5. data/lib/basecamp/generated/services/base_service.rb +13 -0
  6. data/lib/basecamp/generated/services/campfires_service.rb +2 -2
  7. data/lib/basecamp/generated/services/card_columns_service.rb +3 -3
  8. data/lib/basecamp/generated/services/checkins_service.rb +5 -4
  9. data/lib/basecamp/generated/services/client_approvals_service.rb +1 -1
  10. data/lib/basecamp/generated/services/client_correspondences_service.rb +1 -1
  11. data/lib/basecamp/generated/services/documents_service.rb +3 -2
  12. data/lib/basecamp/generated/services/forwards_service.rb +1 -1
  13. data/lib/basecamp/generated/services/gauges_service.rb +4 -4
  14. data/lib/basecamp/generated/services/messages_service.rb +4 -3
  15. data/lib/basecamp/generated/services/my_assignments_service.rb +1 -1
  16. data/lib/basecamp/generated/services/my_notifications_service.rb +1 -1
  17. data/lib/basecamp/generated/services/people_service.rb +2 -2
  18. data/lib/basecamp/generated/services/projects_service.rb +2 -2
  19. data/lib/basecamp/generated/services/recordings_service.rb +1 -1
  20. data/lib/basecamp/generated/services/reports_service.rb +2 -2
  21. data/lib/basecamp/generated/services/schedules_service.rb +5 -4
  22. data/lib/basecamp/generated/services/search_service.rb +14 -3
  23. data/lib/basecamp/generated/services/templates_service.rb +4 -5
  24. data/lib/basecamp/generated/services/timesheets_service.rb +3 -3
  25. data/lib/basecamp/generated/services/todolists_service.rb +15 -3
  26. data/lib/basecamp/generated/services/todos_service.rb +1 -1
  27. data/lib/basecamp/generated/services/tools_service.rb +1 -1
  28. data/lib/basecamp/generated/services/uploads_service.rb +3 -2
  29. data/lib/basecamp/generated/services/webhooks_service.rb +2 -2
  30. data/lib/basecamp/generated/services/wormholes_service.rb +44 -0
  31. data/lib/basecamp/generated/types.rb +254 -58
  32. data/lib/basecamp/version.rb +2 -2
  33. data/scripts/generate-services.rb +24 -10
  34. data/scripts/generate-types.rb +33 -1
  35. metadata +3 -2
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basecamp
4
- VERSION = "0.8.0"
5
- API_VERSION = "2026-07-22"
4
+ VERSION = "0.9.0"
5
+ API_VERSION = "2026-07-24"
6
6
  end
@@ -60,7 +60,8 @@ class ServiceGenerator
60
60
  'CardSteps' => %w[
61
61
  GetCardStep CreateCardStep UpdateCardStep SetCardStepCompletion
62
62
  RepositionCardStep
63
- ]
63
+ ],
64
+ 'Wormholes' => %w[CreateWormhole UpdateWormhole DeleteWormhole]
64
65
  },
65
66
  'Files' => {
66
67
  'Attachments' => %w[CreateAttachment],
@@ -116,7 +117,7 @@ class ServiceGenerator
116
117
  },
117
118
  'Todos' => {
118
119
  'Todos' => %w[ListTodos CreateTodo GetTodo ReplaceTodo CompleteTodo UncompleteTodo TrashTodo],
119
- 'Todolists' => %w[GetTodolistOrGroup UpdateTodolistOrGroup ListTodolists CreateTodolist],
120
+ 'Todolists' => %w[GetTodolistOrGroup UpdateTodolistOrGroup ListTodolists CreateTodolist RepositionTodolist],
120
121
  'Todosets' => %w[GetTodoset],
121
122
  'HillCharts' => %w[GetHillChart UpdateHillChartSettings],
122
123
  'TodolistGroups' => %w[ListTodolistGroups CreateTodolistGroup RepositionTodolistGroup]
@@ -302,6 +303,7 @@ class ServiceGenerator
302
303
  clientcorrespondences clientreply clientreplies forwardreply
303
304
  forwardreplies campfireline campfirelines todolistgroup todolistgroups
304
305
  todolistorgroup uploadversions hillchart hillcharts
306
+ wormhole wormholes
305
307
  ].freeze
306
308
 
307
309
  def initialize(openapi_path)
@@ -396,7 +398,12 @@ class ServiceGenerator
396
398
  .select { |p| p['in'] == 'query' }
397
399
  .map do |p|
398
400
  {
399
- name: p['name'],
401
+ # Strip a trailing `[]` from bracketed array wire names (e.g.
402
+ # `bucket_ids[]`): the kwarg and the params-hash key are both clean
403
+ # `bucket_ids`, and Faraday's NestedParamsEncoder re-adds the `[]` when
404
+ # serializing the array value (a raw `bucket_ids[]` key would double to
405
+ # `bucket_ids[][]=`).
406
+ name: p['name'].sub(/\[\]\z/, ''),
400
407
  type: schema_to_ruby_type(p['schema']),
401
408
  required: p['required'] || false,
402
409
  description: p['description']
@@ -533,7 +540,13 @@ class ServiceGenerator
533
540
  def schema_to_ruby_type(schema)
534
541
  return 'Object' unless schema
535
542
 
536
- case schema['type']
543
+ # Object-valued members (e.g. a `project`/`gauge`/`schedule` envelope) are passed
544
+ # as a Hash. Resolve $refs and treat only object-typed schemas as Hash; a string
545
+ # ref such as FirstWeekDay must stay String.
546
+ resolved = schema['$ref'] ? resolve_schema_ref(schema) : schema
547
+ return 'Hash' if resolved && resolved['type'] == 'object'
548
+
549
+ case resolved&.fetch('type', nil)
537
550
  when 'integer' then 'Integer'
538
551
  when 'boolean' then 'Boolean'
539
552
  when 'array' then 'Array'
@@ -691,10 +704,11 @@ class ServiceGenerator
691
704
  kwargs << "operation: \"#{op[:method_name]}\""
692
705
  kwargs << "is_mutation: #{op[:is_mutation]}"
693
706
 
694
- project_param = op[:path_params].find { |p| p[:name] == 'projectId' }
695
- resource_param = op[:path_params].reject { |p| p[:name] == 'projectId' }.last
707
+ project_param = op[:path_params].find { |p| %w[projectId bucketId].include?(p[:name]) }
708
+ resource_param = op[:path_params].reject { |p| %w[projectId bucketId].include?(p[:name]) }
709
+ .select { |p| p[:name].end_with?("Id") || p[:name] == "id" }.last
696
710
 
697
- kwargs << "project_id: project_id" if project_param
711
+ kwargs << "project_id: #{to_snake_case(project_param[:name])}" if project_param
698
712
  kwargs << "resource_id: #{to_snake_case(resource_param[:name])}" if resource_param
699
713
 
700
714
  kwargs.join(', ')
@@ -788,7 +802,7 @@ class ServiceGenerator
788
802
  # Build params hash for query params
789
803
  if op[:query_params].any?
790
804
  param_names = op[:query_params].map { |q| "#{to_snake_case(q[:name])}: #{to_snake_case(q[:name])}" }
791
- lines << " params = compact_params(#{param_names.join(', ')})"
805
+ lines << " params = compact_query_params(#{param_names.join(', ')})"
792
806
  lines << " paginate(#{path_expr}, params: params)"
793
807
  else
794
808
  lines << " paginate(#{path_expr})"
@@ -802,7 +816,7 @@ class ServiceGenerator
802
816
 
803
817
  if op[:query_params].any?
804
818
  param_names = op[:query_params].map { |q| "#{to_snake_case(q[:name])}: #{to_snake_case(q[:name])}" }
805
- lines << " params = compact_params(#{param_names.join(', ')})"
819
+ lines << " params = compact_query_params(#{param_names.join(', ')})"
806
820
  lines << " paginate_wrapped(#{path_expr}, key: \"#{pagination_key}\", params: params)"
807
821
  else
808
822
  lines << " paginate_wrapped(#{path_expr}, key: \"#{pagination_key}\")"
@@ -834,7 +848,7 @@ class ServiceGenerator
834
848
  lines << " http_#{http_method}(#{path_expr}, body: #{body_expr}).json"
835
849
  elsif op[:query_params].any?
836
850
  param_names = op[:query_params].map { |q| "#{to_snake_case(q[:name])}: #{to_snake_case(q[:name])}" }
837
- lines << " http_#{http_method}(#{path_expr}, params: compact_params(#{param_names.join(', ')})).json"
851
+ lines << " http_#{http_method}(#{path_expr}, params: compact_query_params(#{param_names.join(', ')})).json"
838
852
  else
839
853
  lines << " http_#{http_method}(#{path_expr}).json"
840
854
  end
@@ -124,6 +124,12 @@ if __FILE__ == $PROGRAM_NAME
124
124
 
125
125
  puts ''
126
126
  puts " # #{name}"
127
+ # Documentation-only deprecation (see #406): YARD marks a whole class/method,
128
+ # not individual params, so a class-level @deprecated tag documents a wholly
129
+ # deprecated type.
130
+ if schema['deprecated']
131
+ puts " # @deprecated #{schema['x-deprecated-reason'] || 'deprecated'}"
132
+ end
127
133
  puts " class #{name}"
128
134
  puts ' include TypeHelpers'
129
135
 
@@ -131,6 +137,19 @@ if __FILE__ == $PROGRAM_NAME
131
137
  # Add system_label for schemas with flexible integer fields
132
138
  has_flexible = ordered_props.any? { |k| properties[k]['x-go-type']&.include?('FlexibleInt64') }
133
139
  attr_names << 'system_label' if has_flexible
140
+
141
+ # Per-attribute deprecation. The accessors are declared in one grouped
142
+ # attr_accessor, so a bare comment would wrongly document every attribute;
143
+ # a YARD @!attribute directive scopes the @deprecated tag to just this one.
144
+ ordered_props.each do |k|
145
+ ps = properties[k]
146
+ next unless ps['deprecated']
147
+
148
+ ruby_name = k.gsub(/([A-Z])/, '_\1').downcase.gsub(/^_/, '')
149
+ puts " # @!attribute [rw] #{ruby_name}"
150
+ puts " # @deprecated #{ps['x-deprecated-reason'] || 'deprecated'}"
151
+ end
152
+
134
153
  puts " attr_accessor #{attr_names.map { |n| ":#{n}" }.join(", ")}"
135
154
 
136
155
  unless required_props.empty?
@@ -182,7 +201,20 @@ if __FILE__ == $PROGRAM_NAME
182
201
  attr_name = prop_name.gsub(/([A-Z])/, '_\1').downcase.gsub(/^_/, '')
183
202
  puts " \"#{prop_name}\" => @#{attr_name},"
184
203
  end
185
- puts ' }.compact'
204
+ # A required-and-nullable field (OpenAPI 3.1 `type: [..., "null"]`) carries an
205
+ # explicit null that must survive to_h; plain .compact would drop it. Keep
206
+ # nil for exactly those keys — required-but-non-nullable fields still get
207
+ # dropped when nil, like any other nil. Everything else keeps .compact.
208
+ required_nullable = required_fields.select do |k|
209
+ t = properties[k] && properties[k]['type']
210
+ t.is_a?(Array) && t.include?('null')
211
+ end
212
+ if required_nullable.any?
213
+ keep_list = required_nullable.map { |k| "\"#{k}\"" }.join(', ')
214
+ puts " }.reject { |k, v| v.nil? && ![#{keep_list}].include?(k) }"
215
+ else
216
+ puts ' }.compact'
217
+ end
186
218
  puts ' end'
187
219
  puts ''
188
220
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: basecamp-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Basecamp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-22 00:00:00.000000000 Z
11
+ date: 2026-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -223,6 +223,7 @@ files:
223
223
  - lib/basecamp/generated/services/uploads_service.rb
224
224
  - lib/basecamp/generated/services/vaults_service.rb
225
225
  - lib/basecamp/generated/services/webhooks_service.rb
226
+ - lib/basecamp/generated/services/wormholes_service.rb
226
227
  - lib/basecamp/generated/types.rb
227
228
  - lib/basecamp/hooks.rb
228
229
  - lib/basecamp/http.rb