setsuzoku 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +2 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +7 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +82 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +93 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/lib/setsuzoku.rb +37 -0
  15. data/lib/setsuzoku/api_response.rb +11 -0
  16. data/lib/setsuzoku/api_strategy.rb +124 -0
  17. data/lib/setsuzoku/auth_strategy.rb +72 -0
  18. data/lib/setsuzoku/credential.rb +37 -0
  19. data/lib/setsuzoku/exception.rb +18 -0
  20. data/lib/setsuzoku/external_api_handler.rb +19 -0
  21. data/lib/setsuzoku/pluggable.rb +87 -0
  22. data/lib/setsuzoku/plugin.rb +128 -0
  23. data/lib/setsuzoku/rspec.rb +2 -0
  24. data/lib/setsuzoku/rspec/dynamic_spec_helper.rb +281 -0
  25. data/lib/setsuzoku/service.rb +70 -0
  26. data/lib/setsuzoku/service/web_service.rb +21 -0
  27. data/lib/setsuzoku/service/web_service/api_strategies/rest_api_request.rb +17 -0
  28. data/lib/setsuzoku/service/web_service/api_strategies/rest_strategy.rb +155 -0
  29. data/lib/setsuzoku/service/web_service/api_strategy.rb +169 -0
  30. data/lib/setsuzoku/service/web_service/auth_strategies/basic_auth_strategy.rb +50 -0
  31. data/lib/setsuzoku/service/web_service/auth_strategies/o_auth_strategy.rb +173 -0
  32. data/lib/setsuzoku/service/web_service/auth_strategy.rb +48 -0
  33. data/lib/setsuzoku/service/web_service/credentials/basic_auth_credential.rb +52 -0
  34. data/lib/setsuzoku/service/web_service/credentials/o_auth_credential.rb +83 -0
  35. data/lib/setsuzoku/service/web_service/service.rb +31 -0
  36. data/lib/setsuzoku/utilities.rb +7 -0
  37. data/lib/setsuzoku/version.rb +6 -0
  38. data/setsuzoku.gemspec +50 -0
  39. data/sorbet/config +2 -0
  40. data/sorbet/rbi/gems/activesupport.rbi +1125 -0
  41. data/sorbet/rbi/gems/addressable.rbi +199 -0
  42. data/sorbet/rbi/gems/concurrent-ruby.rbi +1586 -0
  43. data/sorbet/rbi/gems/crack.rbi +62 -0
  44. data/sorbet/rbi/gems/faraday.rbi +615 -0
  45. data/sorbet/rbi/gems/hashdiff.rbi +66 -0
  46. data/sorbet/rbi/gems/httparty.rbi +401 -0
  47. data/sorbet/rbi/gems/i18n.rbi +133 -0
  48. data/sorbet/rbi/gems/mime-types-data.rbi +17 -0
  49. data/sorbet/rbi/gems/mime-types.rbi +218 -0
  50. data/sorbet/rbi/gems/multi_xml.rbi +35 -0
  51. data/sorbet/rbi/gems/multipart-post.rbi +53 -0
  52. data/sorbet/rbi/gems/nokogiri.rbi +1011 -0
  53. data/sorbet/rbi/gems/public_suffix.rbi +104 -0
  54. data/sorbet/rbi/gems/rake.rbi +646 -0
  55. data/sorbet/rbi/gems/rspec-core.rbi +1893 -0
  56. data/sorbet/rbi/gems/rspec-expectations.rbi +1123 -0
  57. data/sorbet/rbi/gems/rspec-mocks.rbi +1090 -0
  58. data/sorbet/rbi/gems/rspec-support.rbi +280 -0
  59. data/sorbet/rbi/gems/rspec.rbi +15 -0
  60. data/sorbet/rbi/gems/safe_yaml.rbi +124 -0
  61. data/sorbet/rbi/gems/thread_safe.rbi +82 -0
  62. data/sorbet/rbi/gems/tzinfo.rbi +406 -0
  63. data/sorbet/rbi/gems/webmock.rbi +532 -0
  64. data/sorbet/rbi/hidden-definitions/hidden.rbi +13722 -0
  65. data/sorbet/rbi/sorbet-typed/lib/activesupport/all/activesupport.rbi +1431 -0
  66. data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +8684 -0
  67. data/sorbet/rbi/sorbet-typed/lib/httparty/all/httparty.rbi +427 -0
  68. data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +108 -0
  69. data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +111 -0
  70. data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +543 -0
  71. data/sorbet/rbi/todo.rbi +11 -0
  72. metadata +255 -0
@@ -0,0 +1,173 @@
1
+ # typed: false
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 OAuthStrategy < 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::OAuthCredential
22
+ end
23
+
24
+ def self.token_valid_for
25
+ 24.hours
26
+ end
27
+
28
+ # Any api request headers that this service needs to set.
29
+ #
30
+ # @return [Hash]
31
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
32
+ def auth_headers
33
+ {
34
+ headers: {
35
+ 'Authorization': "Bearer #{self.credential.token}"
36
+ }
37
+ }
38
+ end
39
+
40
+ # Get a new credential.
41
+ # Exchanges code for token, refresh_token and expires_on.
42
+ #
43
+ # @param args [Hash] the code from the initial auth response { code: 'abcdefg' }.
44
+ #
45
+ # @return void
46
+ sig { override.params(args: T.untyped).void }
47
+ def new_credential!(**args)
48
+ # get a token object based on the code retrieved from login
49
+ get_token!(params(grant_type: 'authorization_code',
50
+ code: args[:code]), :new_token)
51
+ end
52
+
53
+ # If the auth credentials are valid for this instance and auth_strategy.
54
+ #
55
+ # If the token is invalid we should refresh it. And verify that the credentials are now valid.
56
+ # Otherwise the credentials are already valid.
57
+ #
58
+ # @return [Boolean] true if the auth token is valid for the auth_strategy.
59
+ sig { override.returns(T::Boolean) }
60
+ def auth_credential_valid?
61
+ if token_is_invalid?
62
+ refresh_expired_token!
63
+ !token_is_invalid?
64
+ else
65
+ true
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # Determine whether the token is no longer valid.
72
+ #
73
+ # @return [Boolean] true if the token is invalid.
74
+ sig { returns(T::Boolean) }
75
+ def token_is_invalid?
76
+ active = self.credential.respond_to?(:"status") ? self.credential.status != 'disabled' : true
77
+ active && !self.credential.expires_on.blank? &&
78
+ !self.credential.refresh_token.blank? &&
79
+ (self.credential.expires_on < refresh_before_expiration_time)
80
+ end
81
+
82
+ sig { returns(DateTime) }
83
+ def refresh_before_expiration_time
84
+ 45.minutes.from_now.to_datetime
85
+ end
86
+
87
+ # Exchange refresh_token for a new token and expires_on.
88
+ #
89
+ # @return [Boolean] true if the credential was refreshed successfully
90
+ sig { void }
91
+ def refresh_expired_token!
92
+ get_token!(params(grant_type: 'refresh_token',
93
+ refresh_token: self.credential.refresh_token), :refresh_token)
94
+ end
95
+
96
+ # Exchange code for token, refresh_token and expires_on
97
+ #
98
+ # @return void
99
+ sig { params(code: String, redirect_url: String).void }
100
+ def custom_token!(code: nil, redirect_url: nil)
101
+ get_token!(params(grant_type: 'authorization_code',
102
+ code: code,
103
+ redirect_uri: redirect_url), :refresh_token)
104
+ end
105
+
106
+ # Exchange code for a new token via POST request to API token url,
107
+ # and set token, expiry, and status on the integration
108
+ #
109
+ # @param [Hash] body the request body for the token POST request
110
+ #
111
+ # @return void
112
+ sig { params(body: T::Hash[Symbol, String], action: Symbol).void }
113
+ def get_token!(body, action)
114
+ success = false
115
+ request = self.api_strategy.request_class.new(action: action, body: body)
116
+
117
+ resp = self.api_strategy.call_external_api(request: request, strategy: :auth)
118
+
119
+ if resp.data && resp.data[:access_token]
120
+ self.credential.status = 'active' if self.credential.respond_to?(:"status=")
121
+ self.credential.token = resp.data[:access_token]
122
+ self.credential.refresh_token = resp.data[:refresh_token] if resp.data.key?(:refresh_token)
123
+ self.credential.expires_on = if resp.data[:expires_in]
124
+ resp.data[:expires_in].to_i.seconds.from_now
125
+ # elsif resp.data[:issued_at] && self.class.token_valid_for
126
+ # Time.at(resp.data[:issued_at].to_i / 1000) + self.class.token_valid_for
127
+ end
128
+
129
+ self.plugin.set_credential_fields!(resp) if self.plugin.respond_to?(:set_credential_fields!)
130
+
131
+ #TODO: figure out a better way to do this when we add other integrations...
132
+ #
133
+ # if self.is_a?(FacebookIntegration)
134
+ # resp = self.exchange_for_long_lived_token
135
+ # if resp && resp.data['access_token']
136
+ # self.token = resp.data['access_token']
137
+ # self.refresh_token = resp.data['access_token']
138
+ # self.expires_on = 59.days.from_now
139
+ # end
140
+ # end
141
+ else
142
+ self.credential.status = 'error' if self.credential.respond_to?(:"status=")
143
+ end
144
+
145
+ if self.credential.respond_to?(:"save")
146
+ self.credential.save && resp.success
147
+ else
148
+ resp.success
149
+ end
150
+ end
151
+
152
+ # Add some default params to the params hash.
153
+ #
154
+ # @param params [Hash] the original params hash.
155
+ #
156
+ # @return [Hash] the merged params hash.
157
+ sig { params(params: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped]) }
158
+ def params(params)
159
+ if params.key?(:redirect_uri)
160
+ params.merge(client_id: self.credential.client_id,
161
+ client_secret: self.credential.client_secret)
162
+ else
163
+ params.merge(client_id: self.credential.client_id,
164
+ client_secret: self.credential.client_secret,
165
+ redirect_uri: self.credential.redirect_url_override.nil? ? self.credential.redirect_url : self.credential.redirect_url_override)
166
+
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,48 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Setsuzoku
5
+ module Service
6
+ # The Token-based API Authentication Interface definition.
7
+ # Any Plugin that implements this must implement all methods defined here.
8
+ #
9
+ # Defines all necessary methods for handling authentication for any token-based authentication.
10
+ module WebService
11
+
12
+ class AuthStrategy
13
+ include Setsuzoku::AuthStrategy
14
+ extend T::Sig
15
+ extend T::Helpers
16
+ abstract!
17
+
18
+ # The authorization headers that need to be defined for this authentication strategy.
19
+ #
20
+ # @return [Hash(String)] the authorization key/values needed for authentication.
21
+ sig { abstract.returns(T::Hash[Symbol, T.untyped]) }
22
+ def auth_headers;end
23
+
24
+ # These are interface methods that a plugin that implements this strategy must implement.
25
+ module InterfaceMethods
26
+ extend T::Sig
27
+ extend T::Helpers
28
+ abstract!
29
+
30
+ # All auth actions that are implemented.
31
+ #
32
+ # @return [Hash] all auth endpoint definitions for the API.
33
+ sig { abstract.returns(T::Hash[T.untyped, T.untyped]) }
34
+ def auth_actions; end
35
+
36
+ # The base auth url for the plugin.
37
+ #
38
+ # @return [String] the auth url.
39
+ sig { overridable.returns(String) }
40
+ def auth_base_url
41
+ self.plugin.api_base_url
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,52 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Setsuzoku
5
+ module Service
6
+ module WebService
7
+ module Credentials
8
+ module BasicAuthCredential
9
+ include Setsuzoku::Credential
10
+ extend T::Sig
11
+ extend T::Helpers
12
+ abstract!
13
+
14
+ # The username to use for the credential.
15
+ #
16
+ # @return [String] the credential's token.
17
+ sig{ abstract.returns(T.nilable(String)) }
18
+ def username; end
19
+
20
+ # The username to set for the credential.
21
+ #
22
+ # @return [String] the credential's token to set.
23
+ #
24
+ sig{ abstract.params(val: String).returns(T.nilable(String)) }
25
+ def username=(val); end
26
+
27
+ # The password to use for the credential.
28
+ #
29
+ # @return [String] the credential's token.
30
+ sig{ abstract.returns(T.nilable(String)) }
31
+ def password; end
32
+
33
+ # The password to set for the credential.
34
+ #
35
+ # @return [String] the credential's token to set.
36
+ #
37
+ sig{ abstract.params(val: String).returns(T.nilable(String)) }
38
+ def password=(val); end
39
+
40
+ # Stub a basic_auth_credential-like instance.
41
+ #
42
+ # @return [Struct] a stubbed basic_auth_credential-like struct.
43
+ sig { returns(Struct) }
44
+ def self.stub_credential
45
+ s = Struct.new(:username, :password)
46
+ s.new('stubbed_username', 'stubbed_password')
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,83 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require 'active_support/core_ext/numeric/time'
5
+
6
+ module Setsuzoku
7
+ module Service
8
+ module WebService
9
+ module Credentials
10
+ module OAuthCredential
11
+ extend T::Sig
12
+ extend T::Helpers
13
+ abstract!
14
+
15
+ # The application's client_id to o_auth with.
16
+ #
17
+ # @return [String] the client_id for o_auth.
18
+ sig { abstract.returns(T.nilable(String)) }
19
+ def client_id; end
20
+
21
+ # The application's client_secret to o_auth with.
22
+ #
23
+ # @return [String] the client_secret for o_auth.
24
+ sig { abstract.returns(T.nilable(String)) }
25
+ def client_secret; end
26
+
27
+ # The application's redirect url to handle an o_auth redirect
28
+ #
29
+ # @return [String] the redirect url.
30
+ sig { abstract.returns(T.nilable(String)) }
31
+ def redirect_url; end
32
+
33
+ # The token to use for the credential.
34
+ #
35
+ # @return [String] the credential's token.
36
+ sig{ abstract.returns(T.nilable(String)) }
37
+ def token; end
38
+
39
+ # The token to set for the credential.
40
+ #
41
+ # @return [String] the credential's token to set.
42
+ #
43
+ sig{ abstract.params(val: String).returns(T.nilable(String)) }
44
+ def token=(val); end
45
+
46
+ # The refresh token to use for the credential.
47
+ #
48
+ # @return [String] the credential's refresh token.
49
+ sig{ abstract.returns(T.nilable(String)) }
50
+ def refresh_token; end
51
+
52
+ # The refresh token to set for the credential.
53
+ #
54
+ # @return [String] the credential's refresh token to set.
55
+ #
56
+ sig{ abstract.params(val: String).returns(T.nilable(String)) }
57
+ def refresh_token=(val); end
58
+
59
+ # The time the currently valid token expires on.
60
+ #
61
+ # @return [Datetime] the time the current token expires.
62
+ sig{ abstract.returns(T.nilable(DateTime)) }
63
+ def expires_on; end
64
+
65
+ # The time to set for when the token expires on.
66
+ #
67
+ # @return [Datetime] the time to set for the current token's expiry.
68
+ sig{ abstract.params(val: DateTime).returns(T.nilable(DateTime)) }
69
+ def expires_on=(val); end
70
+
71
+ # Stub an o_auth_credential-like instance.
72
+ #
73
+ # @return [Struct] a stubbed o_auth_credential-like struct.
74
+ sig { returns(Struct) }
75
+ def self.stub_credential
76
+ s = Struct.new(:client_id, :client_secret, :redirect_url, :token, :refresh_token, :expires_on, :settings)
77
+ s.new('stubbed_client_id', 'stubbed_client_secret', 'stubbed_redirect_url', 'stubbed_token', 'refresh_token', (Time.now + 30.days), 'extension': 'test')
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,31 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Setsuzoku
5
+ module Service
6
+ module WebService
7
+ # WebService's Service should define all available auth and api strategies.
8
+ class Service
9
+ extend T::Sig
10
+ extend T::Helpers
11
+ include Setsuzoku::Service
12
+
13
+ # The api and auth strategies available for WebService.
14
+ #
15
+ # @return [Hash(Hash(Class))] the available_strategies object for WebService.
16
+ sig { override.returns(T::Hash[Symbol, T::Hash[Symbol, Class]]) }
17
+ def self.available_strategies
18
+ {
19
+ auth: {
20
+ basic: Setsuzoku::Service::WebService::AuthStrategies::BasicAuthStrategy,
21
+ o_auth: Setsuzoku::Service::WebService::AuthStrategies::OAuthStrategy
22
+ },
23
+ api: {
24
+ rest: ApiStrategies::RestStrategy
25
+ }
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ # typed: strict
2
+
3
+ module Setsuzoku
4
+ module Utilities
5
+ require 'setsuzoku/utilities/phone_utility'
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Setsuzoku
5
+ VERSION = '0.10.1'
6
+ end
@@ -0,0 +1,50 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "setsuzoku/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'setsuzoku'
8
+ spec.version = Setsuzoku::VERSION
9
+ spec.authors = ['Luke Stadtler']
10
+ spec.email = ['luke@rocketreferrals.com']
11
+ spec.homepage = 'https://github.com/rocketreferrals/setsuzoku'
12
+ spec.summary = 'Extensible 3rd-party interface and functionality plugins'
13
+ spec.description = 'Easily build generic, reusable 3rd party SDK-like plugins (intended to share).
14
+ Future functionality will include dynamic UIs and other functionality around the plugin.'
15
+ spec.license = 'MIT'
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
+ #
22
+ # spec.metadata["homepage_uri"] = spec.homepage
23
+ # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
24
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
25
+ # else
26
+ # raise "RubyGems 2.0 or newer is required to protect against " \
27
+ # "public gem pushes."
28
+ # end
29
+
30
+ # Specify which files should be added to the gem when it is released.
31
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
32
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
33
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
34
+ end
35
+ spec.bindir = "exe"
36
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
37
+ spec.require_paths = ["lib"]
38
+
39
+ spec.add_development_dependency "bundler", "~> 1.17"
40
+ spec.add_development_dependency "rake", "~> 10.0"
41
+ spec.add_development_dependency "rspec", "~> 3.0"
42
+ spec.add_development_dependency "webmock", "~> 3.8"
43
+ spec.add_runtime_dependency "activesupport", "~> 5.0"
44
+ spec.add_development_dependency "sorbet", "~> 0.5"
45
+ spec.add_runtime_dependency "sorbet-runtime", "~> 0.5"
46
+ #TODO: we should remove httparty if we can just use faraday
47
+ spec.add_runtime_dependency "httparty", "~> 0.16.4"
48
+ spec.add_runtime_dependency "faraday", "~> 0.11"
49
+ spec.add_runtime_dependency "nokogiri", "~> 1.10"
50
+ end