seam 2.0.0a1 → 2.0.0b0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +63 -28
  3. data/README.md +327 -4
  4. data/Rakefile +4 -1
  5. data/lib/seam/auth.rb +118 -0
  6. data/lib/seam/base_resource.rb +63 -0
  7. data/lib/seam/deep_hash_accessor.rb +37 -0
  8. data/lib/seam/default_endpoint.rb +5 -0
  9. data/lib/seam/helpers/action_attempt.rb +45 -0
  10. data/lib/seam/http.rb +52 -0
  11. data/lib/seam/http_multi_workspace.rb +62 -0
  12. data/lib/seam/http_single_workspace.rb +42 -0
  13. data/lib/seam/logger.rb +12 -0
  14. data/lib/seam/lts_version.rb +5 -0
  15. data/lib/seam/options.rb +64 -0
  16. data/lib/seam/parse_options.rb +23 -0
  17. data/lib/seam/request.rb +104 -0
  18. data/lib/seam/routes/clients/access_codes.rb +74 -0
  19. data/lib/seam/routes/clients/access_codes_simulate.rb +18 -0
  20. data/lib/seam/routes/clients/access_codes_unmanaged.rb +42 -0
  21. data/lib/seam/routes/clients/acs.rb +44 -0
  22. data/lib/seam/routes/clients/acs_access_groups.rb +48 -0
  23. data/lib/seam/routes/clients/acs_access_groups_unmanaged.rb +24 -0
  24. data/lib/seam/routes/clients/acs_credential_pools.rb +18 -0
  25. data/lib/seam/routes/clients/acs_credential_provisioning_automations.rb +18 -0
  26. data/lib/seam/routes/clients/acs_credentials.rb +60 -0
  27. data/lib/seam/routes/clients/acs_credentials_unmanaged.rb +24 -0
  28. data/lib/seam/routes/clients/acs_encoders.rb +36 -0
  29. data/lib/seam/routes/clients/acs_entrances.rb +36 -0
  30. data/lib/seam/routes/clients/acs_systems.rb +30 -0
  31. data/lib/seam/routes/clients/acs_users.rb +78 -0
  32. data/lib/seam/routes/clients/acs_users_unmanaged.rb +24 -0
  33. data/lib/seam/routes/clients/action_attempts.rb +28 -0
  34. data/lib/seam/routes/clients/client_sessions.rb +54 -0
  35. data/lib/seam/routes/clients/connect_webviews.rb +36 -0
  36. data/lib/seam/routes/clients/connected_accounts.rb +36 -0
  37. data/lib/seam/routes/clients/devices.rb +50 -0
  38. data/lib/seam/routes/clients/devices_simulate.rb +30 -0
  39. data/lib/seam/routes/clients/devices_unmanaged.rb +30 -0
  40. data/lib/seam/routes/clients/events.rb +24 -0
  41. data/lib/seam/routes/clients/index.rb +38 -0
  42. data/lib/seam/routes/clients/locks.rb +42 -0
  43. data/lib/seam/routes/clients/networks.rb +24 -0
  44. data/lib/seam/routes/clients/noise_sensors.rb +26 -0
  45. data/lib/seam/routes/clients/noise_sensors_noise_thresholds.rb +42 -0
  46. data/lib/seam/routes/clients/noise_sensors_simulate.rb +18 -0
  47. data/lib/seam/routes/clients/phones.rb +28 -0
  48. data/lib/seam/routes/clients/phones_simulate.rb +18 -0
  49. data/lib/seam/routes/clients/thermostats.rb +108 -0
  50. data/lib/seam/routes/clients/thermostats_schedules.rb +42 -0
  51. data/lib/seam/routes/clients/user_identities.rb +88 -0
  52. data/lib/seam/routes/clients/user_identities_enrollment_automations.rb +36 -0
  53. data/lib/seam/routes/clients/webhooks.rb +42 -0
  54. data/lib/seam/routes/clients/workspaces.rb +40 -0
  55. data/lib/seam/routes/resources/access_code.rb +14 -0
  56. data/lib/seam/routes/resources/acs_access_group.rb +11 -0
  57. data/lib/seam/routes/resources/acs_credential.rb +14 -0
  58. data/lib/seam/routes/resources/acs_credential_pool.rb +11 -0
  59. data/lib/seam/routes/resources/acs_credential_provisioning_automation.rb +11 -0
  60. data/lib/seam/routes/resources/acs_entrance.rb +13 -0
  61. data/lib/seam/routes/resources/acs_system.rb +14 -0
  62. data/lib/seam/routes/resources/acs_user.rb +14 -0
  63. data/lib/seam/routes/resources/action_attempt.rb +9 -0
  64. data/lib/seam/routes/resources/client_session.rb +11 -0
  65. data/lib/seam/routes/resources/connect_webview.rb +11 -0
  66. data/lib/seam/routes/resources/connected_account.rb +14 -0
  67. data/lib/seam/routes/resources/device.rb +14 -0
  68. data/lib/seam/routes/resources/device_provider.rb +9 -0
  69. data/lib/seam/routes/resources/enrollment_automation.rb +11 -0
  70. data/lib/seam/routes/resources/event.rb +11 -0
  71. data/lib/seam/routes/resources/index.rb +33 -0
  72. data/lib/seam/routes/resources/network.rb +11 -0
  73. data/lib/seam/routes/resources/noise_threshold.rb +9 -0
  74. data/lib/seam/routes/resources/phone.rb +14 -0
  75. data/lib/seam/routes/resources/resource_error.rb +11 -0
  76. data/lib/seam/routes/resources/resource_errors_support.rb +11 -0
  77. data/lib/seam/routes/resources/resource_warning.rb +11 -0
  78. data/lib/seam/routes/resources/resource_warnings_support.rb +11 -0
  79. data/lib/seam/routes/resources/service_health.rb +9 -0
  80. data/lib/seam/routes/resources/thermostat_schedule.rb +13 -0
  81. data/lib/seam/routes/resources/unmanaged_access_code.rb +14 -0
  82. data/lib/seam/routes/resources/unmanaged_device.rb +14 -0
  83. data/lib/seam/routes/resources/user_identity.rb +11 -0
  84. data/lib/seam/routes/resources/webhook.rb +9 -0
  85. data/lib/seam/routes/resources/workspace.rb +9 -0
  86. data/lib/seam/routes/routes.rb +94 -0
  87. data/lib/seam/token.rb +53 -0
  88. data/lib/seam/version.rb +1 -1
  89. data/lib/seam/wait_for_action_attempt.rb +32 -0
  90. data/lib/seam/webhook.rb +22 -0
  91. data/lib/seam.rb +20 -3
  92. metadata +143 -3
  93. data/lib/seam/todo.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a88e2aad02f40f721466c0b33699643238246f56a848308cb1ae404182de8585
4
- data.tar.gz: 5f19f1c35824bc75572ca253397d513b77d32aee827db78e6e11a0addefa3384
3
+ metadata.gz: f62e294d25f9d5ed9508e9b5d240197d5a92809dbbbb31719722fb6075fecf85
4
+ data.tar.gz: 51621ca4919764d502838e432d7722949ef504e1629bdb224a103d432cfc94ab
5
5
  SHA512:
6
- metadata.gz: 6614aa8ec8df516ab4c54cbf0203b88efae9fd7f995de8754deddfed00bf5c7f64cdc2c61f28746b38bd5ff9d8268629282387db14fc5351324b6b73e93b455d
7
- data.tar.gz: '03480dd4e1f12fb17f59cb6904734b3357e5c8f9a6d883944499be7b8a31caa527d644a57bfbd65c5fabc015cd293d4146b40698476e3e182203bd56cdf50522'
6
+ metadata.gz: dc17fcb3c4583ccedf6aed31be275ca76ad721ed2364ac3820580ecd5954f4bb778a85e66eb26a804cc20ede4dd2aad8db1a7206ad5d829f3bfd40de6187e520
7
+ data.tar.gz: 15de1ddba2b3b0d727aca93235634fa2fb60aece82a6238f06474c8795af1f7f8cd3b4f240d1facfe9d6ee0bc665bc91cae4af9ed8903a14da1b81f58c4190c2
data/Gemfile.lock CHANGED
@@ -1,90 +1,124 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- seam (2.0.0a1)
4
+ seam (2.0.0b0)
5
+ faraday (~> 2.7)
6
+ faraday-retry (~> 2.2)
7
+ svix (~> 1.30)
5
8
 
6
9
  GEM
7
10
  remote: https://rubygems.org/
8
11
  specs:
12
+ addressable (2.8.7)
13
+ public_suffix (>= 2.0.2, < 7.0)
9
14
  ansi (1.5.0)
10
15
  ast (2.4.2)
16
+ bigdecimal (3.1.8)
17
+ crack (1.0.0)
18
+ bigdecimal
19
+ rexml
11
20
  diff-lcs (1.5.1)
12
- docile (1.4.0)
21
+ docile (1.4.1)
22
+ ethon (0.16.0)
23
+ ffi (>= 1.15.0)
24
+ faraday (2.12.0)
25
+ faraday-net_http (>= 2.0, < 3.4)
26
+ json
27
+ logger
28
+ faraday-net_http (3.3.0)
29
+ net-http
30
+ faraday-retry (2.2.1)
31
+ faraday (~> 2.0)
32
+ ffi (1.17.0)
33
+ ffi (1.17.0-x86_64-linux-gnu)
13
34
  gem-release (2.2.2)
35
+ hashdiff (1.1.1)
14
36
  json (2.7.2)
15
37
  language_server-protocol (3.17.0.3)
16
38
  lint_roller (1.1.0)
39
+ logger (1.6.1)
17
40
  multi_json (1.15.0)
18
- parallel (1.24.0)
41
+ net-http (0.4.1)
42
+ uri
43
+ parallel (1.26.3)
19
44
  parse_gemspec (1.0.0)
20
45
  parse_gemspec-cli (1.0.0)
21
46
  multi_json
22
47
  parse_gemspec
23
48
  thor
24
- parser (3.3.0.5)
49
+ parser (3.3.5.0)
25
50
  ast (~> 2.4.1)
26
51
  racc
27
- racc (1.7.3)
52
+ public_suffix (6.0.1)
53
+ racc (1.8.1)
28
54
  rainbow (3.1.1)
29
55
  rake (13.2.1)
30
- regexp_parser (2.9.0)
31
- rexml (3.2.6)
56
+ regexp_parser (2.9.2)
57
+ rexml (3.3.8)
32
58
  rspec (3.13.0)
33
59
  rspec-core (~> 3.13.0)
34
60
  rspec-expectations (~> 3.13.0)
35
61
  rspec-mocks (~> 3.13.0)
36
- rspec-core (3.13.0)
62
+ rspec-core (3.13.2)
37
63
  rspec-support (~> 3.13.0)
38
- rspec-expectations (3.13.0)
64
+ rspec-expectations (3.13.3)
39
65
  diff-lcs (>= 1.2.0, < 2.0)
40
66
  rspec-support (~> 3.13.0)
41
- rspec-mocks (3.13.0)
67
+ rspec-mocks (3.13.2)
42
68
  diff-lcs (>= 1.2.0, < 2.0)
43
69
  rspec-support (~> 3.13.0)
44
70
  rspec-support (3.13.1)
45
- rubocop (1.62.1)
71
+ rubocop (1.66.1)
46
72
  json (~> 2.3)
47
73
  language_server-protocol (>= 3.17.0)
48
74
  parallel (~> 1.10)
49
75
  parser (>= 3.3.0.2)
50
76
  rainbow (>= 2.2.2, < 4.0)
51
- regexp_parser (>= 1.8, < 3.0)
52
- rexml (>= 3.2.5, < 4.0)
53
- rubocop-ast (>= 1.31.1, < 2.0)
77
+ regexp_parser (>= 2.4, < 3.0)
78
+ rubocop-ast (>= 1.32.2, < 2.0)
54
79
  ruby-progressbar (~> 1.7)
55
80
  unicode-display_width (>= 2.4.0, < 3.0)
56
- rubocop-ast (1.31.2)
57
- parser (>= 3.3.0.4)
58
- rubocop-performance (1.20.2)
81
+ rubocop-ast (1.32.3)
82
+ parser (>= 3.3.1.0)
83
+ rubocop-performance (1.22.1)
59
84
  rubocop (>= 1.48.1, < 2.0)
60
- rubocop-ast (>= 1.30.0, < 2.0)
85
+ rubocop-ast (>= 1.31.1, < 2.0)
61
86
  ruby-progressbar (1.13.0)
62
87
  simplecov (0.22.0)
63
88
  docile (~> 1.1)
64
89
  simplecov-html (~> 0.11)
65
90
  simplecov_json_formatter (~> 0.1)
66
- simplecov-console (0.9.1)
91
+ simplecov-console (0.9.2)
67
92
  ansi
68
93
  simplecov
69
94
  terminal-table
70
- simplecov-html (0.12.3)
95
+ simplecov-html (0.13.1)
71
96
  simplecov_json_formatter (0.1.4)
72
- standard (1.35.1)
97
+ standard (1.41.1)
73
98
  language_server-protocol (~> 3.17.0.2)
74
99
  lint_roller (~> 1.0)
75
- rubocop (~> 1.62.0)
100
+ rubocop (~> 1.66.0)
76
101
  standard-custom (~> 1.0.0)
77
- standard-performance (~> 1.3)
102
+ standard-performance (~> 1.5)
78
103
  standard-custom (1.0.2)
79
104
  lint_roller (~> 1.0)
80
105
  rubocop (~> 1.50)
81
- standard-performance (1.3.1)
106
+ standard-performance (1.5.0)
82
107
  lint_roller (~> 1.1)
83
- rubocop-performance (~> 1.20.2)
108
+ rubocop-performance (~> 1.22.0)
109
+ svix (1.38.0)
110
+ typhoeus (~> 1.0, >= 1.0.1)
84
111
  terminal-table (3.0.2)
85
112
  unicode-display_width (>= 1.1.1, < 3)
86
- thor (1.3.1)
87
- unicode-display_width (2.5.0)
113
+ thor (1.3.2)
114
+ typhoeus (1.4.1)
115
+ ethon (>= 0.9.0)
116
+ unicode-display_width (2.6.0)
117
+ uri (0.13.1)
118
+ webmock (3.0.1)
119
+ addressable (>= 2.3.6)
120
+ crack (>= 0.3.2)
121
+ hashdiff
88
122
 
89
123
  PLATFORMS
90
124
  ruby
@@ -100,6 +134,7 @@ DEPENDENCIES
100
134
  simplecov (~> 0.21)
101
135
  simplecov-console (~> 0.9)
102
136
  standard (~> 1.3)
137
+ webmock (~> 3.0.0)
103
138
 
104
139
  BUNDLED WITH
105
- 2.5.7
140
+ 2.4.19
data/README.md CHANGED
@@ -7,17 +7,337 @@ SDK for the Seam API written in Ruby.
7
7
 
8
8
  ## Description
9
9
 
10
- TODO
10
+ [Seam](https://seam.co) makes it easy to integrate IoT devices with your applications.
11
+ This is an official SDK for the Seam API.
12
+ Please refer to the official [Seam Docs](https://docs.seam.co/latest/) to get started.
13
+
14
+ Parts of this SDK are generated from always up-to-date type information
15
+ provided by [@seamapi/types](https://github.com/seamapi/types/).
16
+ This ensures all API methods, request shapes, and response shapes are
17
+ accurate and fully typed.
18
+
19
+ <!-- toc -->
20
+
21
+ - [Installation](#installation)
22
+ - [Usage](#usage)
23
+ - [Examples](#examples)
24
+ - [List devices](#list-devices)
25
+ - [Unlock a door](#unlock-a-door)
26
+ - [Authentication Method](#authentication-method)
27
+ - [API Key](#api-key)
28
+ - [Personal Access Token](#personal-access-token)
29
+ - [Action Attempts](#action-attempts)
30
+ - [Interacting with Multiple Workspaces](#interacting-with-multiple-workspaces)
31
+ - [Webhooks](#webhooks)
32
+ - [Advanced Usage](#advanced-usage)
33
+ - [Additional Options](#additional-options)
34
+ - [Setting the endpoint](#setting-the-endpoint)
35
+ - [Configuring the Faraday Client](#configuring-the-faraday-client)
36
+ - [Using the Faraday Client](#using-the-faraday-client)
37
+ - [Overriding the Client](#overriding-the-client)
38
+ - [Development and Testing](#development-and-testing)
39
+ - [Quickstart](#quickstart)
40
+ - [Source code](#source-code)
41
+ - [Requirements](#requirements)
42
+ - [Publishing](#publishing)
43
+ - [Automatic](#automatic)
44
+ - [Manual](#manual)
45
+ - [GitHub Actions](#github-actions)
46
+ - [Secrets for Optional GitHub Actions](#secrets-for-optional-github-actions)
47
+ - [Contributing](#contributing)
48
+ - [License](#license)
49
+ - [Warranty](#warranty)
50
+
51
+ <!-- tocstop -->
11
52
 
12
53
  ## Installation
13
54
 
14
- Add this as a dependency to your project using [Bundler] with
55
+ Add this as a dependency to your project using [Bundler] with:
15
56
 
16
57
  ```
17
58
  $ bundle add seam
18
59
  ```
19
60
 
20
- [bundler]: https://bundler.io/
61
+ [Bundler]: https://bundler.io/
62
+
63
+ ## Usage
64
+
65
+ ### Examples
66
+
67
+ > [!NOTE]
68
+ > These examples assume `SEAM_API_KEY` is set in your environment.
69
+
70
+ #### List devices
71
+
72
+ ```ruby
73
+ require "seam"
74
+
75
+ seam = Seam.new
76
+ devices = seam.devices.list
77
+ ```
78
+
79
+ #### Unlock a door
80
+
81
+ ```ruby
82
+ require "seam"
83
+
84
+ seam = Seam.new
85
+ lock = seam.locks.get(name: "Front Door")
86
+ seam.locks.unlock_door(device_id: lock.device_id)
87
+ ```
88
+
89
+ ### Authentication Method
90
+
91
+ The SDK supports API key and personal access token authentication mechanisms.
92
+ Authentication may be configured by passing the corresponding options directly to the `Seam` constructor, or with the more ergonomic static factory methods.
93
+
94
+ #### API Key
95
+
96
+ An API key is scoped to a single workspace and should only be used on the server.
97
+ Obtain one from the Seam Console.
98
+
99
+ ```ruby
100
+ # Set the `SEAM_API_KEY` environment variable
101
+ seam = Seam.new
102
+
103
+ # Pass as a keyword argument to the constructor
104
+ seam = Seam.new(api_key: "your-api-key")
105
+
106
+ # Use the factory method
107
+ seam = Seam.from_api_key("your-api-key")
108
+ ```
109
+
110
+ #### Personal Access Token
111
+
112
+ A Personal Access Token is scoped to a Seam Console user.
113
+ Obtain one from the Seam Console.
114
+ A workspace ID must be provided when using this method and all requests will be scoped to that workspace.
115
+
116
+ ```ruby
117
+ # Pass as an option to the constructor
118
+ seam = Seam.new(
119
+ personal_access_token: "your-personal-access-token",
120
+ workspace_id: "your-workspace-id"
121
+ )
122
+
123
+ # Use the factory method
124
+ seam = Seam.from_personal_access_token(
125
+ "your-personal-access-token",
126
+ "your-workspace-id"
127
+ )
128
+ ```
129
+
130
+ ### Action Attempts
131
+
132
+ Some asynchronous operations, e.g., unlocking a door, return an
133
+ [action attempt](https://docs.seam.co/latest/core-concepts/action-attempts).
134
+ Seam tracks the progress of the requested operation and updates the action attempt
135
+ when it succeeds or fails.
136
+
137
+ To make working with action attempts more convenient for applications,
138
+ this library provides the `wait_for_action_attempt` option and enables it by default.
139
+
140
+ When the `wait_for_action_attempt` option is enabled, the SDK:
141
+
142
+ - Polls the action attempt up to the `timeout`
143
+ at the `polling_interval` (both in seconds).
144
+ - Resolves with a fresh copy of the successful action attempt.
145
+ - Raises a `Seam::ActionAttemptFailedError` if the action attempt is unsuccessful.
146
+ - Raises a `Seam::ActionAttemptTimeoutError` if the action attempt is still pending when the `timeout` is reached.
147
+ - Both errors expose an `action_attempt` property.
148
+
149
+ If you already have an action attempt ID
150
+ and want to wait for it to resolve, simply use:
151
+
152
+ ```ruby
153
+ seam.action_attempts.get(action_attempt_id: action_attempt_id)
154
+ ```
155
+
156
+ Or, to get the current state of an action attempt by ID without waiting:
157
+
158
+ ```ruby
159
+ seam.action_attempts.get(
160
+ action_attempt_id: action_attempt_id,
161
+ wait_for_action_attempt: false
162
+ )
163
+ ```
164
+
165
+ To disable this behavior, set the default option for the client:
166
+
167
+ ```ruby
168
+ seam = Seam.new(
169
+ api_key: "your-api-key",
170
+ wait_for_action_attempt: false
171
+ )
172
+
173
+ seam.locks.unlock_door(device_id: device_id)
174
+ ```
175
+
176
+ or the behavior may be configured per-request:
177
+
178
+ ```ruby
179
+ seam.locks.unlock_door(
180
+ device_id: device_id,
181
+ wait_for_action_attempt: false
182
+ )
183
+ ```
184
+
185
+ The `polling_interval` and `timeout` may be configured for the client or per-request.
186
+ For example:
187
+
188
+ ```ruby
189
+ require "seam"
190
+
191
+ seam = Seam.new("your-api-key")
192
+
193
+ locks = seam.locks.list
194
+
195
+ if locks.empty?
196
+ raise "No locks in this workspace"
197
+ end
198
+
199
+ lock = locks.first
200
+
201
+ begin
202
+ seam.locks.unlock_door(
203
+ device_id: lock.device_id,
204
+ wait_for_action_attempt: {
205
+ timeout: 5.0,
206
+ polling_interval: 1.0
207
+ }
208
+ )
209
+
210
+ puts "Door unlocked"
211
+ rescue Seam::ActionAttemptFailedError
212
+ puts "Could not unlock the door"
213
+ rescue Seam::ActionAttemptTimeoutError
214
+ puts "Door took too long to unlock"
215
+ end
216
+ ```
217
+
218
+ ### Interacting with Multiple Workspaces
219
+
220
+ Some Seam API endpoints interact with multiple workspaces. The `Seam::Http::SeamMultiWorkspace` client is not bound to a specific workspace and may use those endpoints with a personal access token authentication method.
221
+
222
+ A Personal Access Token is scoped to a Seam Console user. Obtain one from the Seam Console.
223
+
224
+ ```ruby
225
+ # Pass as an option to the constructor
226
+ seam = Seam::Http::SeamMultiWorkspace.new(personal_access_token: "your-personal-access-token")
227
+
228
+ # Use the factory method
229
+ seam = Seam::Http::SeamMultiWorkspace.from_personal_access_token("your-personal-access-token")
230
+
231
+ # List workspaces authorized for this Personal Access Token
232
+ workspaces = seam.workspaces.list
233
+ ```
234
+
235
+ ### Webhooks
236
+
237
+ The Seam API implements webhooks using [Svix](https://www.svix.com).This SDK exports a thin wrapper `Seam::Webhook` around the svix package.
238
+ Use it to parse and validate Seam webhook events.
239
+
240
+ > [!TIP]
241
+ > This example is for [Sinatra](https://sinatrarb.com/), see the [Svix docs for more examples in specific frameworks](https://docs.svix.com/receiving/verifying-payloads/how).
242
+
243
+ ```ruby
244
+ require "sinatra"
245
+ require "seam"
246
+
247
+ webhook = Seam::Webhook.new(ENV["SEAM_WEBHOOK_SECRET"])
248
+
249
+ post "/webhook" do
250
+ begin
251
+ headers = {
252
+ "svix-id" => request.env["HTTP_SVIX_ID"],
253
+ "svix-signature" => request.env["HTTP_SVIX_SIGNATURE"],
254
+ "svix-timestamp" => request.env["HTTP_SVIX_TIMESTAMP"]
255
+ }
256
+ data = webhook.verify(request.body.read, headers)
257
+ rescue Seam::WebhookVerificationError
258
+ halt 400, "Bad Request"
259
+ end
260
+
261
+ begin
262
+ store_event(data)
263
+ rescue
264
+ halt 500, "Internal Server Error"
265
+ end
266
+
267
+ 204
268
+ end
269
+
270
+ def store_event(data)
271
+ puts data
272
+ end
273
+ ```
274
+
275
+ ### Advanced Usage
276
+
277
+ #### Additional Options
278
+
279
+ In addition to the various authentication options,
280
+ the constructor takes some advanced options that affect behavior.
281
+
282
+ ```ruby
283
+ seam = Seam.new(
284
+ api_key: "your-api-key",
285
+ endpoint: "https://example.com",
286
+ faraday_options: {},
287
+ faraday_retry_options: {}
288
+ )
289
+ ```
290
+
291
+ When using the static factory methods,
292
+ these options may be passed in as keyword arguments.
293
+
294
+ ```ruby
295
+ seam = Seam.from_api_key("some-api-key",
296
+ endpoint: "https://example.com",
297
+ faraday_options: {},
298
+ faraday_retry_options: {})
299
+ ```
300
+
301
+ #### Setting the endpoint
302
+
303
+ Some contexts may need to override the API endpoint,
304
+ e.g., testing or proxy setups. This option corresponds to the [Faraday](https://lostisland.github.io/faraday/#/) `url` setting.
305
+
306
+ Either pass the `endpoint` option, or set the `SEAM_ENDPOINT` environment variable.
307
+
308
+ #### Configuring the Faraday Client
309
+
310
+ The Faraday client and retry behavior may be configured with custom initiation options
311
+ via [`faraday_option`][faraday_option] and [`faraday_retry_option`][faraday_retry_option].
312
+
313
+ [faraday_option]: https://lostisland.github.io/faraday/#/customization/connection-options?id=connection-options
314
+ [faraday_retry_option]: https://github.com/lostisland/faraday-retry
315
+
316
+ #### Using the Faraday Client
317
+
318
+ The Faraday client is exposed and may be used or configured directly:
319
+
320
+ ```ruby
321
+ require "seam"
322
+ require "faraday"
323
+
324
+ class MyMiddleware < Faraday::Middleware
325
+ def on_complete(env)
326
+ puts env.response.inspect
327
+ end
328
+ end
329
+
330
+ seam = Seam.new
331
+
332
+ seam.client.builder.use MyMiddleware
333
+
334
+ devices = seam.client.get("/devices/list").body["devices"]
335
+ ```
336
+
337
+ #### Overriding the Client
338
+
339
+ A Faraday compatible client may be provided to create a `Seam` instance.
340
+ This API is used internally and is not directly supported.
21
341
 
22
342
  ## Development and Testing
23
343
 
@@ -64,13 +384,14 @@ $ git clone git@github.com:seamapi/ruby-next.git
64
384
 
65
385
  ### Requirements
66
386
 
67
- You will need [Ruby] with [Bundler].
387
+ You will need [Ruby] with [Bundler] and [Node.js] with [npm].
68
388
 
69
389
  Be sure that all commands run under the correct Ruby version, e.g.,
70
390
  if using [rbenv], install the correct version with
71
391
 
72
392
  ```
73
393
  $ rbenv install
394
+ $ npm install
74
395
  ```
75
396
 
76
397
  Install the development dependencies with
@@ -80,6 +401,8 @@ $ bundle install
80
401
  ```
81
402
 
82
403
  [bundler]: https://bundler.io/
404
+ [Node.js]: https://nodejs.org/
405
+ [npm]: https://www.npmjs.com/
83
406
  [ruby]: https://www.ruby-lang.org/
84
407
  [rbenv]: https://github.com/rbenv/rbenv
85
408
 
data/Rakefile CHANGED
@@ -5,7 +5,10 @@ require "rspec/core/rake_task"
5
5
  require "standard/rake"
6
6
 
7
7
  RSpec::Core::RakeTask.new(:spec) do |t|
8
- t.rspec_opts = "--pattern {spec,lib}/**/*_spec.rb"
8
+ t.pattern = [
9
+ "spec/**/*_spec.rb",
10
+ "lib/seam/*_spec.rb"
11
+ ]
9
12
  end
10
13
 
11
14
  task default: %i[lint test]
data/lib/seam/auth.rb ADDED
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "options"
4
+ require_relative "token"
5
+
6
+ module Seam
7
+ module Http
8
+ module Auth
9
+ class SeamInvalidTokenError < StandardError
10
+ def initialize(message)
11
+ super("Seam received an invalid token: #{message}")
12
+ end
13
+ end
14
+
15
+ def self.get_auth_headers(api_key: nil, personal_access_token: nil, workspace_id: nil)
16
+ if Http::Options.seam_http_options_with_api_key?(api_key: api_key, personal_access_token: personal_access_token)
17
+ return get_auth_headers_for_api_key(api_key)
18
+ end
19
+
20
+ if Http::Options.seam_http_options_with_personal_access_token?(personal_access_token: personal_access_token, api_key: api_key,
21
+ workspace_id: workspace_id)
22
+ return get_auth_headers_for_personal_access_token(personal_access_token, workspace_id)
23
+ end
24
+
25
+ raise Http::Options::SeamInvalidOptionsError.new(
26
+ "Must specify an api_key or personal_access_token. " \
27
+ "Attempted reading configuration from the environment, " \
28
+ "but the environment variable SEAM_API_KEY is not set."
29
+ )
30
+ end
31
+
32
+ def self.get_auth_headers_for_api_key(api_key)
33
+ if Auth.client_session_token?(api_key)
34
+ raise SeamInvalidTokenError.new(
35
+ "A Client Session Token cannot be used as an api_key"
36
+ )
37
+ end
38
+
39
+ raise SeamInvalidTokenError.new("A JWT cannot be used as an api_key") if Auth.jwt?(api_key)
40
+
41
+ raise SeamInvalidTokenError.new("An Access Token cannot be used as an api_key") if Auth.access_token?(api_key)
42
+
43
+ if Auth.publishable_key?(api_key)
44
+ raise SeamInvalidTokenError.new(
45
+ "A Publishable Key cannot be used as an api_key"
46
+ )
47
+ end
48
+
49
+ unless Auth.seam_token?(api_key)
50
+ raise SeamInvalidTokenError.new(
51
+ "Unknown or invalid api_key format, expected token to start with #{Auth::TOKEN_PREFIX}"
52
+ )
53
+ end
54
+
55
+ {"authorization" => "Bearer #{api_key}"}
56
+ end
57
+
58
+ def self.get_auth_headers_for_personal_access_token(personal_access_token, workspace_id)
59
+ if Auth.jwt?(personal_access_token)
60
+ raise SeamInvalidTokenError.new(
61
+ "A JWT cannot be used as a personal_access_token"
62
+ )
63
+ end
64
+
65
+ if Auth.client_session_token?(personal_access_token)
66
+ raise SeamInvalidTokenError.new(
67
+ "A Client Session Token cannot be used as a personal_access_token"
68
+ )
69
+ end
70
+
71
+ if Auth.publishable_key?(personal_access_token)
72
+ raise SeamInvalidTokenError.new(
73
+ "A Publishable Key cannot be used as a personal_access_token"
74
+ )
75
+ end
76
+
77
+ unless Auth.access_token?(personal_access_token)
78
+ raise SeamInvalidTokenError.new(
79
+ "Unknown or invalid personal_access_token format, expected token to start with #{Auth::ACCESS_TOKEN_PREFIX}"
80
+ )
81
+ end
82
+
83
+ {
84
+ "authorization" => "Bearer #{personal_access_token}",
85
+ "seam-workspace" => workspace_id
86
+ }
87
+ end
88
+
89
+ def self.get_auth_headers_for_multi_workspace_personal_access_token(personal_access_token)
90
+ if Auth.jwt?(personal_access_token)
91
+ raise SeamInvalidTokenError.new(
92
+ "A JWT cannot be used as a personal_access_token"
93
+ )
94
+ end
95
+
96
+ if Auth.client_session_token?(personal_access_token)
97
+ raise SeamInvalidTokenError.new(
98
+ "A Client Session Token cannot be used as a personal_access_token"
99
+ )
100
+ end
101
+
102
+ if Auth.publishable_key?(personal_access_token)
103
+ raise SeamInvalidTokenError.new(
104
+ "A Publishable Key cannot be used as a personal_access_token"
105
+ )
106
+ end
107
+
108
+ unless Auth.access_token?(personal_access_token)
109
+ raise SeamInvalidTokenError.new(
110
+ "Unknown or invalid personal_access_token format, expected token to start with #{Auth::ACCESS_TOKEN_PREFIX}"
111
+ )
112
+ end
113
+
114
+ {"authorization" => "Bearer #{personal_access_token}"}
115
+ end
116
+ end
117
+ end
118
+ end