From 12268ff6f3ba849dcda65bdd6bf595a526db52e8 Mon Sep 17 00:00:00 2001 From: Eric Jensen Date: Thu, 19 Oct 2023 16:22:02 -0400 Subject: [PATCH 1/3] support for restricted operations --- README.md | 19 +++++++++++ lib/restricted_sp_api_client.rb | 57 +++++++++++++++++++++++++++++++++ lib/sp_api_client.rb | 32 +++++++++--------- 3 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 lib/restricted_sp_api_client.rb diff --git a/README.md b/README.md index 9b927b846..d48bd9fb2 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,25 @@ require 'fulfillment-outbound-api-model' end ``` +## Restricted operations + +``` +client = AmzSpApi::RestrictedSpApiClient.new({ + 'restrictedResources' => [ + { + 'method' => 'GET', + 'path' => "/orders/v0/orders/#{my_order_id}", + 'dataElements' => ['buyerInfo', 'shippingAddress'] + } + ] +}) +api_orders = AmzSpApi::OrdersApiModel::OrdersV0Api.new(client) +api_orders.get_order(my_order_id) + +# or you can use models AmzSpApi::RestrictedSpApiClient.new(AmzSpApi::TokensApiModel::CreateRestrictedDataTokenRequest.new(restricted_resources: [ + AmzSpApi::TokensApiModel::RestrictedResource.new(... +``` + ## Feeds and reports This gem also offers encrypt/decrypt helper methods for feeds and reports, but actually using that API as per https://developer-docs.amazon.com/sp-api/docs/ requires the following calls, e.g. for feeds but reports is the same pattern: diff --git a/lib/restricted_sp_api_client.rb b/lib/restricted_sp_api_client.rb new file mode 100644 index 000000000..1315f9cab --- /dev/null +++ b/lib/restricted_sp_api_client.rb @@ -0,0 +1,57 @@ +require 'sp_api_client' +require 'tokens_api_model' + +require 'api_error' +require 'api_client' + +require 'configuration' + +module AmzSpApi + class RestrictedSpApiClient < ApiClient + + def initialize(create_restricted_data_token_request, config = SpConfiguration.default) + super(config) + raise "pass create_restricted_data_token_request to RestrictedSpApiClient.new" if create_restricted_data_token_request.kind_of?(Configuration) + @wrapped_client = SpApiClient.new(config) + @create_restricted_data_token_request = create_restricted_data_token_request + @cache_key = config.access_token_key + "-RDT-" + Digest::MD5.hexdigest(create_restricted_data_token_request.to_s) + end + + alias_method :super_call_api, :call_api + def call_api(http_method, path, opts = {}) + unsigned_request = build_request(http_method, path, opts) + aws_headers = auth_headers(http_method, unsigned_request.url, unsigned_request.encoded_body) + signed_opts = opts.merge(:header_params => aws_headers.merge(opts[:header_params] || {})) + super(http_method, path, signed_opts) + end + + private + + def retrieve_rdt_access_token + return request_rdt_access_token[:access_token] unless config.get_access_token + stored_token = config.get_access_token.call(@cache_key) + if stored_token.nil? + new_token = request_rdt_access_token + config.save_access_token.call(@cache_key, new_token) if config.save_access_token + return new_token[:access_token] + else + return stored_token + end + end + + def request_rdt_access_token + api_tokens = AmzSpApi::TokensApiModel::TokensApi.new(@wrapped_client) + response = api_tokens.create_restricted_data_token(@create_restricted_data_token_request) + + {access_token: response.restricted_data_token, + expires_in: response.expires_in} + end + + def auth_headers(http_method, url, body) + SpApiClient.signed_request_headers(config, http_method, url, body).merge({ + 'x-amz-access-token' => retrieve_rdt_access_token + }) + end + + end +end diff --git a/lib/sp_api_client.rb b/lib/sp_api_client.rb index 5d3a935ce..6dadaa077 100644 --- a/lib/sp_api_client.rb +++ b/lib/sp_api_client.rb @@ -18,6 +18,21 @@ def call_api(http_method, path, opts = {}) super(http_method, path, signed_opts) end + def self.signed_request_headers(config, http_method, url, body) + request_config = { + service: 'execute-api', + region: config.aws_region + } + if config.credentials_provider + request_config[:credentials_provider] = config.credentials_provider + else + request_config[:access_key_id] = config.aws_access_key_id + request_config[:secret_access_key] = config.aws_secret_access_key + end + signer = Aws::Sigv4::Signer.new(request_config) + signer.sign_request(http_method: http_method.to_s, url: url, body: body).headers + end + private def retrieve_lwa_access_token @@ -58,23 +73,8 @@ def request_lwa_access_token data end - def signed_request_headers(http_method, url, body) - request_config = { - service: 'execute-api', - region: config.aws_region - } - if config.credentials_provider - request_config[:credentials_provider] = config.credentials_provider - else - request_config[:access_key_id] = config.aws_access_key_id - request_config[:secret_access_key] = config.aws_secret_access_key - end - signer = Aws::Sigv4::Signer.new(request_config) - signer.sign_request(http_method: http_method.to_s, url: url, body: body).headers - end - def auth_headers(http_method, url, body) - signed_request_headers(http_method, url, body).merge({ + self.class.signed_request_headers(config, http_method, url, body).merge({ 'x-amz-access-token' => retrieve_lwa_access_token }) end From b5f07f7a45412c34bb59a581801e8b8d5b77a43c Mon Sep 17 00:00:00 2001 From: Eric Jensen Date: Thu, 19 Oct 2023 16:55:09 -0400 Subject: [PATCH 2/3] hypens not underscores --- README.md | 16 ++++++++++++++++ lib/amz_sp_api.rb | 3 ++- lib/restricted_sp_api_client.rb | 7 +++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d48bd9fb2..d70244f10 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,23 @@ require 'fulfillment-outbound-api-model' ## Restricted operations +Configure as per above but also create a new client for each restrictedResources you need, e.g.: + ``` +require 'orders-api-model' + +client = AmzSpApi::RestrictedSpApiClient.new({ + 'restrictedResources' => [ + { + 'method' => 'GET', + 'path' => "/orders/v0/orders", + 'dataElements' => ['buyerInfo', 'shippingAddress'] + } + ] +}) +api_orders = AmzSpApi::OrdersApiModel::OrdersV0Api.new(client) +api_orders.get_orders(marketplace_ids, created_after: 1.day.ago.iso8601) + client = AmzSpApi::RestrictedSpApiClient.new({ 'restrictedResources' => [ { diff --git a/lib/amz_sp_api.rb b/lib/amz_sp_api.rb index d6c41c3c7..4d09dd5b9 100644 --- a/lib/amz_sp_api.rb +++ b/lib/amz_sp_api.rb @@ -1,7 +1,8 @@ require 'amz_sp_api_version' require 'api_error' -require 'sp_api_client' require 'sp_configuration' +require 'sp_api_client' +require 'restricted_sp_api_client' module AmzSpApi class << self diff --git a/lib/restricted_sp_api_client.rb b/lib/restricted_sp_api_client.rb index 1315f9cab..8d99a3d12 100644 --- a/lib/restricted_sp_api_client.rb +++ b/lib/restricted_sp_api_client.rb @@ -1,10 +1,9 @@ -require 'sp_api_client' -require 'tokens_api_model' - require 'api_error' require 'api_client' - require 'configuration' +require 'sp_api_client' + +require 'tokens-api-model' module AmzSpApi class RestrictedSpApiClient < ApiClient From fa386df545a833462da8631db5cbb3245b3a9ad7 Mon Sep 17 00:00:00 2001 From: Eric Jensen Date: Thu, 19 Oct 2023 17:05:10 -0400 Subject: [PATCH 3/3] bump version --- lib/amz_sp_api_version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/amz_sp_api_version.rb b/lib/amz_sp_api_version.rb index 12009128b..942607ea5 100644 --- a/lib/amz_sp_api_version.rb +++ b/lib/amz_sp_api_version.rb @@ -1,3 +1,3 @@ module AmzSpApi - VERSION = '1.0.0' + VERSION = '1.0.1' end