seam 2.139.0 → 2.141.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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +69 -66
  3. data/README.md +86 -0
  4. data/lib/seam/http_without_workspace.rb +4 -4
  5. data/lib/seam/null.rb +23 -0
  6. data/lib/seam/paginator.rb +4 -1
  7. data/lib/seam/request.rb +49 -1
  8. data/lib/seam/resources/access_code.rb +74 -0
  9. data/lib/seam/resources/access_grant.rb +60 -0
  10. data/lib/seam/resources/access_method.rb +39 -0
  11. data/lib/seam/resources/acs_access_group.rb +45 -0
  12. data/lib/seam/resources/acs_credential.rb +67 -0
  13. data/lib/seam/resources/acs_encoder.rb +11 -0
  14. data/lib/seam/resources/acs_entrance.rb +95 -0
  15. data/lib/seam/resources/acs_system.rb +36 -0
  16. data/lib/seam/resources/acs_user.rb +66 -0
  17. data/lib/seam/resources/action_attempt.rb +192 -0
  18. data/lib/seam/resources/batch.rb +24 -0
  19. data/lib/seam/resources/client_session.rb +12 -0
  20. data/lib/seam/resources/connect_webview.rb +19 -0
  21. data/lib/seam/resources/connected_account.rb +50 -0
  22. data/lib/seam/resources/customer_portal.rb +5 -0
  23. data/lib/seam/resources/device.rb +622 -4
  24. data/lib/seam/resources/device_provider.rb +24 -0
  25. data/lib/seam/resources/event.rb +142 -3
  26. data/lib/seam/resources/instant_key.rb +13 -0
  27. data/lib/seam/resources/noise_threshold.rb +7 -0
  28. data/lib/seam/resources/pagination.rb +3 -0
  29. data/lib/seam/resources/phone.rb +29 -0
  30. data/lib/seam/resources/space.rb +19 -0
  31. data/lib/seam/resources/thermostat_daily_program.rb +9 -0
  32. data/lib/seam/resources/thermostat_schedule.rb +15 -0
  33. data/lib/seam/resources/unmanaged_access_code.rb +51 -0
  34. data/lib/seam/resources/unmanaged_access_grant.rb +56 -0
  35. data/lib/seam/resources/unmanaged_access_method.rb +36 -0
  36. data/lib/seam/resources/unmanaged_device.rb +80 -0
  37. data/lib/seam/resources/unmanaged_user_identity.rb +20 -0
  38. data/lib/seam/resources/user_identity.rb +21 -0
  39. data/lib/seam/resources/webhook.rb +4 -0
  40. data/lib/seam/resources/workspace.rb +15 -0
  41. data/lib/seam/routes/access_codes.rb +79 -79
  42. data/lib/seam/routes/access_codes_simulate.rb +3 -3
  43. data/lib/seam/routes/access_codes_unmanaged.rb +27 -23
  44. data/lib/seam/routes/access_grants.rb +57 -45
  45. data/lib/seam/routes/access_grants_unmanaged.rb +13 -13
  46. data/lib/seam/routes/access_methods.rb +32 -24
  47. data/lib/seam/routes/access_methods_unmanaged.rb +7 -7
  48. data/lib/seam/routes/acs_access_groups.rb +21 -21
  49. data/lib/seam/routes/acs_credentials.rb +40 -40
  50. data/lib/seam/routes/acs_encoders.rb +16 -16
  51. data/lib/seam/routes/acs_encoders_simulate.rb +11 -11
  52. data/lib/seam/routes/acs_entrances.rb +20 -20
  53. data/lib/seam/routes/acs_systems.rb +11 -11
  54. data/lib/seam/routes/acs_users.rb +83 -55
  55. data/lib/seam/routes/action_attempts.rb +6 -6
  56. data/lib/seam/routes/client_sessions.rb +38 -34
  57. data/lib/seam/routes/connect_webviews.rb +20 -20
  58. data/lib/seam/routes/connected_accounts.rb +24 -20
  59. data/lib/seam/routes/connected_accounts_simulate.rb +1 -1
  60. data/lib/seam/routes/customers.rb +50 -50
  61. data/lib/seam/routes/devices.rb +33 -29
  62. data/lib/seam/routes/devices_simulate.rb +7 -7
  63. data/lib/seam/routes/devices_unmanaged.rb +25 -26
  64. data/lib/seam/routes/events.rb +40 -32
  65. data/lib/seam/routes/instant_keys.rb +11 -7
  66. data/lib/seam/routes/locks.rb +20 -27
  67. data/lib/seam/routes/locks_simulate.rb +3 -3
  68. data/lib/seam/routes/noise_sensors.rb +8 -19
  69. data/lib/seam/routes/noise_sensors_noise_thresholds.rb +21 -21
  70. data/lib/seam/routes/noise_sensors_simulate.rb +1 -1
  71. data/lib/seam/routes/phones.rb +7 -7
  72. data/lib/seam/routes/phones_simulate.rb +4 -4
  73. data/lib/seam/routes/spaces.rb +53 -45
  74. data/lib/seam/routes/thermostats.rb +75 -86
  75. data/lib/seam/routes/thermostats_daily_programs.rb +9 -9
  76. data/lib/seam/routes/thermostats_schedules.rb +22 -22
  77. data/lib/seam/routes/thermostats_simulate.rb +9 -9
  78. data/lib/seam/routes/user_identities.rb +50 -46
  79. data/lib/seam/routes/user_identities_unmanaged.rb +11 -11
  80. data/lib/seam/routes/webhooks.rb +10 -10
  81. data/lib/seam/routes/workspaces.rb +19 -19
  82. data/lib/seam/strict_url_search_params_serializer.rb +17 -0
  83. data/lib/seam/url_search_params.rb +102 -0
  84. data/lib/seam/url_search_params_serializer.rb +217 -0
  85. data/lib/seam/version.rb +1 -1
  86. data/lib/seam.rb +3 -0
  87. metadata +12 -11
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Seam
6
+ # A mutable, ordered list of name/value string pairs modeling the parts of
7
+ # the WHATWG URLSearchParams interface that the Seam URL search params
8
+ # serializer needs.
9
+ class UrlSearchParams
10
+ include Enumerable
11
+
12
+ # @param init [String, Hash, Enumerable, nil]
13
+ def initialize(init = nil)
14
+ @pairs = []
15
+ return if init.nil?
16
+
17
+ case init
18
+ when String
19
+ query = init.delete_prefix("?")
20
+ URI.decode_www_form(query).each { |name, value| append(name, value) } unless query.empty?
21
+ when Hash
22
+ init.each { |name, value| append(name, value) }
23
+ else
24
+ init.each { |name, value| append(name, value) }
25
+ end
26
+ end
27
+
28
+ def append(name, value)
29
+ @pairs << [name.to_s, value.to_s]
30
+ nil
31
+ end
32
+
33
+ def set(name, value)
34
+ name = name.to_s
35
+ replaced = false
36
+ @pairs = @pairs.filter_map do |pair|
37
+ next pair unless pair.first == name
38
+ next nil if replaced
39
+
40
+ replaced = true
41
+ [name, value.to_s]
42
+ end
43
+ append(name, value) unless replaced
44
+ nil
45
+ end
46
+
47
+ def get(name)
48
+ name = name.to_s
49
+ @pairs.each { |pair_name, value| return value if pair_name == name }
50
+ nil
51
+ end
52
+
53
+ def get_all(name)
54
+ name = name.to_s
55
+ @pairs.filter_map { |pair_name, value| value if pair_name == name }
56
+ end
57
+
58
+ def has?(name)
59
+ name = name.to_s
60
+ @pairs.any? { |pair_name, _| pair_name == name }
61
+ end
62
+
63
+ def delete(name)
64
+ name = name.to_s
65
+ @pairs.reject! { |pair_name, _| pair_name == name }
66
+ nil
67
+ end
68
+
69
+ def sort!
70
+ @pairs = @pairs.each_with_index.sort_by do |(name, _), index|
71
+ [name.encode(Encoding::UTF_16BE).b, index]
72
+ end.map(&:first)
73
+ nil
74
+ end
75
+
76
+ def each(&block)
77
+ return @pairs.each unless block
78
+
79
+ @pairs.each(&block)
80
+ self
81
+ end
82
+
83
+ def size
84
+ @pairs.size
85
+ end
86
+ alias_method :length, :size
87
+
88
+ def empty?
89
+ @pairs.empty?
90
+ end
91
+
92
+ def to_s
93
+ @pairs.map do |name, value|
94
+ "#{self.class.encode_component(name)}=#{self.class.encode_component(value)}"
95
+ end.join("&")
96
+ end
97
+
98
+ def self.encode_component(string)
99
+ URI.encode_www_form_component(string.encode(Encoding::UTF_8))
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ require_relative "null"
6
+ require_relative "url_search_params"
7
+
8
+ module Seam
9
+ class UnserializableParamError < StandardError
10
+ attr_reader :param_name
11
+
12
+ def initialize(param_name, reason)
13
+ @param_name = param_name
14
+ super("Could not serialize parameter: '#{param_name}' #{reason}")
15
+ end
16
+ end
17
+
18
+ def self.replace_null(value)
19
+ case value
20
+ when Seam::Null then nil
21
+ when Hash then value.transform_values { |v| replace_null(v) }
22
+ when Array then value.map { |v| replace_null(v) }
23
+ else value
24
+ end
25
+ end
26
+
27
+ # Serializes parameters to a URL query string following the
28
+ # @seamapi/url-search-params-serializer standard:
29
+ # https://github.com/seamapi/url-search-params-serializer
30
+ module UrlSearchParamsSerializer
31
+ # @param params [Hash]
32
+ # @param strict [Boolean] Whether to add +_strict=true+ to a non-empty
33
+ # query string
34
+ # @return [String] The query string, without a leading +?+
35
+ # @raise [UnserializableParamError]
36
+ def self.serialize_url_search_params(params, strict: false)
37
+ search_params = UrlSearchParams.new
38
+ update_url_search_params(search_params, params, strict: strict)
39
+ search_params.to_s
40
+ end
41
+
42
+ # Serializes parameters into an existing {UrlSearchParams} collection,
43
+ # preserving pairs it does not overwrite.
44
+ #
45
+ # @param search_params [UrlSearchParams]
46
+ # @param params [Hash]
47
+ # @param strict [Boolean] Whether to add +_strict=true+ when the
48
+ # resulting collection is non-empty
49
+ # @return [nil]
50
+ # @raise [UnserializableParamError]
51
+ def self.update_url_search_params(search_params, params, strict: false)
52
+ nested_update(search_params, params, [])
53
+ search_params.sort!
54
+
55
+ if strict && !search_params.empty?
56
+ search_params.delete("_strict")
57
+ search_params.append("_strict", "true")
58
+ end
59
+
60
+ nil
61
+ end
62
+
63
+ def self.nested_update(search_params, params, path)
64
+ params.each do |key, value|
65
+ unless key.is_a?(String) || key.is_a?(Symbol)
66
+ raise UnserializableParamError.new(
67
+ key.inspect,
68
+ "has a name that is not a string which is unsupported"
69
+ )
70
+ end
71
+ key = key.to_s
72
+
73
+ if key.include?(".")
74
+ raise UnserializableParamError.new(
75
+ key,
76
+ 'contains one or more dots "." in its name which is unsupported'
77
+ )
78
+ end
79
+
80
+ current_path = [*path, key]
81
+
82
+ if value.is_a?(Hash)
83
+ nested_update(search_params, value, current_path)
84
+ next
85
+ end
86
+
87
+ name = current_path.join(".")
88
+
89
+ next if value.nil?
90
+
91
+ value = value.to_s if value.is_a?(Symbol)
92
+
93
+ next if value.is_a?(String) && value.empty?
94
+
95
+ if value.is_a?(Array)
96
+ serialize_array(search_params, name, value)
97
+ next
98
+ end
99
+
100
+ search_params.set(name, serialize_value(name, value))
101
+ end
102
+ end
103
+
104
+ def self.serialize_array(search_params, name, values)
105
+ # The parser reads a single pair with an empty value as an empty array.
106
+ if values.empty?
107
+ search_params.set(name, "")
108
+ return
109
+ end
110
+
111
+ values = values.map { |value| value.is_a?(Symbol) ? value.to_s : value }
112
+
113
+ if values.length == 1 && values.first == ""
114
+ raise UnserializableParamError.new(
115
+ name,
116
+ "is a single element array containing the empty string which is unsupported"
117
+ )
118
+ end
119
+
120
+ if values.any? { |value| value == "" }
121
+ raise UnserializableParamError.new(
122
+ name,
123
+ "is an array containing the empty string which is unsupported"
124
+ )
125
+ end
126
+
127
+ if values.any? { |value| value.nil? || value.is_a?(Seam::Null) }
128
+ raise UnserializableParamError.new(
129
+ name,
130
+ "is an array containing null or undefined values which is unsupported"
131
+ )
132
+ end
133
+
134
+ values.each { |value| search_params.append(name, serialize_value(name, value)) }
135
+ end
136
+
137
+ def self.serialize_value(name, value)
138
+ case value
139
+ when Seam::Null then ""
140
+ when String then value
141
+ when true, false then value.to_s
142
+ when Integer then value.to_s
143
+ when Float then serialize_float(name, value)
144
+ when Time then serialize_time(value)
145
+ when DateTime then serialize_time(value.to_time)
146
+ when Date then serialize_time(Time.utc(value.year, value.month, value.day))
147
+ else
148
+ raise UnserializableParamError.new(name, "is a #{value.class}")
149
+ end
150
+ end
151
+
152
+ # Formats a float exactly like ECMAScript Number::toString.
153
+ def self.serialize_float(name, value)
154
+ raise UnserializableParamError.new(name, "is NaN") if value.nan?
155
+ if value.infinite?
156
+ raise UnserializableParamError.new(name, "is #{value.positive? ? "Infinity" : "-Infinity"}")
157
+ end
158
+ return "0" if value.zero?
159
+
160
+ digits, point = shortest_decimal(value.abs)
161
+ count = digits.length
162
+
163
+ formatted = if point.between?(count, 21)
164
+ digits + "0" * (point - count)
165
+ elsif point.positive? && point <= 21
166
+ "#{digits[0, point]}.#{digits[point..]}"
167
+ elsif point > -6 && point <= 0
168
+ "0.#{"0" * -point}#{digits}"
169
+ else
170
+ mantissa = (count == 1) ? digits : "#{digits[0]}.#{digits[1..]}"
171
+ exponent = point - 1
172
+ "#{mantissa}e#{(exponent >= 0) ? "+" : "-"}#{exponent.abs}"
173
+ end
174
+
175
+ value.negative? ? "-#{formatted}" : formatted
176
+ end
177
+
178
+ # Returns the shortest round-tripping decimal digits of a positive float
179
+ # and the position of the decimal point relative to the first digit.
180
+ def self.shortest_decimal(value)
181
+ repr = value.to_s
182
+
183
+ if repr.include?("e")
184
+ mantissa, exponent = repr.split("e")
185
+ integer_part, fraction_part = mantissa.split(".")
186
+ digits = integer_part + (fraction_part || "")
187
+ point = integer_part.length + exponent.to_i
188
+ else
189
+ integer_part, fraction_part = repr.split(".")
190
+ digits = integer_part + (fraction_part || "")
191
+ point = integer_part.length
192
+ end
193
+
194
+ leading_zeros = digits[/\A0*/].length
195
+ digits = digits[leading_zeros..]
196
+ point -= leading_zeros
197
+
198
+ [digits.sub(/0+\z/, ""), point]
199
+ end
200
+
201
+ # Formats a time exactly like JavaScript's Date#toISOString.
202
+ def self.serialize_time(time)
203
+ utc = time.getutc
204
+ year = if utc.year.between?(0, 9999)
205
+ format("%04d", utc.year)
206
+ elsif utc.year > 9999
207
+ format("+%06d", utc.year)
208
+ else
209
+ format("-%06d", -utc.year)
210
+ end
211
+ format(
212
+ "%s-%02d-%02dT%02d:%02d:%02d.%03dZ",
213
+ year, utc.month, utc.day, utc.hour, utc.min, utc.sec, utc.nsec / 1_000_000
214
+ )
215
+ end
216
+ end
217
+ end
data/lib/seam/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Seam
4
- VERSION = "2.139.0"
4
+ VERSION = "2.141.0"
5
5
  end
data/lib/seam.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "seam/null"
4
+ require_relative "seam/url_search_params"
5
+ require_relative "seam/strict_url_search_params_serializer"
3
6
  require_relative "seam/http"
4
7
  require_relative "seam/http_without_workspace"
5
8
  require_relative "seam/webhook"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seam
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.139.0
4
+ version: 2.141.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seam Labs, Inc.
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-08-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: faraday
@@ -128,14 +127,14 @@ dependencies:
128
127
  requirements:
129
128
  - - "~>"
130
129
  - !ruby/object:Gem::Version
131
- version: '0.21'
130
+ version: '1.1'
132
131
  type: :development
133
132
  prerelease: false
134
133
  version_requirements: !ruby/object:Gem::Requirement
135
134
  requirements:
136
135
  - - "~>"
137
136
  - !ruby/object:Gem::Version
138
- version: '0.21'
137
+ version: '1.1'
139
138
  - !ruby/object:Gem::Dependency
140
139
  name: simplecov-console
141
140
  requirement: !ruby/object:Gem::Requirement
@@ -170,14 +169,14 @@ dependencies:
170
169
  requirements:
171
170
  - - "~>"
172
171
  - !ruby/object:Gem::Version
173
- version: 3.24.0
172
+ version: 3.26.2
174
173
  type: :development
175
174
  prerelease: false
176
175
  version_requirements: !ruby/object:Gem::Requirement
177
176
  requirements:
178
177
  - - "~>"
179
178
  - !ruby/object:Gem::Version
180
- version: 3.24.0
179
+ version: 3.26.2
181
180
  description: SDK for the Seam API written in Ruby.
182
181
  email: engineering@getseam.com
183
182
  executables: []
@@ -198,6 +197,7 @@ files:
198
197
  - lib/seam/http.rb
199
198
  - lib/seam/http_single_workspace.rb
200
199
  - lib/seam/http_without_workspace.rb
200
+ - lib/seam/null.rb
201
201
  - lib/seam/options.rb
202
202
  - lib/seam/paginator.rb
203
203
  - lib/seam/parse_options.rb
@@ -280,7 +280,10 @@ files:
280
280
  - lib/seam/routes/user_identities_unmanaged.rb
281
281
  - lib/seam/routes/webhooks.rb
282
282
  - lib/seam/routes/workspaces.rb
283
+ - lib/seam/strict_url_search_params_serializer.rb
283
284
  - lib/seam/token.rb
285
+ - lib/seam/url_search_params.rb
286
+ - lib/seam/url_search_params_serializer.rb
284
287
  - lib/seam/version.rb
285
288
  - lib/seam/wait_for_action_attempt.rb
286
289
  - lib/seam/webhook.rb
@@ -293,7 +296,6 @@ metadata:
293
296
  bug_tracker_uri: https://github.com/seamapi/ruby-next/issues
294
297
  changelog_uri: https://github.com/seamapi/ruby-next/blob/main/CHANGELOG.md
295
298
  github_repo: git@github.com:seamapi/ruby-next.git
296
- post_install_message:
297
299
  rdoc_options: []
298
300
  require_paths:
299
301
  - lib
@@ -301,15 +303,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
301
303
  requirements:
302
304
  - - ">="
303
305
  - !ruby/object:Gem::Version
304
- version: 3.1.0
306
+ version: 3.3.0
305
307
  required_rubygems_version: !ruby/object:Gem::Requirement
306
308
  requirements:
307
309
  - - ">="
308
310
  - !ruby/object:Gem::Version
309
311
  version: '0'
310
312
  requirements: []
311
- rubygems_version: 3.5.3
312
- signing_key:
313
+ rubygems_version: 4.0.3
313
314
  specification_version: 4
314
315
  summary: Seam Ruby SDK
315
316
  test_files: []