help_scout-sdk 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f99c317fac88d6b07729a5a730c26ae64af24ec1ea94eb194c83f9a49c6bfa0
4
- data.tar.gz: 5e7ccef0d2293c93b9c9622de7caca137456f3ccdaa5521add45a08e0a19b916
3
+ metadata.gz: 9d4c58f9f96b57b2d1bbc0cd69ad741ffa6201244861167992e8212901d491d2
4
+ data.tar.gz: 055c66adf697283a7836ce6fdb91156cea27ea1cddf0fc6f8b2c05c07ed174a8
5
5
  SHA512:
6
- metadata.gz: 592babfac0e434ea33a7f6066d26ee9b83a700ee1bbfd7c699a5ed3386384016a191714e31a7e6bd0853dc5ed6a8a4cc2947655ecd8254834bebf163dd0c387d
7
- data.tar.gz: 82b191e5922303d0398b5164f1f50d5a7fa1b488e2647338ad4b7475f629cbe760f00fe970a8b2fa75614c5bb1ab91a46b968b86ad677c2d7c34cedca4f50570
6
+ metadata.gz: 2058da0b726acce0ef9993b40e827bb2be27f2947c78dfcb57216e51acb3fbcd4969b86a658e621e1bd63c1e6410f291a55cb2aaa03fd446374208e2376eeed0
7
+ data.tar.gz: 10b8bbca725910b62a0b3120247506d50b8c7e3f34ed50a39ae2ddeb340d9081803b66d2649e9a9b410de4d1af2eb346372f3b96bcb6e7543f38e6f7c790f163
data/README.md CHANGED
@@ -95,6 +95,19 @@ HelpScout::User.list
95
95
  user = HelpScout::User.get(id)
96
96
  ```
97
97
 
98
+ ### Caching Access Tokens
99
+
100
+ Since short-lived access tokens aren't likely to be embedded into environment variables, it can be difficult to share them across processes. To work around this, you can configure a `token_cache` (and optional `token_cache_key`) to be used to store and retrieve the token until expiry. In general any object that conforms to the `ActiveSupport::Cache::Store` API should work. For example, using an application's Rails cache:
101
+
102
+ ```ruby
103
+ HelpScout.configuration.token_cache = Rails.cache
104
+ HelpScout.configuration.token_cache_key
105
+ # => 'help_scout_token_cache'
106
+ HelpScout.configuration.token_cache_key = 'my-own-key'
107
+ ```
108
+
109
+ With caching configured, whenever the gem attempts to create an access token, it will first attempt to read a value from the cache using the configured cache key. If it's a hit, the cached values will be used to create a new `AccessToken`. If it's a miss, then the gem will make a request to the Help Scout API to retrieve a new token, writing the token's details to the cache before returning the new token.
110
+
98
111
  ## Development
99
112
 
100
113
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -52,7 +52,7 @@ module HelpScout
52
52
  response = new_connection.send(action, path, params.compact)
53
53
 
54
54
  if response.status == 401
55
- access_token.invalidate!
55
+ access_token&.invalidate!
56
56
  response = new_connection.send(action, path, params.compact)
57
57
  end
58
58
 
@@ -1,32 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'date'
4
+ require 'help_scout/api/access_token/cache'
5
+ require 'help_scout/api/access_token/request'
6
+
3
7
  module HelpScout
4
8
  class API
5
9
  class AccessToken
6
10
  class << self
7
11
  def create
8
- connection = HelpScout::API::Client.new(authorize: false).connection
9
- response = connection.post('oauth2/token', token_request_params)
10
-
11
- case response.status
12
- when 200 then new HelpScout::Response.new(response).body
13
- when 429 then raise HelpScout::API::ThrottleLimitReached, response.body&.dig('error')
14
- else raise HelpScout::API::InternalError, "unexpected response (status #{response.status})"
15
- end
16
- end
12
+ cache = HelpScout::API::AccessToken::Cache.new
13
+ request = HelpScout::API::AccessToken::Request.new
17
14
 
18
- def refresh!
19
- HelpScout.api.access_token = create
15
+ cache.configured? ? cache.fetch_token { request.execute } : request.execute
20
16
  end
21
17
 
22
- private
18
+ def refresh!
19
+ return HelpScout.api.access_token unless HelpScout.access_token.nil? || HelpScout.access_token.stale?
23
20
 
24
- def token_request_params
25
- @_token_request_params ||= {
26
- grant_type: 'client_credentials',
27
- client_id: HelpScout.app_id,
28
- client_secret: HelpScout.app_secret
29
- }
21
+ HelpScout.api.access_token = create
30
22
  end
31
23
  end
32
24
 
@@ -35,10 +27,20 @@ module HelpScout
35
27
 
36
28
  def initialize(params)
37
29
  @value = params[:access_token]
38
- @expires_in = params[:expires_in]
39
- return unless @expires_in
40
30
 
41
- @expires_at = Time.now.utc + expires_in
31
+ if params[:expires_at]
32
+ @expires_at = DateTime.parse(params[:expires_at].to_s).to_time.utc
33
+ elsif params[:expires_in]
34
+ @expires_in = params[:expires_in].to_i
35
+ @expires_at = (Time.now.utc + @expires_in)
36
+ end
37
+ end
38
+
39
+ def as_json(*)
40
+ {
41
+ access_token: value,
42
+ expires_at: expires_at
43
+ }
42
44
  end
43
45
 
44
46
  def expired?
@@ -48,10 +50,14 @@ module HelpScout
48
50
  end
49
51
 
50
52
  def invalid?
51
- invalid
53
+ !!invalid # rubocop:disable Style/DoubleNegation
52
54
  end
53
55
 
54
56
  def invalidate!
57
+ cache = HelpScout::API::AccessToken::Cache.new
58
+
59
+ cache.delete if cache.configured?
60
+
55
61
  self.invalid = true
56
62
  end
57
63
 
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HelpScout
4
+ class API
5
+ class AccessToken
6
+ class Cache
7
+ attr_reader :backend, :key
8
+
9
+ def initialize(backend: nil, key: nil)
10
+ @backend = backend || HelpScout.configuration.token_cache
11
+ @key = key || HelpScout.configuration.token_cache_key
12
+ end
13
+
14
+ def configured?
15
+ backend.present? && key.present?
16
+ end
17
+
18
+ def delete
19
+ backend.delete(key)
20
+ end
21
+
22
+ def fetch_token(&token_request)
23
+ raise ArgumentError, 'A request fallback block is required' unless block_given?
24
+
25
+ if (cached_token_json = backend.read(key))
26
+ AccessToken.new(JSON.parse(cached_token_json, symbolize_names: true))
27
+ else
28
+ token_request.call.tap { |token| write(token) }
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def write(token)
35
+ backend.write(key, token.to_json, expires_in: token.expires_in)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HelpScout
4
+ class API
5
+ class AccessToken
6
+ class Request
7
+ attr_reader :response
8
+
9
+ def execute
10
+ @response = request_token
11
+ end
12
+
13
+ private
14
+
15
+ def request_token
16
+ connection = API::Client.new(authorize: false).connection
17
+ http_response = connection.post('oauth2/token', token_request_params)
18
+
19
+ case http_response.status
20
+ when 200 then AccessToken.new(Response.new(http_response).body)
21
+ when 429 then raise API::ThrottleLimitReached, http_response.body&.dig('error')
22
+ else raise API::InternalError, "unexpected response (status #{http_response.status})"
23
+ end
24
+ end
25
+
26
+ def token_request_params
27
+ @_token_request_params ||= {
28
+ grant_type: 'client_credentials',
29
+ client_id: HelpScout.app_id,
30
+ client_secret: HelpScout.app_secret
31
+ }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -10,14 +10,20 @@ module HelpScout
10
10
  end
11
11
 
12
12
  def connection
13
- @_connection ||= begin
14
- HelpScout::API::AccessToken.refresh! if authorize? && token_needs_refresh?
15
- build_connection
13
+ @_connection ||= build_connection.tap do |conn|
14
+ if authorize?
15
+ HelpScout::API::AccessToken.refresh!
16
+ conn.authorization(:Bearer, access_token) if access_token
17
+ end
16
18
  end
17
19
  end
18
20
 
19
21
  private
20
22
 
23
+ def access_token
24
+ HelpScout.access_token&.value
25
+ end
26
+
21
27
  def authorize?
22
28
  authorize
23
29
  end
@@ -25,15 +31,10 @@ module HelpScout
25
31
  def build_connection
26
32
  Faraday.new(url: BASE_URL) do |conn|
27
33
  conn.request :json
28
- conn.authorization(:Bearer, HelpScout.access_token.value) if authorize? && HelpScout.access_token&.value
29
34
  conn.response(:json, content_type: /\bjson$/)
30
35
  conn.adapter(Faraday.default_adapter)
31
36
  end
32
37
  end
33
-
34
- def token_needs_refresh?
35
- HelpScout.access_token.nil? || HelpScout.access_token.stale?
36
- end
37
38
  end
38
39
  end
39
40
  end
@@ -2,13 +2,22 @@
2
2
 
3
3
  module HelpScout
4
4
  class Configuration
5
- attr_accessor :app_id, :app_secret, :default_mailbox
5
+ attr_accessor :app_id, :app_secret, :default_mailbox, :token_cache
6
6
  attr_reader :access_token
7
+ attr_writer :token_cache_key
8
+
9
+ DEFAULT_CACHE_KEY = 'help_scout_token_cache'
7
10
 
8
11
  def access_token=(token_value)
9
12
  return unless token_value
10
13
 
11
14
  @access_token = HelpScout::API::AccessToken.new(access_token: token_value)
12
15
  end
16
+
17
+ def token_cache_key
18
+ return @token_cache_key if defined?(@token_cache_key)
19
+
20
+ @token_cache_key = DEFAULT_CACHE_KEY
21
+ end
13
22
  end
14
23
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HelpScout
4
- VERSION = '1.0.2'
4
+ VERSION = '1.1.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: help_scout-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TaxJar
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-07 00:00:00.000000000 Z
11
+ date: 2019-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -234,6 +234,8 @@ files:
234
234
  - lib/help_scout-sdk.rb
235
235
  - lib/help_scout/api.rb
236
236
  - lib/help_scout/api/access_token.rb
237
+ - lib/help_scout/api/access_token/cache.rb
238
+ - lib/help_scout/api/access_token/request.rb
237
239
  - lib/help_scout/api/client.rb
238
240
  - lib/help_scout/attachment.rb
239
241
  - lib/help_scout/base.rb