setsuzoku 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitattributes +2 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +82 -0
- data/LICENSE.txt +21 -0
- data/README.md +93 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/setsuzoku.rb +37 -0
- data/lib/setsuzoku/api_response.rb +11 -0
- data/lib/setsuzoku/api_strategy.rb +124 -0
- data/lib/setsuzoku/auth_strategy.rb +72 -0
- data/lib/setsuzoku/credential.rb +37 -0
- data/lib/setsuzoku/exception.rb +18 -0
- data/lib/setsuzoku/external_api_handler.rb +19 -0
- data/lib/setsuzoku/pluggable.rb +87 -0
- data/lib/setsuzoku/plugin.rb +128 -0
- data/lib/setsuzoku/rspec.rb +2 -0
- data/lib/setsuzoku/rspec/dynamic_spec_helper.rb +281 -0
- data/lib/setsuzoku/service.rb +70 -0
- data/lib/setsuzoku/service/web_service.rb +21 -0
- data/lib/setsuzoku/service/web_service/api_strategies/rest_api_request.rb +17 -0
- data/lib/setsuzoku/service/web_service/api_strategies/rest_strategy.rb +155 -0
- data/lib/setsuzoku/service/web_service/api_strategy.rb +169 -0
- data/lib/setsuzoku/service/web_service/auth_strategies/basic_auth_strategy.rb +50 -0
- data/lib/setsuzoku/service/web_service/auth_strategies/o_auth_strategy.rb +173 -0
- data/lib/setsuzoku/service/web_service/auth_strategy.rb +48 -0
- data/lib/setsuzoku/service/web_service/credentials/basic_auth_credential.rb +52 -0
- data/lib/setsuzoku/service/web_service/credentials/o_auth_credential.rb +83 -0
- data/lib/setsuzoku/service/web_service/service.rb +31 -0
- data/lib/setsuzoku/utilities.rb +7 -0
- data/lib/setsuzoku/version.rb +6 -0
- data/setsuzoku.gemspec +50 -0
- data/sorbet/config +2 -0
- data/sorbet/rbi/gems/activesupport.rbi +1125 -0
- data/sorbet/rbi/gems/addressable.rbi +199 -0
- data/sorbet/rbi/gems/concurrent-ruby.rbi +1586 -0
- data/sorbet/rbi/gems/crack.rbi +62 -0
- data/sorbet/rbi/gems/faraday.rbi +615 -0
- data/sorbet/rbi/gems/hashdiff.rbi +66 -0
- data/sorbet/rbi/gems/httparty.rbi +401 -0
- data/sorbet/rbi/gems/i18n.rbi +133 -0
- data/sorbet/rbi/gems/mime-types-data.rbi +17 -0
- data/sorbet/rbi/gems/mime-types.rbi +218 -0
- data/sorbet/rbi/gems/multi_xml.rbi +35 -0
- data/sorbet/rbi/gems/multipart-post.rbi +53 -0
- data/sorbet/rbi/gems/nokogiri.rbi +1011 -0
- data/sorbet/rbi/gems/public_suffix.rbi +104 -0
- data/sorbet/rbi/gems/rake.rbi +646 -0
- data/sorbet/rbi/gems/rspec-core.rbi +1893 -0
- data/sorbet/rbi/gems/rspec-expectations.rbi +1123 -0
- data/sorbet/rbi/gems/rspec-mocks.rbi +1090 -0
- data/sorbet/rbi/gems/rspec-support.rbi +280 -0
- data/sorbet/rbi/gems/rspec.rbi +15 -0
- data/sorbet/rbi/gems/safe_yaml.rbi +124 -0
- data/sorbet/rbi/gems/thread_safe.rbi +82 -0
- data/sorbet/rbi/gems/tzinfo.rbi +406 -0
- data/sorbet/rbi/gems/webmock.rbi +532 -0
- data/sorbet/rbi/hidden-definitions/hidden.rbi +13722 -0
- data/sorbet/rbi/sorbet-typed/lib/activesupport/all/activesupport.rbi +1431 -0
- data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +8684 -0
- data/sorbet/rbi/sorbet-typed/lib/httparty/all/httparty.rbi +427 -0
- data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +108 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +111 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +543 -0
- data/sorbet/rbi/todo.rbi +11 -0
- metadata +255 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Setsuzoku
|
5
|
+
# Core service required for a web service plugin.
|
6
|
+
# It should be able to register all of its available api and auth strategies.
|
7
|
+
# It acts as the orchestration between a plugin, auth_strategy, and api_strategy.
|
8
|
+
module Service
|
9
|
+
autoload :WebService, 'setsuzoku/service/web_service'
|
10
|
+
|
11
|
+
extend Forwardable
|
12
|
+
extend T::Sig
|
13
|
+
extend T::Helpers
|
14
|
+
abstract!
|
15
|
+
|
16
|
+
attr_accessor :plugin
|
17
|
+
attr_accessor :auth_strategy
|
18
|
+
attr_accessor :api_strategy
|
19
|
+
attr_accessor :external_api_handler
|
20
|
+
def_delegators :@auth_strategy, :new_credential!
|
21
|
+
def_delegators :@api_strategy, :call_external_api, :request_class
|
22
|
+
|
23
|
+
def self.included(klass)
|
24
|
+
klass.extend(ClassMethods)
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
extend T::Sig
|
29
|
+
extend T::Helpers
|
30
|
+
abstract!
|
31
|
+
|
32
|
+
# The api and auth strategies available for the service.
|
33
|
+
# api: { simple_name: ApiModule }
|
34
|
+
#
|
35
|
+
# @return [Hash(Hash(Class))] the available_strategies object for the Service.
|
36
|
+
sig { abstract.returns(T::Hash[Symbol, T::Hash[Symbol, Class]]) }
|
37
|
+
def available_strategies; end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Initialize the plugin_service by setting its auth and api strategies.
|
41
|
+
#
|
42
|
+
# @param auth_strategy [AuthStrategy] the auth_strategy for the service to use.
|
43
|
+
# @param api_strategy [ApiStrategy] the api_strategy for the service to use.
|
44
|
+
#
|
45
|
+
# @return [Service] the new instance of service with its correct strategies.
|
46
|
+
sig(:final) do
|
47
|
+
params(
|
48
|
+
plugin: T.untyped,
|
49
|
+
strategies: T::Hash[Symbol, T.untyped],
|
50
|
+
args: T.untyped
|
51
|
+
).returns(T.any(Setsuzoku::Service::WebService::Service, T.untyped))
|
52
|
+
end
|
53
|
+
def initialize(plugin:, strategies:, **args)
|
54
|
+
#TODO: here we need to assign credentials etc, I think.
|
55
|
+
self.plugin = plugin
|
56
|
+
self.external_api_handler = Setsuzoku.external_api_handler.new
|
57
|
+
|
58
|
+
# iterate over all strategies this plugin's service uses and set their configuration
|
59
|
+
strategies.each do |strategy, attrs|
|
60
|
+
# get the strategy type from auth_strategy/api_strategy etc.
|
61
|
+
type = T.must(strategy.to_s.split("_strategy").first).to_sym
|
62
|
+
strat = attrs[:strategy]
|
63
|
+
# associate the strategy with the service
|
64
|
+
self.send("#{strategy}=", self.class.available_strategies[type][strat].new(service: self, credential: args[:credential], **attrs.except(:strategy)))
|
65
|
+
end
|
66
|
+
|
67
|
+
self
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'setsuzoku/service/web_service/api_strategy'
|
5
|
+
require 'setsuzoku/service/web_service/auth_strategy'
|
6
|
+
require 'setsuzoku/service/web_service/service'
|
7
|
+
require 'setsuzoku/service/web_service/api_strategies/rest_strategy'
|
8
|
+
require 'setsuzoku/service/web_service/api_strategies/rest_api_request'
|
9
|
+
require 'setsuzoku/service/web_service/auth_strategies/o_auth_strategy'
|
10
|
+
require 'setsuzoku/service/web_service/auth_strategies/basic_auth_strategy'
|
11
|
+
require 'setsuzoku/service/web_service/credentials/o_auth_credential'
|
12
|
+
require 'setsuzoku/service/web_service/credentials/basic_auth_credential'
|
13
|
+
|
14
|
+
module Setsuzoku
|
15
|
+
module Service
|
16
|
+
module WebService
|
17
|
+
# Core service required for a web service plugin.
|
18
|
+
# It should be able to register all of its available api and auth strategies.
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
# typed: false
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module Setsuzoku
|
6
|
+
module Service
|
7
|
+
module WebService
|
8
|
+
module ApiStrategies
|
9
|
+
class RestAPIRequest < T::Struct
|
10
|
+
prop :action, Symbol, default: nil
|
11
|
+
prop :body, T::Hash[T.untyped, T.untyped], default: {}
|
12
|
+
prop :without_headers, T::Boolean, default: false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'active_support/json'
|
5
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
6
|
+
require 'active_support/core_ext/string/access'
|
7
|
+
|
8
|
+
module Setsuzoku
|
9
|
+
module Service
|
10
|
+
module WebService
|
11
|
+
module ApiStrategies
|
12
|
+
# Defines all necessary methods for handling interfacing with a REST API.
|
13
|
+
class RestStrategy < WebService::ApiStrategy
|
14
|
+
extend T::Sig
|
15
|
+
extend T::Helpers
|
16
|
+
|
17
|
+
def request_class
|
18
|
+
RestAPIRequest
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.required_instance_methods
|
22
|
+
[]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Make a REST API request.
|
26
|
+
# 1. Format the request and send it via the appropriate HTTP method.
|
27
|
+
# 2. Format the response and return it.
|
28
|
+
#
|
29
|
+
# @param request [RestAPIRequest] the constructed API request object.
|
30
|
+
# @param action_details [Hash] the action details for the action to execute.
|
31
|
+
# @param options [Any] additional options needed to pass to correctly perform the request.
|
32
|
+
# options are:
|
33
|
+
# media_type - 'json'
|
34
|
+
#
|
35
|
+
# @return [Hash] the parsed response object.
|
36
|
+
sig { override.params(request: T.untyped, action_details: T::Hash[T.untyped, T.untyped], options: T.untyped).returns(Faraday::Response) }
|
37
|
+
def perform_external_call(request:, action_details:, **options)
|
38
|
+
request_properties = self.get_request_properties(action_name: request.action, action_details: action_details, req_params: request.body)
|
39
|
+
request_options = self.request_options(request_properties[:request_format], action_details[:actions][request.action])
|
40
|
+
|
41
|
+
full_request = self.formulate_request(request_properties, request_options)
|
42
|
+
|
43
|
+
@faraday = Faraday.new(url: request_properties[:formatted_full_url]) do |faraday|
|
44
|
+
faraday.request(:multipart) if options[:attachments].present?
|
45
|
+
faraday.request(:url_encoded)
|
46
|
+
# faraday.basic_auth(request_options[:basic_auth][:username], request_options[:basic_auth][:password]) if request_options[:basic_auth]
|
47
|
+
faraday.request(:basic_auth, request_options[:basic_auth][:username], request_options[:basic_auth][:password]) if request_options[:basic_auth]
|
48
|
+
# faraday.response request_properties[:response_format] if request_properties[:response_format]
|
49
|
+
faraday.authorization(:Bearer, request_properties[:token]) if request_properties[:token]
|
50
|
+
# request_options[:headers][:Authorization].prepend('Bearer ') if request_options[:headers][:Authorization]
|
51
|
+
faraday.adapter Faraday.default_adapter
|
52
|
+
end
|
53
|
+
|
54
|
+
if options[:attachments].present?
|
55
|
+
resp = @faraday.post do |req|
|
56
|
+
io = StringIO.new(full_request)
|
57
|
+
payload = {}
|
58
|
+
payload[:json] = Faraday::UploadIO.new(io, 'application/json')
|
59
|
+
payload[:attachment] = options[:attachments].map{ |file| Faraday::UploadIO.new(file[0], file[1], file[2]) }
|
60
|
+
req.body = payload
|
61
|
+
end
|
62
|
+
else
|
63
|
+
@faraday.headers = request_options[:headers] if request_options[:headers]
|
64
|
+
resp = @faraday.send(request_properties[:request_method], request_properties[:formatted_full_url], full_request)
|
65
|
+
end
|
66
|
+
resp
|
67
|
+
end
|
68
|
+
|
69
|
+
sig do
|
70
|
+
params(
|
71
|
+
action_name: Symbol,
|
72
|
+
for_stub: T::Boolean,
|
73
|
+
req_params: T::Hash[T.untyped, T.untyped],
|
74
|
+
action_details: T::Hash[Symbol, T.untyped]
|
75
|
+
).returns(T::Hash[Symbol, T.untyped])
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_request_properties(action_name:, for_stub: false, req_params: {}, action_details: { actions: self.plugin.api_actions, url: self.plugin.api_base_url })
|
79
|
+
action = action_details[:actions][action_name]
|
80
|
+
request_method, endpoint = action.first
|
81
|
+
request_method = request_method.downcase.to_sym
|
82
|
+
request_format = action[:request_type]
|
83
|
+
response_format = action[:response_type]
|
84
|
+
token = action[:token]
|
85
|
+
stub_data = action[:stub_data] if for_stub
|
86
|
+
full_url = action_details[:url] + endpoint
|
87
|
+
formatted_full_url, req_params = self.replace_dynamic_vars(full_url: full_url, req_params: req_params)
|
88
|
+
{
|
89
|
+
request_method: request_method,
|
90
|
+
endpoint: endpoint,
|
91
|
+
request_format: request_format,
|
92
|
+
response_format: response_format,
|
93
|
+
formatted_full_url: formatted_full_url,
|
94
|
+
req_params: req_params,
|
95
|
+
stub_data: stub_data,
|
96
|
+
token: token
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
# Create the proper request body based on the request_properties and request_options passed
|
101
|
+
#
|
102
|
+
# @param request_properties [Hash] information pertaining to the body of the request
|
103
|
+
# @param request_options [Hash] information pertainint to the headers of the request
|
104
|
+
#
|
105
|
+
# @return full_request [Hash/String] returns the request body in the format required
|
106
|
+
def formulate_request(request_properties = {}, request_options = {})
|
107
|
+
request_format = request_properties.dig(:request_format).to_s
|
108
|
+
content_type = request_options.dig(:headers, :'Content-Type').to_s
|
109
|
+
|
110
|
+
if request_properties[:request_method] == :get || request_properties[:req_params].empty?
|
111
|
+
request_properties[:req_params]
|
112
|
+
else
|
113
|
+
# if the header or request format and request format include urlencoded return the body as a hash
|
114
|
+
if request_format.include?('urlencoded') && content_type.include?('urlencoded')
|
115
|
+
request_properties[:req_params]
|
116
|
+
else
|
117
|
+
# return either xml or json
|
118
|
+
if request_properties[:request_format] == :xml
|
119
|
+
convert_hash_to_xml(request_properties[:req_params])
|
120
|
+
else
|
121
|
+
request_properties[:req_params].to_json
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def request_options(request_format = nil, action_details = {})
|
128
|
+
request_options = self.auth_strategy.auth_headers.merge(self.plugin.api_headers).merge(action_details[:request_options] || {})
|
129
|
+
(request_options[:headers] ||= {})[:'Content-Type'] = "application/#{request_format || :json}"
|
130
|
+
request_options
|
131
|
+
end
|
132
|
+
|
133
|
+
def replace_dynamic_vars(full_url:, req_params: {})
|
134
|
+
# replace matching vars in the action with matching params.
|
135
|
+
# scans the string for variables like {{number_sid}} and replaces it with the matching key in params
|
136
|
+
# removes the params variable, as it's probably intended to be in the url only. If you encounter a need to have
|
137
|
+
# it in the body and the url, then you should put it in params twice with different names.
|
138
|
+
req_params = req_params.dup
|
139
|
+
full_url = full_url.dup
|
140
|
+
dynamic_vars = self.plugin.dynamic_url_params.merge(req_params).with_indifferent_access
|
141
|
+
full_url.scan(/({{.*?}})/).flatten.each do |var|
|
142
|
+
var_name = var.tr('{{ }}', '')
|
143
|
+
next unless dynamic_vars[var_name]
|
144
|
+
|
145
|
+
full_url.gsub!(var, dynamic_vars[var_name])
|
146
|
+
req_params.delete(var_name)
|
147
|
+
req_params.delete(var_name.to_sym)
|
148
|
+
end
|
149
|
+
[full_url, req_params]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Setsuzoku
|
5
|
+
module Service
|
6
|
+
# Defines all common methods for handling interfacing with a Web Service API.
|
7
|
+
module WebService
|
8
|
+
|
9
|
+
class ApiStrategy
|
10
|
+
include Setsuzoku::ApiStrategy
|
11
|
+
extend T::Sig
|
12
|
+
extend T::Helpers
|
13
|
+
|
14
|
+
# These are interface methods that a plugin that implements this strategy must implement.
|
15
|
+
module InterfaceMethods
|
16
|
+
extend T::Sig
|
17
|
+
extend T::Helpers
|
18
|
+
interface!
|
19
|
+
|
20
|
+
# The base Web Service API URL for the plugin.
|
21
|
+
#
|
22
|
+
# @return [String] the API base url.
|
23
|
+
sig { abstract.returns(String) }
|
24
|
+
def api_base_url; end
|
25
|
+
|
26
|
+
# the base webhook api url
|
27
|
+
#
|
28
|
+
# @return [String] the webhook base url
|
29
|
+
def webhook_base_url; end
|
30
|
+
|
31
|
+
# The collection of all implemented endpoints for the API.
|
32
|
+
# Formatted as: { api_action: { 'HTTP_VERB': 'api.com/endpoints' } }
|
33
|
+
#
|
34
|
+
# @return [Hash] all endpoint definitions for the API.
|
35
|
+
sig { abstract.returns(T::Hash[T.untyped, T.untyped]) }
|
36
|
+
def api_actions; end
|
37
|
+
|
38
|
+
# Any api request headers that this service needs to set.
|
39
|
+
#
|
40
|
+
# @return [Hash]
|
41
|
+
sig { abstract.returns(T::Hash[Symbol, T.untyped]) }
|
42
|
+
def api_headers; end
|
43
|
+
|
44
|
+
# All dynamic url parameters provided by the plugin.
|
45
|
+
#
|
46
|
+
# @return [Hash(String)] all parameters that need to be replaced dynamically for url requests.
|
47
|
+
sig { abstract.returns(T::Hash[Symbol, T.nilable(String)]) }
|
48
|
+
def dynamic_url_params; end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Perform the external call for the external API.
|
52
|
+
# Each WebService::ApiStrategy must define how this works.
|
53
|
+
#
|
54
|
+
# It should:
|
55
|
+
# 1. Make the external request
|
56
|
+
# 2. Parse the response
|
57
|
+
# 3. Handle any bad response
|
58
|
+
# 4. Format the response
|
59
|
+
#
|
60
|
+
# @param request [APIRequest] the request object to be used for the request. Each strategy defines its request structure.
|
61
|
+
# @param action_details [Hash] the action_details for the action to execute.
|
62
|
+
# @param options [Any] any additional options needed to pass to correctly perform the request.
|
63
|
+
#
|
64
|
+
# @return [Any] the formatted response object.
|
65
|
+
sig { override.params(request: T.untyped, action_details: T::Hash[T.untyped, T.untyped], options: T.untyped).returns(T.untyped) }
|
66
|
+
def perform_external_call(request:, action_details:, **options); end
|
67
|
+
|
68
|
+
# Parse the response from the API for the given request.
|
69
|
+
# This should just convert JSON strings/XML/SQL rows to a formatted response Hash.
|
70
|
+
#
|
71
|
+
# @param response [HTTParty::Response] the response from the HTTP request.
|
72
|
+
# @param options [Hash] the parsing options. Generally the response_type. e.g. :xml, :json
|
73
|
+
#
|
74
|
+
# @return [Hash] the parsed hash of the response object.
|
75
|
+
sig { override.params(response: Faraday::Response, options: T.untyped).returns(T::Hash[Symbol, T.untyped]) }
|
76
|
+
def parse_response(response:, **options)
|
77
|
+
case options[:response_type]
|
78
|
+
when :json
|
79
|
+
JSON.parse(response.body).deep_symbolize_keys
|
80
|
+
when :xml
|
81
|
+
convert_xml_to_hash(response.body)
|
82
|
+
else
|
83
|
+
JSON.parse(response.body).deep_symbolize_keys
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
sig { params(hash: T::Hash[Symbol, T.untyped], mutate_keys: T::Boolean).returns(String)}
|
90
|
+
def convert_hash_to_xml(hash, mutate_keys = true)
|
91
|
+
hash = hash.map do |k, v|
|
92
|
+
text = if v.is_a?(Hash)
|
93
|
+
convert_hash_to_xml(v)
|
94
|
+
elsif v.is_a?(Array)
|
95
|
+
v.map do |elem|
|
96
|
+
convert_hash_to_xml(elem)
|
97
|
+
end.join
|
98
|
+
else
|
99
|
+
v
|
100
|
+
end
|
101
|
+
k = k.to_s.camelize if mutate_keys
|
102
|
+
"<%s>%s</%s>" % [k, text, k]
|
103
|
+
end.join
|
104
|
+
hash
|
105
|
+
end
|
106
|
+
|
107
|
+
# Convert an XML string to a usable hash.
|
108
|
+
sig { params(xml: String).returns(T::Hash[Symbol, T.untyped])}
|
109
|
+
def convert_xml_to_hash(xml)
|
110
|
+
begin
|
111
|
+
result = Nokogiri::XML(xml)
|
112
|
+
return { result.root.name.underscore.to_sym => xml_node_to_hash(result.root)}
|
113
|
+
rescue Exception => e
|
114
|
+
{}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def xml_node_to_hash(node)
|
119
|
+
# If we are at the root of the document, start the hash
|
120
|
+
if node.element?
|
121
|
+
result_hash = {}
|
122
|
+
if node.attributes != {}
|
123
|
+
attributes = {}
|
124
|
+
node.attributes.keys.each do |key|
|
125
|
+
attributes[node.attributes[key].name.underscore.to_sym] = node.attributes[key].value
|
126
|
+
end
|
127
|
+
end
|
128
|
+
if node.children.size > 0
|
129
|
+
node.children.each do |child|
|
130
|
+
result = xml_node_to_hash(child)
|
131
|
+
|
132
|
+
if child.name == "text"
|
133
|
+
unless child.next_sibling || child.previous_sibling
|
134
|
+
return result unless attributes
|
135
|
+
result_hash[child.name.underscore.to_sym] = result
|
136
|
+
end
|
137
|
+
elsif result_hash[child.name.to_sym]
|
138
|
+
|
139
|
+
if result_hash[child.name.to_sym].is_a?(Array)
|
140
|
+
result_hash[child.name.underscore.to_sym] << result
|
141
|
+
else
|
142
|
+
result_hash[child.name.underscore.to_sym] = [result_hash[child.name.to_sym]] << result
|
143
|
+
end
|
144
|
+
else
|
145
|
+
stripped_children = node.children.reject{ |n| n.text.strip.blank? }
|
146
|
+
if stripped_children.length > 1 && stripped_children.combination(2).all? { |a,b| a.name == b.name }
|
147
|
+
return result_hash[node.name.underscore.to_sym] = stripped_children.map{|n| xml_node_to_hash(n) }
|
148
|
+
else
|
149
|
+
result_hash[child.name.underscore.to_sym] = result
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
if attributes
|
154
|
+
#add code to remove non-data attributes e.g. xml schema, namespace here
|
155
|
+
#if there is a collision then node content supersets attributes
|
156
|
+
result_hash = attributes.merge(result_hash)
|
157
|
+
end
|
158
|
+
return result_hash
|
159
|
+
else
|
160
|
+
return attributes
|
161
|
+
end
|
162
|
+
else
|
163
|
+
return node.content.to_s
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# typed: ignore
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Setsuzoku
|
5
|
+
module Service
|
6
|
+
module WebService
|
7
|
+
module AuthStrategies
|
8
|
+
# The API OAuth Authentication Interface definition.
|
9
|
+
# Any Plugin that implements this must implement all methods required for OAuth.
|
10
|
+
#
|
11
|
+
# Defines all necessary methods for handling authentication for any authentication strategy.
|
12
|
+
class BasicAuthStrategy < WebService::AuthStrategy
|
13
|
+
extend T::Sig
|
14
|
+
extend T::Helpers
|
15
|
+
|
16
|
+
def self.required_instance_methods
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.credential_class
|
21
|
+
Setsuzoku::Service::WebService::Credentials::BasicAuthCredential
|
22
|
+
end
|
23
|
+
|
24
|
+
# OAuth API authentication headers.
|
25
|
+
#
|
26
|
+
# @return [Hash(String)] the formatted authorization headers for an OAuth request
|
27
|
+
sig { override.returns(T::Hash[Symbol, T.untyped]) }
|
28
|
+
def auth_headers
|
29
|
+
{
|
30
|
+
basic_auth: {
|
31
|
+
username: self.credential.username,
|
32
|
+
password: self.credential.password
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# if the auth credientials are valid
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# sig { override.returns(T::Boolean) }
|
42
|
+
def auth_credential_valid?
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|