setsuzoku 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
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