Skip to content

Commit

Permalink
Implement dynamic sandboxes
Browse files Browse the repository at this point in the history
  • Loading branch information
hakanensari committed Sep 19, 2024
1 parent a292282 commit c11e1c4
Show file tree
Hide file tree
Showing 54 changed files with 606 additions and 53 deletions.
21 changes: 19 additions & 2 deletions bin/generate-code
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,23 @@ module Generator
end

shared_parameters = methods.delete("parameters") || []
skipped_methods = ["x-amzn-api-sandbox", "x-amzn-api-sandbox-only"]
sandbox = methods.delete("x-amzn-api-sandbox")
path_has_dynamic_sandbox = sandbox&.key?("dynamic")

# Should never raise
raise if sandbox && !sandbox.key?("dynamic")

sandbox_only = methods.delete("x-amzn-api-sandbox-only")

methods.map do |method, operation|
next if skipped_methods.include?(method)
sandbox = operation.delete("x-amzn-api-sandbox")
operation_has_dynamic_sandbox = sandbox&.key?("dynamic")

# Should never raise
raise if sandbox && !sandbox.key?("dynamic")

code = operation["responses"].keys.find { |code| code.start_with?("2") }
static_sandbox = operation["responses"][code]&.delete("x-amzn-api-sandbox")&.fetch("static")

operation_parameters = (shared_parameters + (operation["parameters"] || [])).uniq { |p| p["name"] }
parameters = operation_parameters.select { |p| p["name"] }
Expand All @@ -95,6 +109,9 @@ module Generator
"parameters" => parameters,
"body_param" => parameters&.find { |p| p["in"] == "body" },
"query_params" => parameters&.select { |p| p["in"] == "query" },
"dynamic_sandbox" => !!(path_has_dynamic_sandbox || operation_has_dynamic_sandbox),
"static_sandbox" => static_sandbox,
"sandbox_only" => sandbox_only,
)
end
end.compact
Expand Down
67 changes: 33 additions & 34 deletions bin/templates/api.rb.erb
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
# frozen_string_literal: true

require "peddler/api"

<% %>
module Peddler
class API
<%= format_text(model["info"]["title"], 4) %>
#
<%= format_text(model["info"]["description"], 4) %>
class <%= api_data[:class_name] %> < API
<% operations.each do |operation| %>
<%= generate_method_docs(operation["description"], operation["parameters"]) %>
# @return [Hash] The API response
def <%= snakecase(operation["operationId"]) %><%= generate_parameters(operation["parameters"], snakecase(operation["operationId"]), 6) %>
path = "<%= operation["path"] %>"
<% if operation["body_param"] && operation["body_param"]["name"] != "body" -%>
body = <%= snakecase(operation["body_param"]["name"]) %>
<% end -%>
<% if operation["query_params"]&.any? -%>
params = {
<%= operation["query_params"].map { |p| %("#{p["name"]}" => #{snakecase(p["name"])}) }.join(",\n ") %>
}.compact
<% end -%>
<% http_method = operation["method"].downcase %>
<% method_args = ["path"] %>
<% method_args << "body:" if operation["body_param"] %>
<% method_args << "params:" if operation["query_params"]&.any? %>
<% if operation["rate_limit"] %>
rate_limit(<%= operation["rate_limit"] %>).<%= http_method %>(<%= method_args.join(", ") %>)
<% else %>
<%= http_method %>(<%= method_args.join(", ") %>)
<% end %>
end
class API
<%= format_text(model["info"]["title"], 4) %>
#
<%= format_text(model["info"]["description"], 4) %>
class <%= api_data[:class_name] %> < API
<% operations.each do |operation| %>
<%= generate_method_docs(operation["description"], operation["parameters"]) %>
# @return [Hash] The API response
def <%= snakecase(operation["operationId"]) %><%= generate_parameters(operation["parameters"], snakecase(operation["operationId"]), 6) %>
<% unless operation["dynamic_sandbox"] -%>
cannot_sandbox!
<% end %>
path = "<%= operation["path"] %>"
<% if operation["body_param"] && operation["body_param"]["name"] != "body" -%>
body = <%= snakecase(operation["body_param"]["name"]) %>
<% end -%>
<% if operation["query_params"]&.any? -%>
params = {
<%= operation["query_params"].map { |p| %("#{p["name"]}" => #{snakecase(p["name"])}) }.join(",\n ") %>
}.compact
<% end -%>
<% http_method = operation["method"].downcase %>
<% method_args = ["path"] %>
<% method_args << "body:" if operation["body_param"] %>
<% method_args << "params:" if operation["query_params"]&.any? %>
<% if operation["rate_limit"] %>
rate_limit(<%= operation["rate_limit"] %>).<%= http_method %>(<%= method_args.join(", ") %>)
<% else %>
<%= http_method %>(<%= method_args.join(", ") %>)
<% end %>
end
<% end %>
end
end
end
end
24 changes: 21 additions & 3 deletions lib/peddler/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
module Peddler
# Wraps an Amazon Selling Partner API (SP-API)
class API
class CannotSandbox < StandardError; end

# @return [Peddler::Region]
attr_reader :region

Expand All @@ -19,19 +21,31 @@ class API
def initialize(aws_region, access_token)
@region = Region.new(aws_region)
@access_token = access_token
@sandbox = false
end

# @return [URI]
def endpoint
@endpoint ||= region.endpoint
URI(sandbox? ? region.sandbox_endpoint : region.endpoint)
end

# @see https://developer-docs.amazon.com/sp-api/docs/the-selling-partner-api-sandbox
# @return [self]
def sandbox
@endpoint = endpoint.sub(/(?:sandbox.)?sellingpartnerapi/, "sandbox.sellingpartnerapi")
@sandbox = true
self
end

# @return [Boolean]
def sandbox?
@sandbox
end

# @raise [CannotSandbox] if in sandbox environment
def cannot_sandbox!
raise CannotSandbox, "cannot run in sandbox" if sandbox?
end

# @see https://developer-docs.amazon.com/sp-api/docs/include-a-user-agent-header-in-all-requests
# @see https://developer-docs.amazon.com/amazon-shipping/docs/connecting-to-the-selling-partner-api#step-3-add-headers-to-the-uri
# @return [HTTP::Client]
Expand Down Expand Up @@ -89,7 +103,11 @@ def rate_limit(rate)
options[:json] = options.delete(:body)
end

response = http.send(method, [endpoint, path].join, **options)
uri = endpoint.tap do |uri|
uri.path = path
end

response = http.send(method, uri, **options)

if response.status.client_error?
error = Error.build(response)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class AmazonWarehousingAndDistribution20240509 < API
# Defaults to `HIDE`, in which case the response does not contain SKU quantities
# @return [Hash] The API response
def get_inbound_shipment(shipment_id, sku_quantities: nil)
cannot_sandbox!

path = "/awd/2024-05-09/inboundShipments/#{shipment_id}"
params = {
"skuQuantities" => sku_quantities,
Expand All @@ -40,6 +42,8 @@ def get_inbound_shipment(shipment_id, sku_quantities: nil)
# @return [Hash] The API response
def list_inbound_shipments(sort_by: nil, sort_order: nil, shipment_status: nil, updated_after: nil,
updated_before: nil, max_results: nil, next_token: nil)
cannot_sandbox!

path = "/awd/2024-05-09/inboundShipments"
params = {
"sortBy" => sort_by,
Expand All @@ -64,6 +68,8 @@ def list_inbound_shipments(sort_by: nil, sort_order: nil, shipment_status: nil,
# @param [Integer] max_results Maximum number of results to return.
# @return [Hash] The API response
def list_inventory(sku: nil, sort_order: nil, details: nil, next_token: nil, max_results: nil)
cannot_sandbox!

path = "/awd/2024-05-09/inventory"
params = {
"sku" => sku,
Expand Down
20 changes: 20 additions & 0 deletions lib/peddler/api/aplus_content_2020_11_01.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class AplusContent20201101 < API
# pages to return. A pageToken value is not usable across different operations.
# @return [Hash] The API response
def search_content_documents(marketplace_id, page_token: nil)
cannot_sandbox!

path = "/aplus/2020-11-01/contentDocuments"
params = {
"marketplaceId" => marketplace_id,
Expand All @@ -38,6 +40,8 @@ def search_content_documents(marketplace_id, page_token: nil)
# @param [Hash] post_content_document_request The content document request details.
# @return [Hash] The API response
def create_content_document(marketplace_id, post_content_document_request)
cannot_sandbox!

path = "/aplus/2020-11-01/contentDocuments"
body = post_content_document_request
params = {
Expand All @@ -56,6 +60,8 @@ def create_content_document(marketplace_id, post_content_document_request)
# @param [Array<String>] included_data_set The set of A+ Content data types to include in the response.
# @return [Hash] The API response
def get_content_document(content_reference_key, marketplace_id, included_data_set)
cannot_sandbox!

path = "/aplus/2020-11-01/contentDocuments/#{content_reference_key}"
params = {
"marketplaceId" => marketplace_id,
Expand All @@ -74,6 +80,8 @@ def get_content_document(content_reference_key, marketplace_id, included_data_se
# @param [Hash] post_content_document_request The content document request details.
# @return [Hash] The API response
def update_content_document(content_reference_key, marketplace_id, post_content_document_request)
cannot_sandbox!

path = "/aplus/2020-11-01/contentDocuments/#{content_reference_key}"
body = post_content_document_request
params = {
Expand Down Expand Up @@ -101,6 +109,8 @@ def update_content_document(content_reference_key, marketplace_id, post_content_
# @return [Hash] The API response
def list_content_document_asin_relations(content_reference_key, marketplace_id, included_data_set: nil,
asin_set: nil, page_token: nil)
cannot_sandbox!

path = "/aplus/2020-11-01/contentDocuments/#{content_reference_key}/asins"
params = {
"marketplaceId" => marketplace_id,
Expand All @@ -124,6 +134,8 @@ def list_content_document_asin_relations(content_reference_key, marketplace_id,
# @return [Hash] The API response
def post_content_document_asin_relations(content_reference_key, marketplace_id,
post_content_document_asin_relations_request)
cannot_sandbox!

path = "/aplus/2020-11-01/contentDocuments/#{content_reference_key}/asins"
body = post_content_document_asin_relations_request
params = {
Expand All @@ -140,6 +152,8 @@ def post_content_document_asin_relations(content_reference_key, marketplace_id,
# @param [Hash] post_content_document_request The content document request details.
# @return [Hash] The API response
def validate_content_document_asin_relations(marketplace_id, post_content_document_request, asin_set: nil)
cannot_sandbox!

path = "/aplus/2020-11-01/contentAsinValidations"
body = post_content_document_request
params = {
Expand All @@ -161,6 +175,8 @@ def validate_content_document_asin_relations(marketplace_id, post_content_docume
# pages to return. A pageToken value is not usable across different operations.
# @return [Hash] The API response
def search_content_publish_records(marketplace_id, asin, page_token: nil)
cannot_sandbox!

path = "/aplus/2020-11-01/contentPublishRecords"
params = {
"marketplaceId" => marketplace_id,
Expand All @@ -179,6 +195,8 @@ def search_content_publish_records(marketplace_id, asin, page_token: nil)
# @param [String] marketplace_id The identifier for the marketplace where the A+ Content is published.
# @return [Hash] The API response
def post_content_document_approval_submission(content_reference_key, marketplace_id)
cannot_sandbox!

path = "/aplus/2020-11-01/contentDocuments/#{content_reference_key}/approvalSubmissions"
params = {
"marketplaceId" => marketplace_id,
Expand All @@ -196,6 +214,8 @@ def post_content_document_approval_submission(content_reference_key, marketplace
# @param [String] marketplace_id The identifier for the marketplace where the A+ Content is published.
# @return [Hash] The API response
def post_content_document_suspend_submission(content_reference_key, marketplace_id)
cannot_sandbox!

path = "/aplus/2020-11-01/contentDocuments/#{content_reference_key}/suspendSubmissions"
params = {
"marketplaceId" => marketplace_id,
Expand Down
2 changes: 2 additions & 0 deletions lib/peddler/api/application_management_2023_11_30.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class ApplicationManagement20231130 < API
# secret](https://developer-docs.amazon.com/sp-api/v0/docs/application-management-api-v2023-11-30-use-case-guide#tutorial-rotate-your-applications-client-secret).
# @return [Hash] The API response
def rotate_application_client_secret
cannot_sandbox!

path = "/applications/2023-11-30/clientSecret"

rate_limit(0.0167).post(path)
Expand Down
8 changes: 6 additions & 2 deletions lib/peddler/api/catalog_items_2020_12_01.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ class CatalogItems20201201 < API
# @param [String] locale Locale for retrieving localized summaries. Defaults to the primary locale of the
# marketplace.
# @return [Hash] The API response
def search_catalog_items(keywords, marketplace_ids, included_data: nil, brand_names: nil,
classification_ids: nil, page_size: nil, page_token: nil, keywords_locale: nil, locale: nil)
def search_catalog_items(keywords, marketplace_ids, included_data: nil, brand_names: nil, classification_ids: nil,
page_size: nil, page_token: nil, keywords_locale: nil, locale: nil)
cannot_sandbox!

path = "/catalog/2020-12-01/items"
params = {
"keywords" => keywords,
Expand Down Expand Up @@ -56,6 +58,8 @@ def search_catalog_items(keywords, marketplace_ids, included_data: nil, brand_na
# marketplace.
# @return [Hash] The API response
def get_catalog_item(asin, marketplace_ids, included_data: nil, locale: nil)
cannot_sandbox!

path = "/catalog/2020-12-01/items/#{asin}"
params = {
"marketplaceIds" => marketplace_ids,
Expand Down
4 changes: 4 additions & 0 deletions lib/peddler/api/catalog_items_2022_04_01.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def search_catalog_items(
marketplace_ids, identifiers: nil, identifiers_type: nil, included_data: nil, locale: nil, seller_id: nil,
keywords: nil, brand_names: nil, classification_ids: nil, page_size: nil, page_token: nil, keywords_locale: nil
)
cannot_sandbox!

path = "/catalog/2022-04-01/items"
params = {
"identifiers" => identifiers,
Expand Down Expand Up @@ -69,6 +71,8 @@ def search_catalog_items(
# marketplace.
# @return [Hash] The API response
def get_catalog_item(asin, marketplace_ids, included_data: nil, locale: nil)
cannot_sandbox!

path = "/catalog/2022-04-01/items/#{asin}"
params = {
"marketplaceIds" => marketplace_ids,
Expand Down
6 changes: 6 additions & 0 deletions lib/peddler/api/catalog_items_v0.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class CatalogItemsV0 < API
# @return [Hash] The API response
def list_catalog_items(marketplace_id, query: nil, query_context_id: nil, seller_sku: nil, upc: nil, ean: nil,
isbn: nil, jan: nil)
cannot_sandbox!

path = "/catalog/v0/items"
params = {
"MarketplaceId" => marketplace_id,
Expand All @@ -60,6 +62,8 @@ def list_catalog_items(marketplace_id, query: nil, query_context_id: nil, seller
# @param [String] asin The Amazon Standard Identification Number (ASIN) of the item.
# @return [Hash] The API response
def get_catalog_item(marketplace_id, asin)
cannot_sandbox!

path = "/catalog/v0/items/#{asin}"
params = {
"MarketplaceId" => marketplace_id,
Expand All @@ -76,6 +80,8 @@ def get_catalog_item(marketplace_id, asin)
# seller's SellerId, which is included with every operation that you submit.
# @return [Hash] The API response
def list_catalog_categories(marketplace_id, asin: nil, seller_sku: nil)
cannot_sandbox!

path = "/catalog/v0/categories"
params = {
"MarketplaceId" => marketplace_id,
Expand Down
Loading

0 comments on commit c11e1c4

Please sign in to comment.