muffin_man 1.1.0 → 1.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c253f9126fb48ecbbe228b42323456c6ddaa88bbb6677f0bedf2bcdfbc432a36
4
- data.tar.gz: 10c960af76cf5f319552791d64e65591f96018d94849fc36a48fe4daa9ee37f2
3
+ metadata.gz: 1034d149292cba5172729ff270ccb038a70c6dcf1b5a9d460add8b7b370f0dea
4
+ data.tar.gz: 4d99306ee66d36d0df05bcd46e4d71a4af2db29b3a2d5562b37d5634b7722469
5
5
  SHA512:
6
- metadata.gz: 377273e4a2f05f4358a4aad602490e2ba16241ca162a208560711ca933bfb06fa1e813929095e624aaf441a7c5c1e5c694e36aec472bb96e64002f5a82ff8384
7
- data.tar.gz: 35769ae5f42ac6f69c50dc90755ecbd27b027e6b1334bf6b06b6527b42b345d1d4cbcb0cd3f5fa42166e11bf6659788dcc7f220dc67b207e4ceb8d62b2b8e845
6
+ metadata.gz: 9e0faedca6d800deb56277c05103859cbdc2ceaebb80ec811ecab69e49d280913e37ecd09630d35bb95ae291e446b71d054877e4b7a6ca245c59b6797e1988a0
7
+ data.tar.gz: 34a92f83302a0f1945f1887bbac28e97c2150d734efffcdba648f91fa42aa5ba89e164f1005460694676c100d6ead10eee9177ca1df88055066acb5efb49e204
data/.gitignore CHANGED
@@ -14,3 +14,4 @@
14
14
  Gemfile.lock
15
15
  *.gem
16
16
  .ruby-version
17
+ .byebug_history
data/.rubocop.yml CHANGED
@@ -11,3 +11,6 @@ Style/StringLiteralsInInterpolation:
11
11
 
12
12
  Layout/LineLength:
13
13
  Max: 120
14
+
15
+ Metrics/MethodLength:
16
+ Max: 30
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 1.4.0
2
+
3
+ - Pass cache key to client for caching access tokens [#11](https://github.com/patterninc/muffin_man/pull/11)
data/README.md CHANGED
@@ -40,6 +40,7 @@ credentials = {
40
40
  aws_secret_access_key: AWS_SECRET_ACCESS_KEY,
41
41
  region: REGION, # This can be one of ['na', 'eu', 'fe'] and defaults to 'na'
42
42
  sts_iam_role_arn: STS_IAM_ROLE_ARN, # Optional
43
+ access_token_cache_key: SELLING_PARTNER_ID, # Optional if you want access token caching
43
44
  }
44
45
  client = MuffinMan::Solicitations::V1.new(credentials)
45
46
  response = client.create_product_review_and_seller_feedback_solicitation(amazon_order_id, marketplace_ids)
@@ -48,6 +49,8 @@ JSON.parse(response.body)
48
49
 
49
50
  You can optionally use Amazon's sandbox environment by specifying `client = MuffinMan::Solicitations.new(credentials, sandbox = true)`
50
51
 
52
+ ### Access Token Caching
53
+
51
54
  You can save and retrieve the LWA refresh token by defining a lambda in your initializers.
52
55
 
53
56
  For example, if you are using Redis as your cache you could define:
@@ -55,14 +58,35 @@ For example, if you are using Redis as your cache you could define:
55
58
  ```ruby
56
59
  @@redis = Redis.new
57
60
  MuffinMan.configure do |config|
58
- config.save_access_token = -> (client_id, token) do
59
- @@redis.set("SP-TOKEN-#{client_id}", token['access_token'], ex: token['expires_in'])
61
+ config.save_access_token = -> (access_token_cache_key, token) do
62
+ @@redis.set("SP-TOKEN-#{access_token_cache_key}", token['access_token'], ex: token['expires_in'])
60
63
  end
61
64
 
62
- config.get_access_token = -> (client_id) { @@redis.get("SP-TOKEN-#{client_id}") }
65
+ config.get_access_token = -> (access_token_cache_key) { @@redis.get("SP-TOKEN-#{access_token_cache_key}") }
63
66
  end
64
67
  ```
65
68
 
69
+ ### Retrieiving the refresh token
70
+
71
+ To retrieve the refresh token from an LWA Website authorization workflow, you can use the LWA helper:
72
+
73
+ ```ruby
74
+ # Get your auth code first, either through the Website oauth flow or Authorization API
75
+ credentials = {
76
+ client_id: CLIENT_ID,
77
+ client_secret: CLIENT_SECRET,
78
+ aws_access_key_id: AWS_ACCESS_KEY_ID,
79
+ aws_secret_access_key: AWS_SECRET_ACCESS_KEY,
80
+ sts_iam_role_arn: STS_IAM_ROLE_ARN, # Optional
81
+ scope: 'sellingpartnerapi::migration' # Grantless scope for MWS migration
82
+ }
83
+ client = MuffinMan::Authorization::V1.new(credentials)
84
+ resp = JSON.parse(client.get_authorization_code(seller_id, developer_id, mws_auth_token).body)
85
+ auth_code = resp['payload']['authorizationCode']
86
+ # Then query retrieve the refresh token to store
87
+ refresh_token = MuffinMan::Lwa::AuthHelper.get_refresh_token(CLIENT_ID, CLIENT_SECRET, auth_code)
88
+ ```
89
+
66
90
  ## Contributing
67
91
 
68
92
  Bug reports and pull requests are welcome on GitHub at https://github.com/patterninc/muffin_man. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/patterninc/muffin_man/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,16 @@
1
+ module MuffinMan
2
+ module Authorization
3
+ class V1 < SpApiClient
4
+ def get_authorization_code(selling_partner_id, developer_id, mws_auth_token)
5
+ @query_params = {
6
+ "sellingPartnerId" => selling_partner_id,
7
+ "developerId" => developer_id,
8
+ "mwsAuthToken" => mws_auth_token
9
+ }
10
+ @request_type = "GET"
11
+ @local_var_path = "/authorization/v1/authorizationCode"
12
+ call_api
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,14 +1,19 @@
1
1
  module MuffinMan
2
2
  module Finances
3
3
  class V0 < SpApiClient
4
-
5
4
  def list_financial_event_groups(max_results_per_page = nil, financial_event_group_started_before = nil, financial_event_group_started_after = nil, next_token = nil)
6
5
  @local_var_path = "/finances/v0/financialEventGroups"
7
6
  @query_params = {}
8
- @query_params['MaxResultsPerPage'] = max_results_per_page unless max_results_per_page.nil?
9
- @query_params['FinancialEventGroupStartedBefore'] = financial_event_group_started_before unless financial_event_group_started_before.nil?
10
- @query_params['FinancialEventGroupStartedAfter'] = financial_event_group_started_after unless financial_event_group_started_after.nil?
11
- @query_params['NextToken'] = next_token unless next_token.nil?
7
+ @query_params["MaxResultsPerPage"] = max_results_per_page unless max_results_per_page.nil?
8
+ unless financial_event_group_started_before.nil?
9
+ @query_params["FinancialEventGroupStartedBefore"] =
10
+ financial_event_group_started_before
11
+ end
12
+ unless financial_event_group_started_after.nil?
13
+ @query_params["FinancialEventGroupStartedAfter"] =
14
+ financial_event_group_started_after
15
+ end
16
+ @query_params["NextToken"] = next_token unless next_token.nil?
12
17
  @request_type = "GET"
13
18
  call_api
14
19
  end
@@ -16,10 +21,10 @@ module MuffinMan
16
21
  def list_financial_events_by_group_id(event_group_id, max_results_per_page = nil, posted_after = nil, posted_before = nil, next_token = nil)
17
22
  @local_var_path = "/finances/v0/financialEventGroups/#{event_group_id}/financialEvents"
18
23
  @query_params = {}
19
- @query_params['MaxResultsPerPage'] = max_results_per_page unless max_results_per_page.nil?
20
- @query_params['PostedAfter'] = posted_after unless posted_after.nil?
21
- @query_params['PostedBefore'] = posted_before unless posted_before.nil?
22
- @query_params['NextToken'] = next_token unless next_token.nil?
24
+ @query_params["MaxResultsPerPage"] = max_results_per_page unless max_results_per_page.nil?
25
+ @query_params["PostedAfter"] = posted_after unless posted_after.nil?
26
+ @query_params["PostedBefore"] = posted_before unless posted_before.nil?
27
+ @query_params["NextToken"] = next_token unless next_token.nil?
23
28
  @request_type = "GET"
24
29
  call_api
25
30
  end
@@ -0,0 +1,27 @@
1
+ module MuffinMan::Lwa
2
+ class AuthHelper
3
+ ACCESS_TOKEN_URL = "https://api.amazon.com/auth/o2/token".freeze
4
+
5
+ def self.get_refresh_token(client_id, client_secret, auth_code)
6
+ body = {
7
+ grant_type: "authorization_code",
8
+ code: auth_code,
9
+ client_id: client_id,
10
+ client_secret: client_secret
11
+ }
12
+ response = Typhoeus.post(
13
+ ACCESS_TOKEN_URL,
14
+ body: URI.encode_www_form(body),
15
+ headers: {
16
+ "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8"
17
+ }
18
+ )
19
+ if response.code != 200
20
+ error_body = JSON.parse(response.body)
21
+ error = "#{error_body["error"]}: #{error_body["error_description"]}"
22
+ raise MuffinMan::Error, error
23
+ end
24
+ JSON.parse(response.body)["refresh_token"]
25
+ end
26
+ end
27
+ end
@@ -44,7 +44,7 @@ module MuffinMan
44
44
  @local_var_path = "/reports/2021-06-30/reports"
45
45
  @request_body = {
46
46
  "reportType" => report_type,
47
- "marketplaceIds" => marketplace_ids,
47
+ "marketplaceIds" => marketplace_ids
48
48
  }
49
49
  @request_body["dataStartTime"] = start_time unless start_time.nil?
50
50
  @request_body["dataEndTime"] = end_time unless end_time.nil?
@@ -11,9 +11,11 @@ module MuffinMan
11
11
  def create_product_review_and_seller_feedback_solicitation(amazon_order_id, marketplace_ids)
12
12
  @amazon_order_id = sandbox ? SANDBOX_AMAZON_ORDER_ID : amazon_order_id
13
13
  @marketplace_ids = sandbox ? SANDBOX_MARKETPLACE_IDS : marketplace_ids
14
- @local_var_path = '/solicitations/v1/orders/{amazonOrderId}/solicitations/productReviewAndSellerFeedback'.sub('{' + 'amazonOrderId' + '}', @amazon_order_id)
14
+ @local_var_path = "/solicitations/v1/orders/{amazonOrderId}/solicitations/productReviewAndSellerFeedback".sub(
15
+ "{" + "amazonOrderId" + "}", @amazon_order_id
16
+ )
15
17
  @query_params = { "marketplaceIds" => @marketplace_ids }
16
- @request_type = 'POST'
18
+ @request_type = "POST"
17
19
  call_api
18
20
  end
19
21
  end
@@ -1,20 +1,22 @@
1
- require 'aws-sigv4'
2
- require 'uri'
3
- require 'aws-sdk-core'
4
- require 'typhoeus'
5
- require 'securerandom'
1
+ require "aws-sigv4"
2
+ require "uri"
3
+ require "aws-sdk-core"
4
+ require "typhoeus"
5
+ require "securerandom"
6
6
 
7
7
  module MuffinMan
8
8
  class SpApiClient
9
9
  attr_reader :refresh_token, :client_id, :client_secret, :aws_access_key_id,
10
- :aws_secret_access_key, :sts_iam_role_arn, :sandbox, :config, :region, :request_type,
11
- :local_var_path, :query_params, :request_body
12
- ACCESS_TOKEN_URL = 'https://api.amazon.com/auth/o2/token'.freeze
13
- SERVICE_NAME = 'execute-api'.freeze
10
+ :aws_secret_access_key, :sts_iam_role_arn, :sandbox, :config,
11
+ :region, :request_type, :local_var_path, :query_params,
12
+ :request_body, :scope, :access_token_cache_key
13
+
14
+ ACCESS_TOKEN_URL = "https://api.amazon.com/auth/o2/token".freeze
15
+ SERVICE_NAME = "execute-api".freeze
14
16
  AWS_REGION_MAP = {
15
- 'na' => 'us-east-1',
16
- 'eu' => 'eu-west-1',
17
- 'fe' => 'us-west-2'
17
+ "na" => "us-east-1",
18
+ "eu" => "eu-west-1",
19
+ "fe" => "us-west-2"
18
20
  }.freeze
19
21
 
20
22
  def initialize(credentials, sandbox = false)
@@ -24,9 +26,11 @@ module MuffinMan
24
26
  @aws_access_key_id = credentials[:aws_access_key_id]
25
27
  @aws_secret_access_key = credentials[:aws_secret_access_key]
26
28
  @sts_iam_role_arn = credentials[:sts_iam_role_arn]
27
- @region = credentials[:region] || 'na'
29
+ @region = credentials[:region] || "na"
30
+ @scope = credentials[:scope]
31
+ @access_token_cache_key = credentials[:access_token_cache_key]
28
32
  @sandbox = sandbox
29
- Typhoeus::Config.user_agent = ''
33
+ Typhoeus::Config.user_agent = ""
30
34
  @config = MuffinMan.configuration
31
35
  end
32
36
 
@@ -38,9 +42,7 @@ module MuffinMan
38
42
 
39
43
  def request_opts
40
44
  opts = { headers: headers }
41
- if request_body
42
- opts[:body] = request_body.to_json
43
- end
45
+ opts[:body] = request_body.to_json if request_body
44
46
  opts
45
47
  end
46
48
 
@@ -63,20 +65,25 @@ module MuffinMan
63
65
  end
64
66
 
65
67
  def retrieve_lwa_access_token
66
- return request_lwa_access_token['access_token'] unless defined?(config.get_access_token)
67
- stored_token = config.get_access_token.call(client_id)
68
+ return request_lwa_access_token["access_token"] unless use_cache?
69
+
70
+ stored_token = config.get_access_token.call(access_token_cache_key)
68
71
  if stored_token.nil?
69
72
  new_token = request_lwa_access_token
70
- config.save_access_token.call(client_id, new_token) if defined?(config.save_access_token)
71
- return new_token['access_token']
73
+ config.save_access_token.call(access_token_cache_key, new_token)
74
+ new_token["access_token"]
72
75
  else
73
- return stored_token
76
+ stored_token
74
77
  end
75
78
  end
76
79
 
80
+ def use_cache?
81
+ defined?(config.save_access_token) && defined?(config.get_access_token) && access_token_cache_key
82
+ end
83
+
77
84
  def request_lwa_access_token
78
85
  body = {
79
- grant_type: 'refresh_token',
86
+ grant_type: "refresh_token",
80
87
  refresh_token: refresh_token,
81
88
  client_id: client_id,
82
89
  client_secret: client_secret
@@ -85,7 +92,29 @@ module MuffinMan
85
92
  ACCESS_TOKEN_URL,
86
93
  body: URI.encode_www_form(body),
87
94
  headers: {
88
- 'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'
95
+ "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8"
96
+ }
97
+ )
98
+ JSON.parse(response.body)
99
+ end
100
+
101
+ def retrieve_grantless_access_token
102
+ # No storage of this type for now
103
+ request_grantless_access_token["access_token"]
104
+ end
105
+
106
+ def request_grantless_access_token
107
+ body = {
108
+ grant_type: "client_credentials",
109
+ scope: scope,
110
+ client_id: client_id,
111
+ client_secret: client_secret
112
+ }
113
+ response = Typhoeus.post(
114
+ ACCESS_TOKEN_URL,
115
+ body: URI.encode_www_form(body),
116
+ headers: {
117
+ "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8"
89
118
  }
90
119
  )
91
120
  JSON.parse(response.body)
@@ -117,17 +146,22 @@ module MuffinMan
117
146
  end
118
147
 
119
148
  def headers
149
+ access_token = scope ? retrieve_grantless_access_token : retrieve_lwa_access_token
120
150
  headers = {
121
- 'x-amz-access-token' => retrieve_lwa_access_token,
122
- 'user-agent' => "MuffinMan/#{VERSION} (Language=Ruby)",
123
- 'content-type' => "application/json"
151
+ "x-amz-access-token" => access_token,
152
+ "user-agent" => "MuffinMan/#{VERSION} (Language=Ruby)",
153
+ "content-type" => "application/json"
124
154
  }
125
155
  signed_request.headers.merge(headers)
126
156
  end
127
157
 
128
158
  def derive_aws_region
129
159
  @aws_region ||= AWS_REGION_MAP[region]
130
- raise MuffinMan::Error.new("#{region} is not supported or does not exist. Region must be one of the following: #{AWS_REGION_MAP.keys.join(', ')}") unless @aws_region
160
+ unless @aws_region
161
+ raise MuffinMan::Error,
162
+ "#{region} is not supported or does not exist. Region must be one of the following: #{AWS_REGION_MAP.keys.join(", ")}"
163
+ end
164
+
131
165
  @aws_region
132
166
  end
133
167
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MuffinMan
4
- VERSION = "1.1.0"
4
+ VERSION = "1.4.0"
5
5
  end
data/lib/muffin_man.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  require "muffin_man/version"
2
2
  require "muffin_man/sp_api_client"
3
+ require "muffin_man/lwa/auth_helper"
3
4
  require "muffin_man/solicitations/v1"
4
5
  require "muffin_man/reports/v20210630"
5
6
  require "muffin_man/catalog_items/v20201201"
6
7
  require "muffin_man/finances/v0"
8
+ require "muffin_man/authorization/v1"
7
9
 
8
10
  module MuffinMan
9
11
  class Error < StandardError; end
data/muffin_man.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "rspec", "~> 3.2"
22
22
  spec.add_development_dependency 'webmock', '~> 2.1'
23
+ spec.add_development_dependency 'byebug'
23
24
  spec.add_development_dependency 'mock_redis', '>=0.14'
24
25
  spec.add_runtime_dependency 'typhoeus', '~> 1.0', '>= 1.0.1'
25
26
  spec.add_runtime_dependency 'aws-sigv4', '>= 1.1'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: muffin_man
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gavin
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2022-04-05 00:00:00.000000000 Z
13
+ date: 2022-05-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -40,6 +40,20 @@ dependencies:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
42
  version: '2.1'
43
+ - !ruby/object:Gem::Dependency
44
+ name: byebug
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
43
57
  - !ruby/object:Gem::Dependency
44
58
  name: mock_redis
45
59
  requirement: !ruby/object:Gem::Requirement
@@ -115,6 +129,7 @@ files:
115
129
  - ".gitignore"
116
130
  - ".rspec"
117
131
  - ".rubocop.yml"
132
+ - CHANGELOG.md
118
133
  - CODE_OF_CONDUCT.md
119
134
  - Gemfile
120
135
  - LICENSE.txt
@@ -123,8 +138,10 @@ files:
123
138
  - bin/console
124
139
  - bin/setup
125
140
  - lib/muffin_man.rb
141
+ - lib/muffin_man/authorization/v1.rb
126
142
  - lib/muffin_man/catalog_items/v20201201.rb
127
143
  - lib/muffin_man/finances/v0.rb
144
+ - lib/muffin_man/lwa/auth_helper.rb
128
145
  - lib/muffin_man/reports/v20210630.rb
129
146
  - lib/muffin_man/solicitations/v1.rb
130
147
  - lib/muffin_man/sp_api_client.rb
@@ -149,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
166
  - !ruby/object:Gem::Version
150
167
  version: '0'
151
168
  requirements: []
152
- rubygems_version: 3.1.6
169
+ rubygems_version: 3.2.22
153
170
  signing_key:
154
171
  specification_version: 4
155
172
  summary: Amazon Selling Partner API client