seam 2.0.0a1 → 2.0.0b0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +63 -28
- data/README.md +327 -4
- data/Rakefile +4 -1
- data/lib/seam/auth.rb +118 -0
- data/lib/seam/base_resource.rb +63 -0
- data/lib/seam/deep_hash_accessor.rb +37 -0
- data/lib/seam/default_endpoint.rb +5 -0
- data/lib/seam/helpers/action_attempt.rb +45 -0
- data/lib/seam/http.rb +52 -0
- data/lib/seam/http_multi_workspace.rb +62 -0
- data/lib/seam/http_single_workspace.rb +42 -0
- data/lib/seam/logger.rb +12 -0
- data/lib/seam/lts_version.rb +5 -0
- data/lib/seam/options.rb +64 -0
- data/lib/seam/parse_options.rb +23 -0
- data/lib/seam/request.rb +104 -0
- data/lib/seam/routes/clients/access_codes.rb +74 -0
- data/lib/seam/routes/clients/access_codes_simulate.rb +18 -0
- data/lib/seam/routes/clients/access_codes_unmanaged.rb +42 -0
- data/lib/seam/routes/clients/acs.rb +44 -0
- data/lib/seam/routes/clients/acs_access_groups.rb +48 -0
- data/lib/seam/routes/clients/acs_access_groups_unmanaged.rb +24 -0
- data/lib/seam/routes/clients/acs_credential_pools.rb +18 -0
- data/lib/seam/routes/clients/acs_credential_provisioning_automations.rb +18 -0
- data/lib/seam/routes/clients/acs_credentials.rb +60 -0
- data/lib/seam/routes/clients/acs_credentials_unmanaged.rb +24 -0
- data/lib/seam/routes/clients/acs_encoders.rb +36 -0
- data/lib/seam/routes/clients/acs_entrances.rb +36 -0
- data/lib/seam/routes/clients/acs_systems.rb +30 -0
- data/lib/seam/routes/clients/acs_users.rb +78 -0
- data/lib/seam/routes/clients/acs_users_unmanaged.rb +24 -0
- data/lib/seam/routes/clients/action_attempts.rb +28 -0
- data/lib/seam/routes/clients/client_sessions.rb +54 -0
- data/lib/seam/routes/clients/connect_webviews.rb +36 -0
- data/lib/seam/routes/clients/connected_accounts.rb +36 -0
- data/lib/seam/routes/clients/devices.rb +50 -0
- data/lib/seam/routes/clients/devices_simulate.rb +30 -0
- data/lib/seam/routes/clients/devices_unmanaged.rb +30 -0
- data/lib/seam/routes/clients/events.rb +24 -0
- data/lib/seam/routes/clients/index.rb +38 -0
- data/lib/seam/routes/clients/locks.rb +42 -0
- data/lib/seam/routes/clients/networks.rb +24 -0
- data/lib/seam/routes/clients/noise_sensors.rb +26 -0
- data/lib/seam/routes/clients/noise_sensors_noise_thresholds.rb +42 -0
- data/lib/seam/routes/clients/noise_sensors_simulate.rb +18 -0
- data/lib/seam/routes/clients/phones.rb +28 -0
- data/lib/seam/routes/clients/phones_simulate.rb +18 -0
- data/lib/seam/routes/clients/thermostats.rb +108 -0
- data/lib/seam/routes/clients/thermostats_schedules.rb +42 -0
- data/lib/seam/routes/clients/user_identities.rb +88 -0
- data/lib/seam/routes/clients/user_identities_enrollment_automations.rb +36 -0
- data/lib/seam/routes/clients/webhooks.rb +42 -0
- data/lib/seam/routes/clients/workspaces.rb +40 -0
- data/lib/seam/routes/resources/access_code.rb +14 -0
- data/lib/seam/routes/resources/acs_access_group.rb +11 -0
- data/lib/seam/routes/resources/acs_credential.rb +14 -0
- data/lib/seam/routes/resources/acs_credential_pool.rb +11 -0
- data/lib/seam/routes/resources/acs_credential_provisioning_automation.rb +11 -0
- data/lib/seam/routes/resources/acs_entrance.rb +13 -0
- data/lib/seam/routes/resources/acs_system.rb +14 -0
- data/lib/seam/routes/resources/acs_user.rb +14 -0
- data/lib/seam/routes/resources/action_attempt.rb +9 -0
- data/lib/seam/routes/resources/client_session.rb +11 -0
- data/lib/seam/routes/resources/connect_webview.rb +11 -0
- data/lib/seam/routes/resources/connected_account.rb +14 -0
- data/lib/seam/routes/resources/device.rb +14 -0
- data/lib/seam/routes/resources/device_provider.rb +9 -0
- data/lib/seam/routes/resources/enrollment_automation.rb +11 -0
- data/lib/seam/routes/resources/event.rb +11 -0
- data/lib/seam/routes/resources/index.rb +33 -0
- data/lib/seam/routes/resources/network.rb +11 -0
- data/lib/seam/routes/resources/noise_threshold.rb +9 -0
- data/lib/seam/routes/resources/phone.rb +14 -0
- data/lib/seam/routes/resources/resource_error.rb +11 -0
- data/lib/seam/routes/resources/resource_errors_support.rb +11 -0
- data/lib/seam/routes/resources/resource_warning.rb +11 -0
- data/lib/seam/routes/resources/resource_warnings_support.rb +11 -0
- data/lib/seam/routes/resources/service_health.rb +9 -0
- data/lib/seam/routes/resources/thermostat_schedule.rb +13 -0
- data/lib/seam/routes/resources/unmanaged_access_code.rb +14 -0
- data/lib/seam/routes/resources/unmanaged_device.rb +14 -0
- data/lib/seam/routes/resources/user_identity.rb +11 -0
- data/lib/seam/routes/resources/webhook.rb +9 -0
- data/lib/seam/routes/resources/workspace.rb +9 -0
- data/lib/seam/routes/routes.rb +94 -0
- data/lib/seam/token.rb +53 -0
- data/lib/seam/version.rb +1 -1
- data/lib/seam/wait_for_action_attempt.rb +32 -0
- data/lib/seam/webhook.rb +22 -0
- data/lib/seam.rb +20 -3
- metadata +143 -3
- data/lib/seam/todo.rb +0 -9
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "deep_hash_accessor"
|
4
|
+
|
5
|
+
module Seam
|
6
|
+
module Resources
|
7
|
+
class BaseResource
|
8
|
+
attr_accessor :data, :client
|
9
|
+
|
10
|
+
def initialize(data, client = nil)
|
11
|
+
@data = data
|
12
|
+
@client = client
|
13
|
+
|
14
|
+
@data.each do |key, value|
|
15
|
+
value = Seam::DeepHashAccessor.new(value) if value.is_a?(Hash)
|
16
|
+
instance_variable_set(:"@#{key}", value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def update_from_response(data)
|
21
|
+
@data = data
|
22
|
+
@data.each do |key, value|
|
23
|
+
instance_variable_set(:"@#{key}", value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.load_from_response(data, client = nil)
|
28
|
+
if data.is_a?(Array)
|
29
|
+
data.map { |d| new(d, client) }
|
30
|
+
else
|
31
|
+
new(data, client)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
"<#{self.class.name}:#{"0x00%x" % (object_id << 1)}\n" + # rubocop:disable Style/StringConcatenation, Style/FormatString
|
37
|
+
instance_variables
|
38
|
+
.map { |k| k.to_s.sub("@", "") }
|
39
|
+
.filter { |k| k != "data" and k != "client" and respond_to? k }
|
40
|
+
.map { |k| " #{k}=#{send(k).inspect}" }
|
41
|
+
.join("\n") + ">"
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.date_accessor(*attrs)
|
45
|
+
attrs.each do |attr|
|
46
|
+
define_method(attr) do
|
47
|
+
value = instance_variable_get(:"@#{attr}")
|
48
|
+
|
49
|
+
raise "No value for #{attr} set" if value.nil?
|
50
|
+
|
51
|
+
parse_datetime(value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def parse_datetime(value)
|
59
|
+
Time.parse(value)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
module Seam
|
6
|
+
class DeepHashAccessor
|
7
|
+
def initialize(data)
|
8
|
+
@data = data
|
9
|
+
create_accessor_methods
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
instance_variable_get(:"@#{key}")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def create_accessor_methods
|
19
|
+
@data.each do |key, value|
|
20
|
+
define_singleton_method(key) do
|
21
|
+
process_value(value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_value(value)
|
27
|
+
case value
|
28
|
+
when Hash
|
29
|
+
DeepHashAccessor.new(value)
|
30
|
+
when Array
|
31
|
+
value.map { |v| process_value(v) }
|
32
|
+
else
|
33
|
+
value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seam
|
4
|
+
module Helpers
|
5
|
+
module ActionAttempt
|
6
|
+
def self.decide_and_wait(action_attempt, client, wait_for_action_attempt)
|
7
|
+
if wait_for_action_attempt == true
|
8
|
+
return wait_until_finished(action_attempt, client)
|
9
|
+
elsif wait_for_action_attempt.is_a?(Hash)
|
10
|
+
return wait_until_finished(action_attempt, client, timeout: wait_for_action_attempt[:timeout],
|
11
|
+
polling_interval: wait_for_action_attempt[:polling_interval])
|
12
|
+
end
|
13
|
+
|
14
|
+
action_attempt
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.wait_until_finished(action_attempt, client, timeout: nil, polling_interval: nil)
|
18
|
+
timeout = timeout.nil? ? 5.0 : timeout
|
19
|
+
polling_interval = polling_interval.nil? ? 0.5 : polling_interval
|
20
|
+
|
21
|
+
time_waiting = 0.0
|
22
|
+
|
23
|
+
while action_attempt.status == "pending"
|
24
|
+
sleep(polling_interval)
|
25
|
+
time_waiting += polling_interval
|
26
|
+
|
27
|
+
raise Seam::ActionAttemptTimeoutError.new(action_attempt, timeout) if time_waiting > timeout
|
28
|
+
|
29
|
+
action_attempt = update_action_attempt(action_attempt, client)
|
30
|
+
end
|
31
|
+
|
32
|
+
raise Seam::ActionAttemptFailedError.new(action_attempt) if action_attempt.status == "error"
|
33
|
+
|
34
|
+
action_attempt
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.update_action_attempt(action_attempt, client)
|
38
|
+
response = client.get("/action_attempts/get", {action_attempt_id: action_attempt.action_attempt_id})
|
39
|
+
|
40
|
+
action_attempt.update_from_response(response.body["action_attempt"])
|
41
|
+
action_attempt
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/seam/http.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "http_single_workspace"
|
4
|
+
|
5
|
+
module Seam
|
6
|
+
module Http
|
7
|
+
def self.new(**args)
|
8
|
+
Http::SingleWorkspace.new(**args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false)
|
12
|
+
Http::SingleWorkspace.from_api_key(api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false)
|
16
|
+
Http::SingleWorkspace.from_personal_access_token(personal_access_token, workspace_id, endpoint: endpoint,
|
17
|
+
wait_for_action_attempt: wait_for_action_attempt)
|
18
|
+
end
|
19
|
+
|
20
|
+
class ApiError < StandardError
|
21
|
+
attr_reader :code, :status_code, :request_id, :data
|
22
|
+
|
23
|
+
def initialize(error, status_code, request_id)
|
24
|
+
super(error[:message])
|
25
|
+
@code = error[:type]
|
26
|
+
@status_code = status_code
|
27
|
+
@request_id = request_id
|
28
|
+
@data = error[:data]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class UnauthorizedError < ApiError
|
33
|
+
def initialize(request_id)
|
34
|
+
super({type: "unauthorized", message: "Unauthorized"}, 401, request_id)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class InvalidInputError < ApiError
|
39
|
+
attr_reader :validation_errors
|
40
|
+
|
41
|
+
def initialize(error, status_code, request_id)
|
42
|
+
super
|
43
|
+
@code = "invalid_input"
|
44
|
+
@validation_errors = error["validation_errors"] || {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_validation_error_messages(param_name)
|
48
|
+
@validation_errors.dig(param_name, "_errors") || []
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "request"
|
4
|
+
require_relative "parse_options"
|
5
|
+
require_relative "lts_version"
|
6
|
+
require_relative "auth"
|
7
|
+
|
8
|
+
module Seam
|
9
|
+
module Http
|
10
|
+
class MultiWorkspace
|
11
|
+
attr_reader :client, :defaults
|
12
|
+
|
13
|
+
def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true, faraday_options: {},
|
14
|
+
faraday_retry_options: {})
|
15
|
+
@wait_for_action_attempt = wait_for_action_attempt
|
16
|
+
@defaults = {"wait_for_action_attempt" => wait_for_action_attempt}
|
17
|
+
@endpoint = Http::Options.get_endpoint(endpoint)
|
18
|
+
@auth_headers = Http::Auth.get_auth_headers_for_multi_workspace_personal_access_token(personal_access_token)
|
19
|
+
@client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, faraday_options,
|
20
|
+
faraday_retry_options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.lts_version
|
24
|
+
Seam::LTS_VERSION
|
25
|
+
end
|
26
|
+
|
27
|
+
def lts_version
|
28
|
+
Seam::LTS_VERSION
|
29
|
+
end
|
30
|
+
|
31
|
+
def workspaces
|
32
|
+
@workspaces ||= WorkspacesProxy.new(Seam::Clients::Workspaces.new(client: @client, defaults: @defaults))
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true, faraday_options: {}, faraday_retry_options: {})
|
36
|
+
new(
|
37
|
+
personal_access_token: personal_access_token,
|
38
|
+
endpoint: endpoint,
|
39
|
+
wait_for_action_attempt: wait_for_action_attempt,
|
40
|
+
faraday_options: faraday_options,
|
41
|
+
faraday_retry_options: faraday_retry_options
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class WorkspacesProxy
|
47
|
+
def initialize(workspaces)
|
48
|
+
@workspaces = workspaces
|
49
|
+
end
|
50
|
+
|
51
|
+
def list(**kwargs)
|
52
|
+
@workspaces.list(**kwargs)
|
53
|
+
end
|
54
|
+
|
55
|
+
def create(**kwargs)
|
56
|
+
@workspaces.create(**kwargs)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private_constant :WorkspacesProxy
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "request"
|
4
|
+
require_relative "parse_options"
|
5
|
+
require_relative "routes/routes"
|
6
|
+
|
7
|
+
module Seam
|
8
|
+
module Http
|
9
|
+
class SingleWorkspace
|
10
|
+
include Seam::Routes
|
11
|
+
|
12
|
+
attr_reader :client, :defaults
|
13
|
+
|
14
|
+
def initialize(client: nil, api_key: nil, personal_access_token: nil, workspace_id: nil, endpoint: nil,
|
15
|
+
wait_for_action_attempt: true, faraday_options: {}, faraday_retry_options: {})
|
16
|
+
options = Http::Options.parse_options(api_key: api_key, personal_access_token: personal_access_token,
|
17
|
+
workspace_id: workspace_id, endpoint: endpoint)
|
18
|
+
@endpoint = options[:endpoint]
|
19
|
+
@auth_headers = options[:auth_headers]
|
20
|
+
@defaults = Seam::DeepHashAccessor.new({"wait_for_action_attempt" => wait_for_action_attempt})
|
21
|
+
@client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, faraday_options,
|
22
|
+
faraday_retry_options)
|
23
|
+
|
24
|
+
initialize_routes(client: @client, defaults: @defaults)
|
25
|
+
end
|
26
|
+
|
27
|
+
def lts_version
|
28
|
+
Seam::LTS_VERSION
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false, faraday_options: {}, faraday_retry_options: {})
|
32
|
+
new(api_key: api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt,
|
33
|
+
faraday_options: faraday_options, faraday_retry_options: faraday_retry_options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false, faraday_options: {}, faraday_retry_options: {})
|
37
|
+
new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint,
|
38
|
+
wait_for_action_attempt: wait_for_action_attempt, faraday_options: faraday_options, faraday_retry_options: faraday_retry_options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/seam/logger.rb
ADDED
data/lib/seam/options.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "default_endpoint"
|
4
|
+
|
5
|
+
module Seam
|
6
|
+
module Http
|
7
|
+
module Options
|
8
|
+
def self.get_endpoint(endpoint = nil)
|
9
|
+
endpoint || get_endpoint_from_env || Seam::DEFAULT_ENDPOINT
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.get_endpoint_from_env
|
13
|
+
seam_api_url = ENV["SEAM_API_URL"]
|
14
|
+
seam_endpoint = ENV["SEAM_ENDPOINT"]
|
15
|
+
|
16
|
+
if seam_api_url
|
17
|
+
warn "\033[93mUsing the SEAM_API_URL environment variable is deprecated. Support will be removed in a later major version. Use SEAM_ENDPOINT instead.\033[0m"
|
18
|
+
end
|
19
|
+
|
20
|
+
if seam_api_url && seam_endpoint
|
21
|
+
warn "\033[93mDetected both the SEAM_API_URL and SEAM_ENDPOINT environment variables. Using SEAM_ENDPOINT.\033[0m"
|
22
|
+
end
|
23
|
+
|
24
|
+
seam_endpoint || seam_api_url
|
25
|
+
end
|
26
|
+
|
27
|
+
class SeamInvalidOptionsError < StandardError
|
28
|
+
def initialize(message)
|
29
|
+
super("Seam received invalid options: #{message}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.seam_http_options_with_api_key?(api_key: nil, personal_access_token: nil)
|
34
|
+
return false if api_key.nil?
|
35
|
+
|
36
|
+
if personal_access_token
|
37
|
+
raise SeamInvalidOptionsError.new(
|
38
|
+
"The personal_access_token option cannot be used with the api_key option"
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.seam_http_options_with_personal_access_token?(personal_access_token: nil, api_key: nil, workspace_id: nil)
|
46
|
+
return false if personal_access_token.nil?
|
47
|
+
|
48
|
+
if api_key
|
49
|
+
raise SeamInvalidOptionsError.new(
|
50
|
+
"The api_key option cannot be used with the personal_access_token option"
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
if workspace_id.nil?
|
55
|
+
raise SeamInvalidOptionsError.new(
|
56
|
+
"Must pass a workspace_id when using a personal_access_token"
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "auth"
|
4
|
+
require_relative "options"
|
5
|
+
|
6
|
+
module Seam
|
7
|
+
module Http
|
8
|
+
module Options
|
9
|
+
def self.parse_options(api_key: nil, personal_access_token: nil, workspace_id: nil, endpoint: nil)
|
10
|
+
api_key ||= ENV["SEAM_API_KEY"] if personal_access_token.nil?
|
11
|
+
|
12
|
+
auth_headers = Http::Auth.get_auth_headers(
|
13
|
+
api_key: api_key,
|
14
|
+
personal_access_token: personal_access_token,
|
15
|
+
workspace_id: workspace_id
|
16
|
+
)
|
17
|
+
endpoint = Http::Options.get_endpoint(endpoint)
|
18
|
+
|
19
|
+
{auth_headers: auth_headers, endpoint: endpoint}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/seam/request.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "faraday/retry"
|
5
|
+
|
6
|
+
module Seam
|
7
|
+
module Http
|
8
|
+
module Request
|
9
|
+
def self.create_faraday_client(endpoint, auth_headers, faraday_options = {}, faraday_retry_options = {})
|
10
|
+
default_options = {
|
11
|
+
url: endpoint,
|
12
|
+
headers: auth_headers.merge(default_headers)
|
13
|
+
}
|
14
|
+
|
15
|
+
options = deep_merge(default_options, faraday_options)
|
16
|
+
|
17
|
+
default_faraday_retry_options = {
|
18
|
+
max: 2,
|
19
|
+
backoff_factor: 2
|
20
|
+
}
|
21
|
+
|
22
|
+
faraday_retry_options = default_faraday_retry_options.merge(faraday_retry_options)
|
23
|
+
|
24
|
+
Faraday.new(options) do |builder|
|
25
|
+
builder.request :json
|
26
|
+
builder.response :json
|
27
|
+
builder.use ResponseMiddleware
|
28
|
+
builder.request :retry, faraday_retry_options
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.default_headers
|
33
|
+
{
|
34
|
+
"User-Agent" => "seam-ruby/#{Seam::VERSION}",
|
35
|
+
"Content-Type" => "application/json",
|
36
|
+
:"seam-sdk-name" => "seamapi/ruby",
|
37
|
+
:"seam-sdk-version" => Seam::VERSION,
|
38
|
+
:"seam-lts-version" => Seam::LTS_VERSION
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
class ResponseMiddleware < Faraday::Response::RaiseError
|
43
|
+
def on_complete(env)
|
44
|
+
return if env.success?
|
45
|
+
|
46
|
+
status_code = env.status
|
47
|
+
request_id = env.response_headers["seam-request-id"]
|
48
|
+
|
49
|
+
raise Http::UnauthorizedError.new(request_id) if status_code == 401
|
50
|
+
|
51
|
+
if seam_api_error_response?(env)
|
52
|
+
body = JSON.parse(env.body)
|
53
|
+
error = body["error"]
|
54
|
+
error_details = {
|
55
|
+
type: error["type"] || "unknown_error",
|
56
|
+
message: error["message"] || "Unknown error",
|
57
|
+
data: error["data"]
|
58
|
+
}
|
59
|
+
|
60
|
+
if error["type"] == "invalid_input"
|
61
|
+
error_details["validation_errors"] = error["validation_errors"]
|
62
|
+
raise Http::InvalidInputError.new(error_details, status_code, request_id)
|
63
|
+
end
|
64
|
+
|
65
|
+
raise Http::ApiError.new(error_details, status_code, request_id)
|
66
|
+
end
|
67
|
+
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
71
|
+
def seam_api_error_response?(env)
|
72
|
+
return false unless env.response_headers
|
73
|
+
|
74
|
+
content_type = env.response_headers["Content-Type"]
|
75
|
+
return false unless content_type&.start_with?("application/json")
|
76
|
+
|
77
|
+
begin
|
78
|
+
body = JSON.parse(env.body)
|
79
|
+
return false unless body.is_a?(Hash) && body["error"].is_a?(Hash)
|
80
|
+
|
81
|
+
error = body["error"]
|
82
|
+
error["type"].is_a?(String) && error["message"].is_a?(String)
|
83
|
+
rescue JSON::ParserError
|
84
|
+
false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.deep_merge(hash1, hash2)
|
90
|
+
result = hash1.dup
|
91
|
+
hash2.each do |key, value|
|
92
|
+
result[key] = if value.is_a?(Hash) && result[key].is_a?(Hash)
|
93
|
+
deep_merge(result[key], value)
|
94
|
+
else
|
95
|
+
value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
101
|
+
private_class_method :deep_merge
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seam
|
4
|
+
module Clients
|
5
|
+
class AccessCodes
|
6
|
+
def initialize(client:, defaults:)
|
7
|
+
@client = client
|
8
|
+
@defaults = defaults
|
9
|
+
end
|
10
|
+
|
11
|
+
def simulate
|
12
|
+
@simulate ||= Seam::Clients::AccessCodesSimulate.new(client: @client, defaults: @defaults)
|
13
|
+
end
|
14
|
+
|
15
|
+
def unmanaged
|
16
|
+
@unmanaged ||= Seam::Clients::AccessCodesUnmanaged.new(client: @client, defaults: @defaults)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create(device_id:, allow_external_modification: nil, attempt_for_offline_device: nil, code: nil, common_code_key: nil, ends_at: nil, is_external_modification_allowed: nil, is_offline_access_code: nil, is_one_time_use: nil, max_time_rounding: nil, name: nil, prefer_native_scheduling: nil, preferred_code_length: nil, starts_at: nil, sync: nil, use_backup_access_code_pool: nil, use_offline_access_code: nil)
|
20
|
+
res = @client.post("/access_codes/create", {device_id: device_id, allow_external_modification: allow_external_modification, attempt_for_offline_device: attempt_for_offline_device, code: code, common_code_key: common_code_key, ends_at: ends_at, is_external_modification_allowed: is_external_modification_allowed, is_offline_access_code: is_offline_access_code, is_one_time_use: is_one_time_use, max_time_rounding: max_time_rounding, name: name, prefer_native_scheduling: prefer_native_scheduling, preferred_code_length: preferred_code_length, starts_at: starts_at, sync: sync, use_backup_access_code_pool: use_backup_access_code_pool, use_offline_access_code: use_offline_access_code}.compact)
|
21
|
+
|
22
|
+
Seam::Resources::AccessCode.load_from_response(res.body["access_code"])
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_multiple(device_ids:, allow_external_modification: nil, attempt_for_offline_device: nil, behavior_when_code_cannot_be_shared: nil, code: nil, ends_at: nil, is_external_modification_allowed: nil, is_offline_access_code: nil, is_one_time_use: nil, max_time_rounding: nil, name: nil, prefer_native_scheduling: nil, preferred_code_length: nil, starts_at: nil, use_backup_access_code_pool: nil, use_offline_access_code: nil)
|
26
|
+
res = @client.post("/access_codes/create_multiple", {device_ids: device_ids, allow_external_modification: allow_external_modification, attempt_for_offline_device: attempt_for_offline_device, behavior_when_code_cannot_be_shared: behavior_when_code_cannot_be_shared, code: code, ends_at: ends_at, is_external_modification_allowed: is_external_modification_allowed, is_offline_access_code: is_offline_access_code, is_one_time_use: is_one_time_use, max_time_rounding: max_time_rounding, name: name, prefer_native_scheduling: prefer_native_scheduling, preferred_code_length: preferred_code_length, starts_at: starts_at, use_backup_access_code_pool: use_backup_access_code_pool, use_offline_access_code: use_offline_access_code}.compact)
|
27
|
+
|
28
|
+
Seam::Resources::AccessCode.load_from_response(res.body["access_codes"])
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(access_code_id:, device_id: nil, sync: nil)
|
32
|
+
@client.post("/access_codes/delete", {access_code_id: access_code_id, device_id: device_id, sync: sync}.compact)
|
33
|
+
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_code(device_id:)
|
38
|
+
res = @client.post("/access_codes/generate_code", {device_id: device_id}.compact)
|
39
|
+
|
40
|
+
Seam::Resources::AccessCode.load_from_response(res.body["generated_code"])
|
41
|
+
end
|
42
|
+
|
43
|
+
def get(access_code_id: nil, code: nil, device_id: nil)
|
44
|
+
res = @client.post("/access_codes/get", {access_code_id: access_code_id, code: code, device_id: device_id}.compact)
|
45
|
+
|
46
|
+
Seam::Resources::AccessCode.load_from_response(res.body["access_code"])
|
47
|
+
end
|
48
|
+
|
49
|
+
def list(access_code_ids: nil, device_id: nil, user_identifier_key: nil)
|
50
|
+
res = @client.post("/access_codes/list", {access_code_ids: access_code_ids, device_id: device_id, user_identifier_key: user_identifier_key}.compact)
|
51
|
+
|
52
|
+
Seam::Resources::AccessCode.load_from_response(res.body["access_codes"])
|
53
|
+
end
|
54
|
+
|
55
|
+
def pull_backup_access_code(access_code_id:)
|
56
|
+
res = @client.post("/access_codes/pull_backup_access_code", {access_code_id: access_code_id}.compact)
|
57
|
+
|
58
|
+
Seam::Resources::AccessCode.load_from_response(res.body["backup_access_code"])
|
59
|
+
end
|
60
|
+
|
61
|
+
def update(access_code_id:, allow_external_modification: nil, attempt_for_offline_device: nil, code: nil, device_id: nil, ends_at: nil, is_external_modification_allowed: nil, is_managed: nil, is_offline_access_code: nil, is_one_time_use: nil, max_time_rounding: nil, name: nil, prefer_native_scheduling: nil, preferred_code_length: nil, starts_at: nil, sync: nil, type: nil, use_backup_access_code_pool: nil, use_offline_access_code: nil)
|
62
|
+
@client.post("/access_codes/update", {access_code_id: access_code_id, allow_external_modification: allow_external_modification, attempt_for_offline_device: attempt_for_offline_device, code: code, device_id: device_id, ends_at: ends_at, is_external_modification_allowed: is_external_modification_allowed, is_managed: is_managed, is_offline_access_code: is_offline_access_code, is_one_time_use: is_one_time_use, max_time_rounding: max_time_rounding, name: name, prefer_native_scheduling: prefer_native_scheduling, preferred_code_length: preferred_code_length, starts_at: starts_at, sync: sync, type: type, use_backup_access_code_pool: use_backup_access_code_pool, use_offline_access_code: use_offline_access_code}.compact)
|
63
|
+
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def update_multiple(common_code_key:, allow_external_modification: nil, code: nil, ends_at: nil, is_external_modification_allowed: nil, name: nil, prefer_native_scheduling: nil, starts_at: nil)
|
68
|
+
@client.post("/access_codes/update_multiple", {common_code_key: common_code_key, allow_external_modification: allow_external_modification, code: code, ends_at: ends_at, is_external_modification_allowed: is_external_modification_allowed, name: name, prefer_native_scheduling: prefer_native_scheduling, starts_at: starts_at}.compact)
|
69
|
+
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seam
|
4
|
+
module Clients
|
5
|
+
class AccessCodesSimulate
|
6
|
+
def initialize(client:, defaults:)
|
7
|
+
@client = client
|
8
|
+
@defaults = defaults
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_unmanaged_access_code(code:, device_id:, name:)
|
12
|
+
res = @client.post("/access_codes/simulate/create_unmanaged_access_code", {code: code, device_id: device_id, name: name}.compact)
|
13
|
+
|
14
|
+
Seam::Resources::UnmanagedAccessCode.load_from_response(res.body["access_code"])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seam
|
4
|
+
module Clients
|
5
|
+
class AccessCodesUnmanaged
|
6
|
+
def initialize(client:, defaults:)
|
7
|
+
@client = client
|
8
|
+
@defaults = defaults
|
9
|
+
end
|
10
|
+
|
11
|
+
def convert_to_managed(access_code_id:, allow_external_modification: nil, force: nil, is_external_modification_allowed: nil, sync: nil)
|
12
|
+
@client.post("/access_codes/unmanaged/convert_to_managed", {access_code_id: access_code_id, allow_external_modification: allow_external_modification, force: force, is_external_modification_allowed: is_external_modification_allowed, sync: sync}.compact)
|
13
|
+
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete(access_code_id:, sync: nil)
|
18
|
+
@client.post("/access_codes/unmanaged/delete", {access_code_id: access_code_id, sync: sync}.compact)
|
19
|
+
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(access_code_id: nil, code: nil, device_id: nil)
|
24
|
+
res = @client.post("/access_codes/unmanaged/get", {access_code_id: access_code_id, code: code, device_id: device_id}.compact)
|
25
|
+
|
26
|
+
Seam::Resources::UnmanagedAccessCode.load_from_response(res.body["access_code"])
|
27
|
+
end
|
28
|
+
|
29
|
+
def list(device_id:, user_identifier_key: nil)
|
30
|
+
res = @client.post("/access_codes/unmanaged/list", {device_id: device_id, user_identifier_key: user_identifier_key}.compact)
|
31
|
+
|
32
|
+
Seam::Resources::UnmanagedAccessCode.load_from_response(res.body["access_codes"])
|
33
|
+
end
|
34
|
+
|
35
|
+
def update(access_code_id:, is_managed:, allow_external_modification: nil, force: nil, is_external_modification_allowed: nil)
|
36
|
+
@client.post("/access_codes/unmanaged/update", {access_code_id: access_code_id, is_managed: is_managed, allow_external_modification: allow_external_modification, force: force, is_external_modification_allowed: is_external_modification_allowed}.compact)
|
37
|
+
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|