seam 2.0.0a2 → 2.0.0b0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +54 -51
  3. data/README.md +323 -3
  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/options.rb +64 -0
  14. data/lib/seam/parse_options.rb +23 -0
  15. data/lib/seam/request.rb +82 -51
  16. data/lib/seam/routes/clients/access_codes.rb +74 -0
  17. data/lib/seam/routes/clients/access_codes_simulate.rb +18 -0
  18. data/lib/seam/routes/clients/access_codes_unmanaged.rb +42 -0
  19. data/lib/seam/routes/clients/acs.rb +44 -0
  20. data/lib/seam/routes/clients/acs_access_groups.rb +48 -0
  21. data/lib/seam/routes/clients/acs_access_groups_unmanaged.rb +24 -0
  22. data/lib/seam/routes/clients/acs_credential_pools.rb +18 -0
  23. data/lib/seam/routes/clients/acs_credential_provisioning_automations.rb +18 -0
  24. data/lib/seam/routes/clients/acs_credentials.rb +60 -0
  25. data/lib/seam/routes/clients/acs_credentials_unmanaged.rb +24 -0
  26. data/lib/seam/routes/clients/acs_encoders.rb +36 -0
  27. data/lib/seam/routes/clients/acs_entrances.rb +36 -0
  28. data/lib/seam/routes/clients/acs_systems.rb +30 -0
  29. data/lib/seam/routes/clients/acs_users.rb +78 -0
  30. data/lib/seam/routes/clients/acs_users_unmanaged.rb +24 -0
  31. data/lib/seam/routes/clients/action_attempts.rb +28 -0
  32. data/lib/seam/routes/clients/client_sessions.rb +54 -0
  33. data/lib/seam/routes/clients/connect_webviews.rb +36 -0
  34. data/lib/seam/routes/clients/connected_accounts.rb +36 -0
  35. data/lib/seam/routes/clients/devices.rb +50 -0
  36. data/lib/seam/routes/clients/devices_simulate.rb +30 -0
  37. data/lib/seam/routes/clients/devices_unmanaged.rb +30 -0
  38. data/lib/seam/routes/clients/events.rb +24 -0
  39. data/lib/seam/routes/clients/index.rb +38 -0
  40. data/lib/seam/routes/clients/locks.rb +42 -0
  41. data/lib/seam/routes/clients/networks.rb +24 -0
  42. data/lib/seam/routes/clients/noise_sensors.rb +26 -0
  43. data/lib/seam/routes/clients/noise_sensors_noise_thresholds.rb +42 -0
  44. data/lib/seam/routes/clients/noise_sensors_simulate.rb +18 -0
  45. data/lib/seam/routes/clients/phones.rb +28 -0
  46. data/lib/seam/routes/clients/phones_simulate.rb +18 -0
  47. data/lib/seam/routes/clients/thermostats.rb +108 -0
  48. data/lib/seam/routes/clients/thermostats_schedules.rb +42 -0
  49. data/lib/seam/routes/clients/user_identities.rb +88 -0
  50. data/lib/seam/routes/clients/user_identities_enrollment_automations.rb +36 -0
  51. data/lib/seam/routes/clients/webhooks.rb +42 -0
  52. data/lib/seam/routes/clients/workspaces.rb +40 -0
  53. data/lib/seam/routes/resources/access_code.rb +14 -0
  54. data/lib/seam/routes/resources/acs_access_group.rb +11 -0
  55. data/lib/seam/routes/resources/acs_credential.rb +14 -0
  56. data/lib/seam/routes/resources/acs_credential_pool.rb +11 -0
  57. data/lib/seam/routes/resources/acs_credential_provisioning_automation.rb +11 -0
  58. data/lib/seam/routes/resources/acs_entrance.rb +13 -0
  59. data/lib/seam/routes/resources/acs_system.rb +14 -0
  60. data/lib/seam/routes/resources/acs_user.rb +14 -0
  61. data/lib/seam/routes/resources/action_attempt.rb +9 -0
  62. data/lib/seam/routes/resources/client_session.rb +11 -0
  63. data/lib/seam/routes/resources/connect_webview.rb +11 -0
  64. data/lib/seam/routes/resources/connected_account.rb +14 -0
  65. data/lib/seam/routes/resources/device.rb +14 -0
  66. data/lib/seam/routes/resources/device_provider.rb +9 -0
  67. data/lib/seam/routes/resources/enrollment_automation.rb +11 -0
  68. data/lib/seam/routes/resources/event.rb +11 -0
  69. data/lib/seam/routes/resources/index.rb +33 -0
  70. data/lib/seam/routes/resources/network.rb +11 -0
  71. data/lib/seam/routes/resources/noise_threshold.rb +9 -0
  72. data/lib/seam/routes/resources/phone.rb +14 -0
  73. data/lib/seam/routes/resources/resource_error.rb +11 -0
  74. data/lib/seam/routes/resources/resource_errors_support.rb +11 -0
  75. data/lib/seam/routes/resources/resource_warning.rb +11 -0
  76. data/lib/seam/routes/resources/resource_warnings_support.rb +11 -0
  77. data/lib/seam/routes/resources/service_health.rb +9 -0
  78. data/lib/seam/routes/resources/thermostat_schedule.rb +13 -0
  79. data/lib/seam/routes/resources/unmanaged_access_code.rb +14 -0
  80. data/lib/seam/routes/resources/unmanaged_device.rb +14 -0
  81. data/lib/seam/routes/resources/user_identity.rb +11 -0
  82. data/lib/seam/routes/resources/webhook.rb +9 -0
  83. data/lib/seam/routes/resources/workspace.rb +9 -0
  84. data/lib/seam/routes/routes.rb +94 -0
  85. data/lib/seam/token.rb +53 -0
  86. data/lib/seam/version.rb +1 -1
  87. data/lib/seam/wait_for_action_attempt.rb +32 -0
  88. data/lib/seam/webhook.rb +22 -0
  89. data/lib/seam.rb +19 -68
  90. metadata +115 -70
  91. data/lib/seam/client.rb +0 -129
  92. data/lib/seam/clients/access_codes.rb +0 -95
  93. data/lib/seam/clients/access_codes_simulate.rb +0 -17
  94. data/lib/seam/clients/access_codes_unmanaged.rb +0 -57
  95. data/lib/seam/clients/acs.rb +0 -35
  96. data/lib/seam/clients/acs_access_groups.rb +0 -57
  97. data/lib/seam/clients/acs_credential_pools.rb +0 -17
  98. data/lib/seam/clients/acs_credential_provisioning_automations.rb +0 -17
  99. data/lib/seam/clients/acs_credentials.rb +0 -77
  100. data/lib/seam/clients/acs_entrances.rb +0 -47
  101. data/lib/seam/clients/acs_systems.rb +0 -27
  102. data/lib/seam/clients/acs_users.rb +0 -117
  103. data/lib/seam/clients/action_attempts.rb +0 -30
  104. data/lib/seam/clients/base_client.rb +0 -21
  105. data/lib/seam/clients/client_sessions.rb +0 -77
  106. data/lib/seam/clients/connect_webviews.rb +0 -47
  107. data/lib/seam/clients/connected_accounts.rb +0 -47
  108. data/lib/seam/clients/devices.rb +0 -65
  109. data/lib/seam/clients/devices_simulate.rb +0 -17
  110. data/lib/seam/clients/devices_unmanaged.rb +0 -37
  111. data/lib/seam/clients/events.rb +0 -27
  112. data/lib/seam/clients/locks.rb +0 -53
  113. data/lib/seam/clients/networks.rb +0 -27
  114. data/lib/seam/clients/noise_sensors.rb +0 -15
  115. data/lib/seam/clients/noise_sensors_noise_thresholds.rb +0 -57
  116. data/lib/seam/clients/noise_sensors_simulate.rb +0 -17
  117. data/lib/seam/clients/phones.rb +0 -31
  118. data/lib/seam/clients/phones_simulate.rb +0 -17
  119. data/lib/seam/clients/thermostats.rb +0 -106
  120. data/lib/seam/clients/thermostats_climate_setting_schedules.rb +0 -57
  121. data/lib/seam/clients/user_identities.rb +0 -131
  122. data/lib/seam/clients/user_identities_enrollment_automations.rb +0 -47
  123. data/lib/seam/clients/webhooks.rb +0 -57
  124. data/lib/seam/clients/workspaces.rb +0 -50
  125. data/lib/seam/resources/access_code.rb +0 -12
  126. data/lib/seam/resources/acs_access_group.rb +0 -9
  127. data/lib/seam/resources/acs_credential.rb +0 -12
  128. data/lib/seam/resources/acs_credential_pool.rb +0 -9
  129. data/lib/seam/resources/acs_credential_provisioning_automation.rb +0 -9
  130. data/lib/seam/resources/acs_entrance.rb +0 -9
  131. data/lib/seam/resources/acs_system.rb +0 -9
  132. data/lib/seam/resources/acs_user.rb +0 -9
  133. data/lib/seam/resources/action_attempt.rb +0 -46
  134. data/lib/seam/resources/base_resource.rb +0 -58
  135. data/lib/seam/resources/client_session.rb +0 -9
  136. data/lib/seam/resources/climate_setting_schedule.rb +0 -11
  137. data/lib/seam/resources/connect_webview.rb +0 -9
  138. data/lib/seam/resources/connected_account.rb +0 -12
  139. data/lib/seam/resources/device.rb +0 -12
  140. data/lib/seam/resources/device_provider.rb +0 -7
  141. data/lib/seam/resources/enrollment_automation.rb +0 -9
  142. data/lib/seam/resources/event.rb +0 -9
  143. data/lib/seam/resources/network.rb +0 -9
  144. data/lib/seam/resources/noise_threshold.rb +0 -7
  145. data/lib/seam/resources/phone.rb +0 -12
  146. data/lib/seam/resources/resource_error.rb +0 -9
  147. data/lib/seam/resources/resource_errors_support.rb +0 -9
  148. data/lib/seam/resources/resource_warning.rb +0 -9
  149. data/lib/seam/resources/resource_warnings_support.rb +0 -9
  150. data/lib/seam/resources/service_health.rb +0 -7
  151. data/lib/seam/resources/unmanaged_access_code.rb +0 -12
  152. data/lib/seam/resources/unmanaged_device.rb +0 -12
  153. data/lib/seam/resources/user_identity.rb +0 -9
  154. data/lib/seam/resources/webhook.rb +0 -7
  155. data/lib/seam/resources/workspace.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3bb7a5a7828d18d2b54017d76ed325d0e7e25c482de942eca0330da40f7e1105
4
- data.tar.gz: 8a86ca467008bb6f8d2b76394f9f773a938f072a384a29dc78dc16e430917bb3
3
+ metadata.gz: f62e294d25f9d5ed9508e9b5d240197d5a92809dbbbb31719722fb6075fecf85
4
+ data.tar.gz: 51621ca4919764d502838e432d7722949ef504e1629bdb224a103d432cfc94ab
5
5
  SHA512:
6
- metadata.gz: 138e121c4bc2482ca27d3a74df486effb52d749de8e1af57fa95ec20600c109e7b2779794e2724fbe8436192ce3a40481e693857ca19b99a320ca96fe4623fe6
7
- data.tar.gz: 4ae84ca5a842777f890f5df0cbff9252fa22d9658f7d03422abcb992a1412e1a272a538019c0f436290bb0064dea0344292690a919a4aa305ec7b3d84e96a30b
6
+ metadata.gz: dc17fcb3c4583ccedf6aed31be275ca76ad721ed2364ac3820580ecd5954f4bb778a85e66eb26a804cc20ede4dd2aad8db1a7206ad5d829f3bfd40de6187e520
7
+ data.tar.gz: 15de1ddba2b3b0d727aca93235634fa2fb60aece82a6238f06474c8795af1f7f8cd3b4f240d1facfe9d6ee0bc665bc91cae4af9ed8903a14da1b81f58c4190c2
data/Gemfile.lock CHANGED
@@ -1,117 +1,120 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- seam (2.0.0a2)
5
- http (~> 5.2)
4
+ seam (2.0.0b0)
5
+ faraday (~> 2.7)
6
+ faraday-retry (~> 2.2)
7
+ svix (~> 1.30)
6
8
 
7
9
  GEM
8
10
  remote: https://rubygems.org/
9
11
  specs:
10
- addressable (2.8.6)
11
- public_suffix (>= 2.0.2, < 6.0)
12
+ addressable (2.8.7)
13
+ public_suffix (>= 2.0.2, < 7.0)
12
14
  ansi (1.5.0)
13
15
  ast (2.4.2)
14
- base64 (0.2.0)
15
- bigdecimal (3.1.7)
16
+ bigdecimal (3.1.8)
16
17
  crack (1.0.0)
17
18
  bigdecimal
18
19
  rexml
19
20
  diff-lcs (1.5.1)
20
- docile (1.4.0)
21
- domain_name (0.6.20240107)
22
- ffi (1.16.3)
23
- ffi-compiler (1.3.2)
24
- ffi (>= 1.15.5)
25
- rake
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)
26
34
  gem-release (2.2.2)
27
- hashdiff (1.1.0)
28
- http (5.2.0)
29
- addressable (~> 2.8)
30
- base64 (~> 0.1)
31
- http-cookie (~> 1.0)
32
- http-form_data (~> 2.2)
33
- llhttp-ffi (~> 0.5.0)
34
- http-cookie (1.0.5)
35
- domain_name (~> 0.5)
36
- http-form_data (2.3.0)
35
+ hashdiff (1.1.1)
37
36
  json (2.7.2)
38
37
  language_server-protocol (3.17.0.3)
39
38
  lint_roller (1.1.0)
40
- llhttp-ffi (0.5.0)
41
- ffi-compiler (~> 1.0)
42
- rake (~> 13.0)
39
+ logger (1.6.1)
43
40
  multi_json (1.15.0)
44
- parallel (1.24.0)
41
+ net-http (0.4.1)
42
+ uri
43
+ parallel (1.26.3)
45
44
  parse_gemspec (1.0.0)
46
45
  parse_gemspec-cli (1.0.0)
47
46
  multi_json
48
47
  parse_gemspec
49
48
  thor
50
- parser (3.3.0.5)
49
+ parser (3.3.5.0)
51
50
  ast (~> 2.4.1)
52
51
  racc
53
- public_suffix (5.0.5)
54
- racc (1.7.3)
52
+ public_suffix (6.0.1)
53
+ racc (1.8.1)
55
54
  rainbow (3.1.1)
56
55
  rake (13.2.1)
57
- regexp_parser (2.9.0)
58
- rexml (3.2.6)
56
+ regexp_parser (2.9.2)
57
+ rexml (3.3.8)
59
58
  rspec (3.13.0)
60
59
  rspec-core (~> 3.13.0)
61
60
  rspec-expectations (~> 3.13.0)
62
61
  rspec-mocks (~> 3.13.0)
63
- rspec-core (3.13.0)
62
+ rspec-core (3.13.2)
64
63
  rspec-support (~> 3.13.0)
65
- rspec-expectations (3.13.0)
64
+ rspec-expectations (3.13.3)
66
65
  diff-lcs (>= 1.2.0, < 2.0)
67
66
  rspec-support (~> 3.13.0)
68
- rspec-mocks (3.13.0)
67
+ rspec-mocks (3.13.2)
69
68
  diff-lcs (>= 1.2.0, < 2.0)
70
69
  rspec-support (~> 3.13.0)
71
70
  rspec-support (3.13.1)
72
- rubocop (1.62.1)
71
+ rubocop (1.66.1)
73
72
  json (~> 2.3)
74
73
  language_server-protocol (>= 3.17.0)
75
74
  parallel (~> 1.10)
76
75
  parser (>= 3.3.0.2)
77
76
  rainbow (>= 2.2.2, < 4.0)
78
- regexp_parser (>= 1.8, < 3.0)
79
- rexml (>= 3.2.5, < 4.0)
80
- rubocop-ast (>= 1.31.1, < 2.0)
77
+ regexp_parser (>= 2.4, < 3.0)
78
+ rubocop-ast (>= 1.32.2, < 2.0)
81
79
  ruby-progressbar (~> 1.7)
82
80
  unicode-display_width (>= 2.4.0, < 3.0)
83
- rubocop-ast (1.31.2)
84
- parser (>= 3.3.0.4)
85
- rubocop-performance (1.20.2)
81
+ rubocop-ast (1.32.3)
82
+ parser (>= 3.3.1.0)
83
+ rubocop-performance (1.22.1)
86
84
  rubocop (>= 1.48.1, < 2.0)
87
- rubocop-ast (>= 1.30.0, < 2.0)
85
+ rubocop-ast (>= 1.31.1, < 2.0)
88
86
  ruby-progressbar (1.13.0)
89
87
  simplecov (0.22.0)
90
88
  docile (~> 1.1)
91
89
  simplecov-html (~> 0.11)
92
90
  simplecov_json_formatter (~> 0.1)
93
- simplecov-console (0.9.1)
91
+ simplecov-console (0.9.2)
94
92
  ansi
95
93
  simplecov
96
94
  terminal-table
97
- simplecov-html (0.12.3)
95
+ simplecov-html (0.13.1)
98
96
  simplecov_json_formatter (0.1.4)
99
- standard (1.35.1)
97
+ standard (1.41.1)
100
98
  language_server-protocol (~> 3.17.0.2)
101
99
  lint_roller (~> 1.0)
102
- rubocop (~> 1.62.0)
100
+ rubocop (~> 1.66.0)
103
101
  standard-custom (~> 1.0.0)
104
- standard-performance (~> 1.3)
102
+ standard-performance (~> 1.5)
105
103
  standard-custom (1.0.2)
106
104
  lint_roller (~> 1.0)
107
105
  rubocop (~> 1.50)
108
- standard-performance (1.3.1)
106
+ standard-performance (1.5.0)
109
107
  lint_roller (~> 1.1)
110
- rubocop-performance (~> 1.20.2)
108
+ rubocop-performance (~> 1.22.0)
109
+ svix (1.38.0)
110
+ typhoeus (~> 1.0, >= 1.0.1)
111
111
  terminal-table (3.0.2)
112
112
  unicode-display_width (>= 1.1.1, < 3)
113
- thor (1.3.1)
114
- 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)
115
118
  webmock (3.0.1)
116
119
  addressable (>= 2.3.6)
117
120
  crack (>= 0.3.2)
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
 
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/**/*_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