amazon-ads 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d4e68acc1d736bb5c7f6a59b10163bce7a2622065a291fcebfe3aafd7e028fa2
4
+ data.tar.gz: 3f33dd9eeae73cfb943bf548efbb842393aea4b42d6b8f87fb79e8faad4f9d49
5
+ SHA512:
6
+ metadata.gz: ef4d4bf550a0c1cbee2ca11b469906d576e35a1db35978e52896572ad202a0905d32087810e1ee750b358ee9409bb073b1f863485847588bfd1f2cfce7f901bf
7
+ data.tar.gz: 6755c1f89af12711d7dd3970e1ac37e9e3ac1872c944f497030bfa74a84e5cbbd825af087719cf68104e6be019302f7639138c0dcbe6006b2294dcf490448080
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Hakan Ensari
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Sponsor
2
+
3
+ TODO: Delete this and the text below, and describe your gem
4
+
5
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/sponsor`. To experiment with that code, run `bin/console` for an interactive prompt.
6
+
7
+ ## Installation
8
+
9
+ TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
10
+
11
+ Install the gem and add to the application's Gemfile by executing:
12
+
13
+ ```bash
14
+ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
15
+ ```
16
+
17
+ If bundler is not being used to manage dependencies, install the gem by executing:
18
+
19
+ ```bash
20
+ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sponsor.
36
+
37
+ ## License
38
+
39
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ require "http"
6
+
7
+ module AmazonAds
8
+ # Base class for Amazon Ads API clients.
9
+ # Handles authentication, response parsing, error mapping, and retry logic.
10
+ class API
11
+ ENDPOINTS = { #: Hash[Symbol, String]
12
+ na: "https://advertising-api.amazon.com",
13
+ eu: "https://advertising-api-eu.amazon.com",
14
+ fe: "https://advertising-api-fe.amazon.com",
15
+ }.freeze
16
+
17
+ MAX_RETRIES = 3 #: Integer
18
+ BASE_RETRY_DELAY = 1 #: Integer
19
+
20
+ attr_reader :region #: Symbol
21
+ attr_reader :profile_id #: String?
22
+
23
+ #: (region: Symbol, ?access_token: String?, ?lwa: LWA?, ?profile_id: String?) -> void
24
+ def initialize(region:, access_token: nil, lwa: nil, profile_id: nil)
25
+ @region = region
26
+ @access_token = access_token
27
+ @lwa = lwa
28
+ @profile_id = profile_id
29
+
30
+ unless @access_token || @lwa
31
+ raise ArgumentError, "Either access_token or lwa must be provided"
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # Returns the current access token, refreshing via LWA if needed.
38
+ #: () -> String
39
+ def access_token
40
+ @lwa ? @lwa.access_token : @access_token or raise AmazonAds::AuthenticationError, "No access token"
41
+ end
42
+
43
+ #: () -> HTTP::Client
44
+ def http
45
+ HTTP
46
+ .headers(default_headers)
47
+ .use(:auto_inflate)
48
+ end
49
+
50
+ #: () -> Hash[String, String]
51
+ def default_headers
52
+ headers = {
53
+ "Authorization" => "Bearer #{access_token}",
54
+ "Amazon-Advertising-API-ClientId" => client_id,
55
+ "Content-Type" => "application/json",
56
+ "Accept" => "application/json",
57
+ }
58
+ headers["Amazon-Advertising-API-Scope"] = profile_id if profile_id
59
+
60
+ headers
61
+ end
62
+
63
+ #: () -> String
64
+ def client_id
65
+ ENV.fetch("AMAZON_ADS_CLIENT_ID")
66
+ end
67
+
68
+ #: () -> String
69
+ def endpoint
70
+ ENDPOINTS.fetch(region) do
71
+ raise ArgumentError, "Unknown region: #{region}"
72
+ end
73
+ end
74
+
75
+ # Handles HTTP response, raising appropriate errors or parsing success responses.
76
+ #: (HTTP::Response) -> untyped
77
+ def handle_response(response)
78
+ case response.status.code
79
+ when 200..299
80
+ return if response.body.to_s.empty?
81
+
82
+ response.parse
83
+ when 400
84
+ raise AmazonAds::BadRequestError.new("Bad request", response: response)
85
+ when 401
86
+ raise AmazonAds::AuthenticationError.new("Unauthorized", response: response)
87
+ when 404
88
+ raise AmazonAds::NotFoundError.new("Not found", response: response)
89
+ when 429
90
+ retry_after = response.headers["Retry-After"]&.to_i
91
+ raise AmazonAds::RateLimitError.new("Rate limited", response: response, retry_after: retry_after)
92
+ when 500..599
93
+ raise AmazonAds::ServerError.new("Server error", response: response)
94
+ else
95
+ raise AmazonAds::Error.new("Request failed with status #{response.status.code}", response: response)
96
+ end
97
+ end
98
+
99
+ # Executes request with retry logic for rate limiting.
100
+ #: () { () -> HTTP::Response } -> untyped
101
+ def with_retry(&block)
102
+ retries = 0
103
+
104
+ loop do
105
+ response = yield
106
+ return handle_response(response)
107
+ rescue AmazonAds::RateLimitError => e
108
+ retries += 1
109
+ raise if retries > MAX_RETRIES
110
+
111
+ delay = e.retry_after || (BASE_RETRY_DELAY * (2**(retries - 1)))
112
+ sleep(delay)
113
+ end
114
+ end
115
+
116
+ #: (String, ?params: Hash[String, untyped]) -> untyped
117
+ def get(path, params: {})
118
+ with_retry { http.get("#{endpoint}#{path}", params: params) }
119
+ end
120
+
121
+ #: (String, ?body: Hash[String, untyped]) -> untyped
122
+ def post(path, body: {})
123
+ with_retry { http.post("#{endpoint}#{path}", json: body) }
124
+ end
125
+
126
+ #: (String, ?body: Hash[String, untyped]) -> untyped
127
+ def put(path, body: {})
128
+ with_retry { http.put("#{endpoint}#{path}", json: body) }
129
+ end
130
+
131
+ #: (String, ?body: Hash[String, untyped]) -> untyped
132
+ def patch(path, body: {})
133
+ with_retry { http.patch("#{endpoint}#{path}", json: body) }
134
+ end
135
+
136
+ #: (String) -> untyped
137
+ def delete(path)
138
+ with_retry { http.delete("#{endpoint}#{path}") }
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module AmazonAds
6
+ # Amazon Ads API - Profiles
7
+ #
8
+ # @see https://advertising.amazon.com/API/docs/en-us/reference/profiles
9
+ class Profiles < API
10
+ # Gets a list of profiles.
11
+ #: (?api_program: String?, ?access_level: String?, ?profile_type_filter: String?, ?valid_payment_method_filter: String?) -> untyped
12
+ def list_profiles(api_program: nil, access_level: nil, profile_type_filter: nil, valid_payment_method_filter: nil)
13
+ get("/v2/profiles", params: { "apiProgram" => api_program, "accessLevel" => access_level, "profileTypeFilter" => profile_type_filter, "validPaymentMethodFilter" => valid_payment_method_filter }.compact)
14
+ end
15
+
16
+ # Update the daily budget for one or more profiles.
17
+ #: (?body: Hash[String, untyped]) -> untyped
18
+ def update_profiles(body: {})
19
+ put("/v2/profiles", body: body)
20
+ end
21
+
22
+ # Gets a profile specified by identifier.
23
+ #: (Integer) -> untyped
24
+ def get_profile_by_id(profile_id)
25
+ get("/v2/profiles/#{profile_id}")
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module AmazonAds
6
+ # Amazon Ads API - Sponsored Products
7
+ #
8
+ # @see https://advertising.amazon.com/API/docs/en-us/sponsored-products
9
+ class SponsoredProducts < API
10
+ # Gets a bid recommendation for an ad group. [PLANNED DEPRECATION 3/27/2024]
11
+ #: (Numeric) -> untyped
12
+ def get_ad_group_bid_recommendations(ad_group_id)
13
+ get("/v2/sp/adGroups/#{ad_group_id}/bidRecommendations")
14
+ end
15
+
16
+ # Gets a bid recommendation for a keyword. [PLANNED DEPRECATION 3/27/2024]
17
+ #: (Numeric) -> untyped
18
+ def get_keyword_bid_recommendations(keyword_id)
19
+ get("/v2/sp/keywords/#{keyword_id}/bidRecommendations")
20
+ end
21
+
22
+ # Gets bid recommendations for keywords. [PLANNED DEPRECATION 3/27/2024]
23
+ #: (?body: Hash[String, untyped]) -> untyped
24
+ def create_keyword_bid_recommendations(body: {})
25
+ post("/v2/sp/keywords/bidRecommendations", body: body)
26
+ end
27
+
28
+ # Gets suggested keywords for the specified ad group.
29
+ #: (Numeric, ?max_num_suggestions: Integer?, ?ad_state_filter: String?) -> untyped
30
+ def get_ad_group_suggested_keywords(ad_group_id, max_num_suggestions: nil, ad_state_filter: nil)
31
+ get("/v2/sp/adGroups/#{ad_group_id}/suggested/keywords", params: { "maxNumSuggestions" => max_num_suggestions, "adStateFilter" => ad_state_filter }.compact)
32
+ end
33
+
34
+ # Gets suggested keywords with extended data for the specified ad group.
35
+ #: (Numeric, ?max_num_suggestions: Integer?, ?suggest_bids: String?, ?ad_state_filter: String?) -> untyped
36
+ def get_ad_group_suggested_keywords_ex(ad_group_id, max_num_suggestions: nil, suggest_bids: nil, ad_state_filter: nil)
37
+ get("/v2/sp/adGroups/#{ad_group_id}/suggested/keywords/extended", params: { "maxNumSuggestions" => max_num_suggestions, "suggestBids" => suggest_bids, "adStateFilter" => ad_state_filter }.compact)
38
+ end
39
+
40
+ # Gets suggested keywords for the specified ASIN.
41
+ #: (String, ?max_num_suggestions: Integer?) -> untyped
42
+ def get_asin_suggested_keywords(asin_value, max_num_suggestions: nil)
43
+ get("/v2/sp/asins/#{asin_value}/suggested/keywords", params: { "maxNumSuggestions" => max_num_suggestions }.compact)
44
+ end
45
+
46
+ # Gets suggested keyword for a specified list of ASINs.
47
+ #: (?body: Hash[String, untyped]) -> untyped
48
+ def bulk_get_asin_suggested_keywords(body: {})
49
+ post("/v2/sp/asins/suggested/keywords", body: body)
50
+ end
51
+
52
+ # Gets a list of bid recommendations for keyword, product, or auto targeting expressions.
53
+ #: (?body: Hash[String, untyped]) -> untyped
54
+ def get_bid_recommendations(body: {})
55
+ post("/v2/sp/targets/bidRecommendations", body: body)
56
+ end
57
+
58
+ # Request a snapshot
59
+ #: (String, ?body: Hash[String, untyped]) -> untyped
60
+ def request_snapshot(record_type, body: {})
61
+ post("/v2/sp/#{record_type}/snapshot", body: body)
62
+ end
63
+
64
+ # Get the status of a requested snapshot
65
+ #: (Numeric) -> untyped
66
+ def get_snapshot_status(snapshot_id)
67
+ get("/v2/sp/snapshots/#{snapshot_id}")
68
+ end
69
+
70
+ # Download requested snapshot
71
+ #: (Numeric) -> untyped
72
+ def download_snapshot(snapshot_id)
73
+ get("/v2/sp/snapshots/#{snapshot_id}/download")
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module AmazonAds
6
+ # High-level client for Amazon Ads API.
7
+ # Provides convenient access to all API endpoints with automatic authentication.
8
+ class Client
9
+ attr_reader :lwa #: LWA
10
+ attr_reader :region #: Symbol
11
+ attr_reader :profile_id #: String?
12
+
13
+ #: (?region: Symbol, ?profile_id: String?, ?lwa: LWA?) -> void
14
+ def initialize(region: AmazonAds.configuration.region, profile_id: AmazonAds.configuration.profile_id, lwa: nil)
15
+ @lwa = lwa || AmazonAds.configuration.lwa
16
+ @region = region
17
+ @profile_id = profile_id
18
+ end
19
+
20
+ # Access Profiles API
21
+ #: () -> Profiles
22
+ def profiles
23
+ @profiles ||= Profiles.new(region: region, lwa: lwa, profile_id: profile_id)
24
+ end
25
+
26
+ # Access Sponsored Products API
27
+ #: () -> SponsoredProducts
28
+ def sponsored_products
29
+ @sponsored_products ||= SponsoredProducts.new(region: region, lwa: lwa, profile_id: profile_id)
30
+ end
31
+
32
+ # Creates a new client with a different profile ID
33
+ #: (String) -> Client
34
+ def with_profile(profile_id)
35
+ Client.new(region: region, profile_id: profile_id, lwa: lwa)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module AmazonAds
6
+ # Configuration for the AmazonAds library.
7
+ # Centralizes settings for authentication and API access.
8
+ class Configuration
9
+ attr_accessor :client_id #: String?
10
+ attr_accessor :client_secret #: String?
11
+ attr_accessor :refresh_token #: String?
12
+ attr_accessor :region #: Symbol
13
+ attr_accessor :profile_id #: String?
14
+
15
+ #: () -> void
16
+ def initialize
17
+ @client_id = ENV["AMAZON_ADS_CLIENT_ID"]
18
+ @client_secret = ENV["AMAZON_ADS_CLIENT_SECRET"]
19
+ @refresh_token = ENV["AMAZON_ADS_REFRESH_TOKEN"]
20
+ @region = :na
21
+ @profile_id = nil
22
+ end
23
+
24
+ # Builds an LWA instance from the configuration.
25
+ #: () -> LWA
26
+ def lwa
27
+ LWA.new(
28
+ client_id: client_id || raise(ArgumentError, "client_id is required"),
29
+ client_secret: client_secret || raise(ArgumentError, "client_secret is required"),
30
+ refresh_token: refresh_token || raise(ArgumentError, "refresh_token is required"),
31
+ )
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module AmazonAds
6
+ # Base error class for AmazonAds API errors
7
+ class Error < StandardError
8
+ attr_reader :response #: HTTP::Response?
9
+
10
+ #: (?String?, ?response: HTTP::Response?) -> void
11
+ def initialize(message = nil, response: nil)
12
+ @response = response
13
+ super(message)
14
+ end
15
+ end
16
+
17
+ # Raised when authentication fails (401 Unauthorized)
18
+ class AuthenticationError < Error
19
+ end
20
+
21
+ # Raised when rate limited (429 Too Many Requests)
22
+ class RateLimitError < Error
23
+ attr_reader :retry_after #: Integer?
24
+
25
+ #: (?String?, ?response: HTTP::Response?, ?retry_after: Integer?) -> void
26
+ def initialize(message = nil, response: nil, retry_after: nil)
27
+ @retry_after = retry_after
28
+ super(message, response: response)
29
+ end
30
+ end
31
+
32
+ # Raised when resource not found (404 Not Found)
33
+ class NotFoundError < Error
34
+ end
35
+
36
+ # Raised when request is invalid (400 Bad Request)
37
+ class BadRequestError < Error
38
+ end
39
+
40
+ # Raised when server error occurs (5xx)
41
+ class ServerError < Error
42
+ end
43
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ require "http"
6
+
7
+ module AmazonAds
8
+ # Requests Login with Amazon (LWA) access tokens for the Amazon Ads API.
9
+ # Handles token caching and automatic refresh when tokens expire.
10
+ #
11
+ # @see https://advertising.amazon.com/API/docs/en-us/info/api-overview
12
+ class LWA
13
+ URL = "https://api.amazon.com/auth/o2/token" #: String
14
+ TOKEN_EXPIRY_BUFFER = 60 #: Integer
15
+
16
+ attr_reader :client_id #: String
17
+ attr_reader :client_secret #: String
18
+ attr_reader :refresh_token #: String
19
+
20
+ #: (?client_id: String, ?client_secret: String, ?refresh_token: String) -> void
21
+ def initialize(
22
+ client_id: ENV.fetch("AMAZON_ADS_CLIENT_ID"),
23
+ client_secret: ENV.fetch("AMAZON_ADS_CLIENT_SECRET"),
24
+ refresh_token: ENV.fetch("AMAZON_ADS_REFRESH_TOKEN")
25
+ )
26
+ @client_id = client_id
27
+ @client_secret = client_secret
28
+ @refresh_token = refresh_token
29
+ @access_token = nil #: String?
30
+ @expires_at = nil #: Time?
31
+ @mutex = Mutex.new
32
+ end
33
+
34
+ # Returns a valid access token, refreshing if necessary.
35
+ #: () -> String
36
+ def access_token
37
+ @mutex.synchronize do
38
+ refresh! if token_expired?
39
+ @access_token or raise AmazonAds::AuthenticationError, "Failed to obtain access token"
40
+ end
41
+ end
42
+
43
+ # Forces a token refresh regardless of expiry.
44
+ #: () -> String
45
+ def refresh!
46
+ response = HTTP.post(URL, form: params)
47
+
48
+ unless response.status.success?
49
+ body = begin
50
+ response.parse
51
+ rescue StandardError
52
+ {}
53
+ end
54
+ message = body["error_description"] || body["error"] || "Authentication failed"
55
+ raise AmazonAds::AuthenticationError.new(message, response: response)
56
+ end
57
+
58
+ data = response.parse
59
+ @access_token = data.fetch("access_token")
60
+ expires_in = data.fetch("expires_in", 3600)
61
+ @expires_at = Time.now + expires_in - TOKEN_EXPIRY_BUFFER
62
+
63
+ @access_token
64
+ end
65
+
66
+ # Returns true if the token is expired or not yet fetched.
67
+ #: () -> bool
68
+ def token_expired?
69
+ @access_token.nil? || @expires_at.nil? || Time.now >= @expires_at
70
+ end
71
+
72
+ private
73
+
74
+ #: () -> Hash[String, String]
75
+ def params
76
+ {
77
+ "grant_type" => "refresh_token",
78
+ "client_id" => client_id,
79
+ "client_secret" => client_secret,
80
+ "refresh_token" => refresh_token,
81
+ }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AmazonAds
4
+ VERSION = "0.1.0"
5
+ end
data/lib/amazon_ads.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ require "zeitwerk"
6
+
7
+ loader = Zeitwerk::Loader.for_gem
8
+ loader.inflector.inflect("api" => "API", "lwa" => "LWA")
9
+ loader.collapse("#{__dir__}/amazon_ads/apis")
10
+ loader.ignore("#{__dir__}/generator")
11
+ loader.ignore("#{__dir__}/amazon_ads/errors.rb")
12
+ loader.setup
13
+
14
+ require_relative "amazon_ads/errors"
15
+
16
+ # Amazon Ads API client for Ruby
17
+ module AmazonAds
18
+ class << self
19
+ # Returns the global configuration object.
20
+ #: () -> Configuration
21
+ def configuration
22
+ @configuration ||= Configuration.new
23
+ end
24
+
25
+ # Configures the AmazonAds library.
26
+ #: () { (Configuration) -> void } -> void
27
+ def configure
28
+ yield(configuration)
29
+ end
30
+
31
+ # Resets the configuration to defaults.
32
+ #: () -> void
33
+ def reset_configuration!
34
+ @configuration = Configuration.new
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,61 @@
1
+ # Generated from lib/amazon_ads/api.rb with RBS::Inline
2
+
3
+ module AmazonAds
4
+ # Base class for Amazon Ads API clients.
5
+ # Handles authentication, response parsing, error mapping, and retry logic.
6
+ class API
7
+ ENDPOINTS: untyped
8
+
9
+ MAX_RETRIES: Integer
10
+
11
+ BASE_RETRY_DELAY: Integer
12
+
13
+ attr_reader region: Symbol
14
+
15
+ attr_reader profile_id: String?
16
+
17
+ # : (region: Symbol, ?access_token: String?, ?lwa: LWA?, ?profile_id: String?) -> void
18
+ def initialize: (region: Symbol, ?access_token: String?, ?lwa: LWA?, ?profile_id: String?) -> void
19
+
20
+ private
21
+
22
+ # Returns the current access token, refreshing via LWA if needed.
23
+ # : () -> String
24
+ def access_token: () -> String
25
+
26
+ # : () -> HTTP::Client
27
+ def http: () -> HTTP::Client
28
+
29
+ # : () -> Hash[String, String]
30
+ def default_headers: () -> Hash[String, String]
31
+
32
+ # : () -> String
33
+ def client_id: () -> String
34
+
35
+ # : () -> String
36
+ def endpoint: () -> String
37
+
38
+ # Handles HTTP response, raising appropriate errors or parsing success responses.
39
+ # : (HTTP::Response) -> untyped
40
+ def handle_response: (HTTP::Response) -> untyped
41
+
42
+ # Executes request with retry logic for rate limiting.
43
+ # : () { () -> HTTP::Response } -> untyped
44
+ def with_retry: () { () -> HTTP::Response } -> untyped
45
+
46
+ # : (String, ?params: Hash[String, untyped]) -> untyped
47
+ def get: (String, ?params: Hash[String, untyped]) -> untyped
48
+
49
+ # : (String, ?body: Hash[String, untyped]) -> untyped
50
+ def post: (String, ?body: Hash[String, untyped]) -> untyped
51
+
52
+ # : (String, ?body: Hash[String, untyped]) -> untyped
53
+ def put: (String, ?body: Hash[String, untyped]) -> untyped
54
+
55
+ # : (String, ?body: Hash[String, untyped]) -> untyped
56
+ def patch: (String, ?body: Hash[String, untyped]) -> untyped
57
+
58
+ # : (String) -> untyped
59
+ def delete: (String) -> untyped
60
+ end
61
+ end
@@ -0,0 +1,20 @@
1
+ # Generated from lib/amazon_ads/apis/profiles.rb with RBS::Inline
2
+
3
+ module AmazonAds
4
+ # Amazon Ads API - Profiles
5
+ #
6
+ # @see https://advertising.amazon.com/API/docs/en-us/reference/profiles
7
+ class Profiles < API
8
+ # Gets a list of profiles.
9
+ # : (?api_program: String?, ?access_level: String?, ?profile_type_filter: String?, ?valid_payment_method_filter: String?) -> untyped
10
+ def list_profiles: (?api_program: String?, ?access_level: String?, ?profile_type_filter: String?, ?valid_payment_method_filter: String?) -> untyped
11
+
12
+ # Update the daily budget for one or more profiles.
13
+ # : (?body: Hash[String, untyped]) -> untyped
14
+ def update_profiles: (?body: Hash[String, untyped]) -> untyped
15
+
16
+ # Gets a profile specified by identifier.
17
+ # : (Integer) -> untyped
18
+ def get_profile_by_id: (Integer) -> untyped
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ # Generated from lib/amazon_ads/apis/sponsored_products.rb with RBS::Inline
2
+
3
+ module AmazonAds
4
+ # Amazon Ads API - Sponsored Products
5
+ #
6
+ # @see https://advertising.amazon.com/API/docs/en-us/sponsored-products
7
+ class SponsoredProducts < API
8
+ # Gets a bid recommendation for an ad group. [PLANNED DEPRECATION 3/27/2024]
9
+ # : (Numeric) -> untyped
10
+ def get_ad_group_bid_recommendations: (Numeric) -> untyped
11
+
12
+ # Gets a bid recommendation for a keyword. [PLANNED DEPRECATION 3/27/2024]
13
+ # : (Numeric) -> untyped
14
+ def get_keyword_bid_recommendations: (Numeric) -> untyped
15
+
16
+ # Gets bid recommendations for keywords. [PLANNED DEPRECATION 3/27/2024]
17
+ # : (?body: Hash[String, untyped]) -> untyped
18
+ def create_keyword_bid_recommendations: (?body: Hash[String, untyped]) -> untyped
19
+
20
+ # Gets suggested keywords for the specified ad group.
21
+ # : (Numeric, ?max_num_suggestions: Integer?, ?ad_state_filter: String?) -> untyped
22
+ def get_ad_group_suggested_keywords: (Numeric, ?max_num_suggestions: Integer?, ?ad_state_filter: String?) -> untyped
23
+
24
+ # Gets suggested keywords with extended data for the specified ad group.
25
+ # : (Numeric, ?max_num_suggestions: Integer?, ?suggest_bids: String?, ?ad_state_filter: String?) -> untyped
26
+ def get_ad_group_suggested_keywords_ex: (Numeric, ?max_num_suggestions: Integer?, ?suggest_bids: String?, ?ad_state_filter: String?) -> untyped
27
+
28
+ # Gets suggested keywords for the specified ASIN.
29
+ # : (String, ?max_num_suggestions: Integer?) -> untyped
30
+ def get_asin_suggested_keywords: (String, ?max_num_suggestions: Integer?) -> untyped
31
+
32
+ # Gets suggested keyword for a specified list of ASINs.
33
+ # : (?body: Hash[String, untyped]) -> untyped
34
+ def bulk_get_asin_suggested_keywords: (?body: Hash[String, untyped]) -> untyped
35
+
36
+ # Gets a list of bid recommendations for keyword, product, or auto targeting expressions.
37
+ # : (?body: Hash[String, untyped]) -> untyped
38
+ def get_bid_recommendations: (?body: Hash[String, untyped]) -> untyped
39
+
40
+ # Request a snapshot
41
+ # : (String, ?body: Hash[String, untyped]) -> untyped
42
+ def request_snapshot: (String, ?body: Hash[String, untyped]) -> untyped
43
+
44
+ # Get the status of a requested snapshot
45
+ # : (Numeric) -> untyped
46
+ def get_snapshot_status: (Numeric) -> untyped
47
+
48
+ # Download requested snapshot
49
+ # : (Numeric) -> untyped
50
+ def download_snapshot: (Numeric) -> untyped
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ # Generated from lib/amazon_ads/client.rb with RBS::Inline
2
+
3
+ module AmazonAds
4
+ # High-level client for Amazon Ads API.
5
+ # Provides convenient access to all API endpoints with automatic authentication.
6
+ class Client
7
+ attr_reader lwa: LWA
8
+
9
+ attr_reader region: Symbol
10
+
11
+ attr_reader profile_id: String?
12
+
13
+ # : (?region: Symbol, ?profile_id: String?, ?lwa: LWA?) -> void
14
+ def initialize: (?region: Symbol, ?profile_id: String?, ?lwa: LWA?) -> void
15
+
16
+ # Access Profiles API
17
+ # : () -> Profiles
18
+ def profiles: () -> Profiles
19
+
20
+ # Access Sponsored Products API
21
+ # : () -> SponsoredProducts
22
+ def sponsored_products: () -> SponsoredProducts
23
+
24
+ # Creates a new client with a different profile ID
25
+ # : (String) -> Client
26
+ def with_profile: (String) -> Client
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ # Generated from lib/amazon_ads/configuration.rb with RBS::Inline
2
+
3
+ module AmazonAds
4
+ # Configuration for the AmazonAds library.
5
+ # Centralizes settings for authentication and API access.
6
+ class Configuration
7
+ attr_accessor client_id: String?
8
+
9
+ attr_accessor client_secret: String?
10
+
11
+ attr_accessor refresh_token: String?
12
+
13
+ attr_accessor region: Symbol
14
+
15
+ attr_accessor profile_id: String?
16
+
17
+ # : () -> void
18
+ def initialize: () -> void
19
+
20
+ # Builds an LWA instance from the configuration.
21
+ # : () -> LWA
22
+ def lwa: () -> LWA
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ # Generated from lib/amazon_ads/errors.rb with RBS::Inline
2
+
3
+ module AmazonAds
4
+ # Base error class for AmazonAds API errors
5
+ class Error < StandardError
6
+ attr_reader response: HTTP::Response?
7
+
8
+ # : (?String?, ?response: HTTP::Response?) -> void
9
+ def initialize: (?String?, ?response: HTTP::Response?) -> void
10
+ end
11
+
12
+ # Raised when authentication fails (401 Unauthorized)
13
+ class AuthenticationError < Error
14
+ end
15
+
16
+ # Raised when rate limited (429 Too Many Requests)
17
+ class RateLimitError < Error
18
+ attr_reader retry_after: Integer?
19
+
20
+ # : (?String?, ?response: HTTP::Response?, ?retry_after: Integer?) -> void
21
+ def initialize: (?String?, ?response: HTTP::Response?, ?retry_after: Integer?) -> void
22
+ end
23
+
24
+ # Raised when resource not found (404 Not Found)
25
+ class NotFoundError < Error
26
+ end
27
+
28
+ # Raised when request is invalid (400 Bad Request)
29
+ class BadRequestError < Error
30
+ end
31
+
32
+ # Raised when server error occurs (5xx)
33
+ class ServerError < Error
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ # Generated from lib/amazon_ads/lwa.rb with RBS::Inline
2
+
3
+ module AmazonAds
4
+ # Requests Login with Amazon (LWA) access tokens for the Amazon Ads API.
5
+ # Handles token caching and automatic refresh when tokens expire.
6
+ #
7
+ # @see https://advertising.amazon.com/API/docs/en-us/info/api-overview
8
+ class LWA
9
+ URL: String
10
+
11
+ TOKEN_EXPIRY_BUFFER: Integer
12
+
13
+ attr_reader client_id: String
14
+
15
+ attr_reader client_secret: String
16
+
17
+ attr_reader refresh_token: String
18
+
19
+ # : (?client_id: String, ?client_secret: String, ?refresh_token: String) -> void
20
+ def initialize: (?client_id: String, ?client_secret: String, ?refresh_token: String) -> void
21
+
22
+ # Returns a valid access token, refreshing if necessary.
23
+ # : () -> String
24
+ def access_token: () -> String
25
+
26
+ # Forces a token refresh regardless of expiry.
27
+ # : () -> String
28
+ def refresh!: () -> String
29
+
30
+ # Returns true if the token is expired or not yet fetched.
31
+ # : () -> bool
32
+ def token_expired?: () -> bool
33
+
34
+ private
35
+
36
+ # : () -> Hash[String, String]
37
+ def params: () -> Hash[String, String]
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ # Generated from lib/amazon_ads.rb with RBS::Inline
2
+
3
+ # Amazon Ads API client for Ruby
4
+ module AmazonAds
5
+ # Returns the global configuration object.
6
+ # : () -> Configuration
7
+ def self.configuration: () -> Configuration
8
+
9
+ # Configures the AmazonAds library.
10
+ # : () { (Configuration) -> void } -> void
11
+ def self.configure: () { (Configuration) -> void } -> void
12
+
13
+ # Resets the configuration to defaults.
14
+ # : () -> void
15
+ def self.reset_configuration!: () -> void
16
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: amazon-ads
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hakan Ensari
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: http
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '5.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '5.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: zeitwerk
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.6'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.6'
40
+ email:
41
+ - hakanensari@gmail.com
42
+ executables: []
43
+ extensions: []
44
+ extra_rdoc_files: []
45
+ files:
46
+ - LICENSE.txt
47
+ - README.md
48
+ - lib/amazon_ads.rb
49
+ - lib/amazon_ads/api.rb
50
+ - lib/amazon_ads/apis/profiles.rb
51
+ - lib/amazon_ads/apis/sponsored_products.rb
52
+ - lib/amazon_ads/client.rb
53
+ - lib/amazon_ads/configuration.rb
54
+ - lib/amazon_ads/errors.rb
55
+ - lib/amazon_ads/lwa.rb
56
+ - lib/amazon_ads/version.rb
57
+ - sig/generated/amazon_ads.rbs
58
+ - sig/generated/amazon_ads/api.rbs
59
+ - sig/generated/amazon_ads/apis/profiles.rbs
60
+ - sig/generated/amazon_ads/apis/sponsored_products.rbs
61
+ - sig/generated/amazon_ads/client.rbs
62
+ - sig/generated/amazon_ads/configuration.rbs
63
+ - sig/generated/amazon_ads/errors.rbs
64
+ - sig/generated/amazon_ads/lwa.rbs
65
+ homepage: https://github.com/lineofflight/amazon-ads-ruby
66
+ licenses:
67
+ - MIT
68
+ metadata:
69
+ homepage_uri: https://github.com/lineofflight/amazon-ads-ruby
70
+ source_code_uri: https://github.com/lineofflight/amazon-ads-ruby
71
+ changelog_uri: https://github.com/lineofflight/amazon-ads-ruby/blob/main/CHANGELOG.md
72
+ rubygems_mfa_required: 'true'
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 3.2.0
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 4.0.3
88
+ specification_version: 4
89
+ summary: Amazon Ads API client for Ruby
90
+ test_files: []