python-amazon-ad-api 0.4.3__tar.gz → 0.4.5__tar.gz

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.
Files changed (125) hide show
  1. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/PKG-INFO +18 -18
  2. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/README.md +17 -17
  3. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/dsp/client.py +0 -7
  4. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/reports.py +2 -2
  5. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/keywords_v3.py +4 -4
  6. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/product_ads_v3.py +1 -1
  7. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/auth/access_token_client.py +5 -3
  8. python-amazon-ad-api-0.4.5/ad_api/auth/credentials.py +6 -0
  9. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/base/base_client.py +1 -5
  10. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/base/client.py +39 -62
  11. python-amazon-ad-api-0.4.5/ad_api/base/credential_provider.py +160 -0
  12. python-amazon-ad-api-0.4.5/ad_api/base/exceptions.py +140 -0
  13. python-amazon-ad-api-0.4.5/ad_api/version.py +1 -0
  14. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/python_amazon_ad_api.egg-info/PKG-INFO +18 -18
  15. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/python_amazon_ad_api.egg-info/requires.txt +1 -1
  16. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/setup.cfg +1 -1
  17. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/setup.py +2 -2
  18. python-amazon-ad-api-0.4.3/ad_api/auth/credentials.py +0 -11
  19. python-amazon-ad-api-0.4.3/ad_api/base/credential_provider.py +0 -72
  20. python-amazon-ad-api-0.4.3/ad_api/base/exceptions.py +0 -54
  21. python-amazon-ad-api-0.4.3/ad_api/version.py +0 -1
  22. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/LICENSE +0 -0
  23. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/__init__.py +0 -0
  24. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/__init__.py +0 -0
  25. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/advertising_test_account.py +0 -0
  26. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/attribution.py +0 -0
  27. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/audiences.py +0 -0
  28. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/billing.py +0 -0
  29. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/brand_metrics.py +0 -0
  30. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/creative_assets.py +0 -0
  31. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/dsp/__init__.py +0 -0
  32. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/dsp/access_token_client.py +0 -0
  33. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/dsp/credential_provider.py +0 -0
  34. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/dsp/reports.py +0 -0
  35. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/eligibility.py +0 -0
  36. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/history.py +0 -0
  37. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/insights.py +0 -0
  38. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/invoices.py +0 -0
  39. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/localization.py +0 -0
  40. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/manager_accounts.py +0 -0
  41. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/metadata.py +0 -0
  42. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/portfolios.py +0 -0
  43. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/profiles.py +0 -0
  44. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/recommendations.py +0 -0
  45. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/__init__.py +0 -0
  46. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/ad_groups.py +0 -0
  47. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/ad_groups_v4.py +0 -0
  48. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/ads_v4.py +0 -0
  49. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/bid_recommendations.py +0 -0
  50. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/brands.py +0 -0
  51. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/campaigns.py +0 -0
  52. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/campaigns_v4.py +0 -0
  53. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/forecast.py +0 -0
  54. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/keywords.py +0 -0
  55. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/keywords_recommendations.py +0 -0
  56. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/landing_page_asins.py +0 -0
  57. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/media.py +0 -0
  58. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/moderation.py +0 -0
  59. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/negative_keywords.py +0 -0
  60. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/negative_product_targeting.py +0 -0
  61. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/product_targeting.py +0 -0
  62. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/recommendations.py +0 -0
  63. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/reports.py +0 -0
  64. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/snapshots.py +0 -0
  65. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/stores.py +0 -0
  66. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sb/targeting_recommendations.py +0 -0
  67. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/__init__.py +0 -0
  68. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/ad_groups.py +0 -0
  69. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/bid_recommendations.py +0 -0
  70. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/brand_safety.py +0 -0
  71. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/budget_rules.py +0 -0
  72. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/campaigns.py +0 -0
  73. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/campaigns_budget_usage.py +0 -0
  74. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/creatives.py +0 -0
  75. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/forecast.py +0 -0
  76. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/negative_product_targeting.py +0 -0
  77. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/product_ads.py +0 -0
  78. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/product_targeting.py +0 -0
  79. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/recommendations.py +0 -0
  80. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/reports.py +0 -0
  81. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/snapshots.py +0 -0
  82. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sd/targeting_recommendations.py +0 -0
  83. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/__init__.py +0 -0
  84. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/ad_groups.py +0 -0
  85. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/ad_groups_v3.py +0 -0
  86. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/bid_recommendations.py +0 -0
  87. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/bid_recommendations_v3.py +0 -0
  88. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/budget_initial_recommendation.py +0 -0
  89. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/budget_recommendations.py +0 -0
  90. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/budget_rules.py +0 -0
  91. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/budget_rules_recommendations.py +0 -0
  92. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/campaign_consolidated_recommendations.py +0 -0
  93. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/campaign_negative_keywords.py +0 -0
  94. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/campaign_negative_keywords_v3.py +0 -0
  95. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/campaign_negative_targets.py +0 -0
  96. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/campaigns.py +0 -0
  97. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/campaigns_budget_usage.py +0 -0
  98. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/campaigns_v3.py +0 -0
  99. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/campaings_optimization.py +0 -0
  100. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/keywords.py +0 -0
  101. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/negative_keywords.py +0 -0
  102. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/negative_keywords_v3.py +0 -0
  103. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/negative_product_targeting.py +0 -0
  104. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/negative_product_targeting_v3.py +0 -0
  105. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/product_ads.py +0 -0
  106. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/product_recommendations.py +0 -0
  107. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/product_targeting.py +0 -0
  108. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/product_targeting_v3.py +0 -0
  109. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/ranked_keywords_recommendations.py +0 -0
  110. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/reports.py +0 -0
  111. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/snapshots.py +0 -0
  112. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/sp/suggested_keywords.py +0 -0
  113. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/api/validation_configurations.py +0 -0
  114. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/auth/__init__.py +0 -0
  115. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/auth/access_token_response.py +0 -0
  116. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/auth/exceptions.py +0 -0
  117. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/base/__init__.py +0 -0
  118. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/base/api_response.py +0 -0
  119. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/base/config.py +0 -0
  120. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/base/helpers.py +0 -0
  121. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/base/marketplaces.py +0 -0
  122. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/ad_api/base/utils.py +0 -0
  123. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/python_amazon_ad_api.egg-info/SOURCES.txt +0 -0
  124. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/python_amazon_ad_api.egg-info/dependency_links.txt +0 -0
  125. {python-amazon-ad-api-0.4.3 → python-amazon-ad-api-0.4.5}/python_amazon_ad_api.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-amazon-ad-api
3
- Version: 0.4.3
3
+ Version: 0.4.5
4
4
  Summary: Python wrapper for the Amazon Advertising API
5
5
  Home-page: https://github.com/denisneuf/python-amazon-ad-api
6
6
  Author: Daniel Alvaro
@@ -299,21 +299,21 @@ There is a new version 3 of Sponsored Product API, please check the [migration g
299
299
 
300
300
  ### [Modules Available Sponsored Brands 3.0](https://python-amazon-ad-api.readthedocs.io/en/latest/sb_v3.html)
301
301
 
302
- * Campaigns(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/campaigns.html)
303
- * Ad Groups(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/ad_groups.html)
304
- * Keywords(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/keywords.html)
305
- * Negative Keywords(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/negative_keywords.html)
306
- * Product Targeting(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/product_targeting.html)
307
- * Negative Product Targeting(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/negative_product_targeting.html)
308
- * Targeting Recommendations(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/targeting_recommendations.html)
309
- * Bid Recommendations(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/bid_recommendations.html)
310
- * Stores(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/stores.html)
311
- * Landing Page Asins(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/landing_page_asins.html)
312
- * Media(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/media.html)
313
- * Brands(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/brands.html)
314
- * Moderation(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/moderation.html)
315
- * Reports(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/reports.html)
316
- * Snapshots
302
+ * [Campaigns](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/campaigns.html)
303
+ * [Ad Groups](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/ad_groups.html)
304
+ * [Keywords](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/keywords.html)
305
+ * [Negative Keywords](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/negative_keywords.html)
306
+ * [Product Targeting](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/product_targeting.html)
307
+ * [Negative Product Targeting](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/negative_product_targeting.html)
308
+ * [Targeting Recommendations](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/targeting_recommendations.html)
309
+ * [Bid Recommendations](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/bid_recommendations.html)
310
+ * [Stores](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/stores.html)
311
+ * [Landing Page Asins](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/landing_page_asins.html)
312
+ * [Media](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/media.html)
313
+ * [Brands](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/brands.html)
314
+ * [Moderation](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/moderation.html)
315
+ * [Reports](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/reports.html)
316
+ * [Snapshots](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/snapshots.html)
317
317
 
318
318
  ### [Modules Available Sponsored Brands 4.0](https://python-amazon-ad-api.readthedocs.io/en/latest/sb_v4.html)
319
319
 
@@ -338,9 +338,9 @@ There is a new version 3 of Sponsored Product API, please check the [migration g
338
338
  * [Forecasts](https://python-amazon-ad-api.readthedocs.io/en/latest/sd/forecast.html)
339
339
  * [Recommendations](https://python-amazon-ad-api.readthedocs.io/en/latest/sd/recommendations.html)
340
340
 
341
- ### Modules Available DSP
341
+ ### [Modules Available DSP](https://python-amazon-ad-api.readthedocs.io/en/latest/dsp.html)
342
342
 
343
- * Reports
343
+ * [Reports](https://python-amazon-ad-api.readthedocs.io/en/latest/dsp/reports.html)
344
344
 
345
345
  ### Simple Example Usage Campaigns with Credentials
346
346
 
@@ -284,21 +284,21 @@ There is a new version 3 of Sponsored Product API, please check the [migration g
284
284
 
285
285
  ### [Modules Available Sponsored Brands 3.0](https://python-amazon-ad-api.readthedocs.io/en/latest/sb_v3.html)
286
286
 
287
- * Campaigns(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/campaigns.html)
288
- * Ad Groups(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/ad_groups.html)
289
- * Keywords(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/keywords.html)
290
- * Negative Keywords(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/negative_keywords.html)
291
- * Product Targeting(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/product_targeting.html)
292
- * Negative Product Targeting(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/negative_product_targeting.html)
293
- * Targeting Recommendations(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/targeting_recommendations.html)
294
- * Bid Recommendations(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/bid_recommendations.html)
295
- * Stores(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/stores.html)
296
- * Landing Page Asins(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/landing_page_asins.html)
297
- * Media(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/media.html)
298
- * Brands(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/brands.html)
299
- * Moderation(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/moderation.html)
300
- * Reports(https://python-amazon-ad-api.readthedocs.io/en/latest/sb/reports.html)
301
- * Snapshots
287
+ * [Campaigns](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/campaigns.html)
288
+ * [Ad Groups](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/ad_groups.html)
289
+ * [Keywords](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/keywords.html)
290
+ * [Negative Keywords](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/negative_keywords.html)
291
+ * [Product Targeting](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/product_targeting.html)
292
+ * [Negative Product Targeting](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/negative_product_targeting.html)
293
+ * [Targeting Recommendations](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/targeting_recommendations.html)
294
+ * [Bid Recommendations](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/bid_recommendations.html)
295
+ * [Stores](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/stores.html)
296
+ * [Landing Page Asins](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/landing_page_asins.html)
297
+ * [Media](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/media.html)
298
+ * [Brands](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/brands.html)
299
+ * [Moderation](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/moderation.html)
300
+ * [Reports](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/reports.html)
301
+ * [Snapshots](https://python-amazon-ad-api.readthedocs.io/en/latest/sb/snapshots.html)
302
302
 
303
303
  ### [Modules Available Sponsored Brands 4.0](https://python-amazon-ad-api.readthedocs.io/en/latest/sb_v4.html)
304
304
 
@@ -323,9 +323,9 @@ There is a new version 3 of Sponsored Product API, please check the [migration g
323
323
  * [Forecasts](https://python-amazon-ad-api.readthedocs.io/en/latest/sd/forecast.html)
324
324
  * [Recommendations](https://python-amazon-ad-api.readthedocs.io/en/latest/sd/recommendations.html)
325
325
 
326
- ### Modules Available DSP
326
+ ### [Modules Available DSP](https://python-amazon-ad-api.readthedocs.io/en/latest/dsp.html)
327
327
 
328
- * Reports
328
+ * [Reports](https://python-amazon-ad-api.readthedocs.io/en/latest/dsp/reports.html)
329
329
 
330
330
  ### Simple Example Usage Campaigns with Credentials
331
331
 
@@ -1,14 +1,7 @@
1
1
  from ad_api.base import Client
2
- from ad_api.auth.credentials import BaseCredentials
3
- from ad_api.api.dsp.credential_provider import DspCredentialProvider
4
- from ad_api.api.dsp.access_token_client import DspAccessTokenClient
5
2
 
6
3
 
7
4
  class DspClient(Client):
8
- access_token_client_class = DspAccessTokenClient
9
- credentials_class = BaseCredentials
10
- credential_provider_class = DspCredentialProvider
11
-
12
5
  @property
13
6
  def headers(self):
14
7
  return {
@@ -1,4 +1,4 @@
1
- from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse
1
+ from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils
2
2
 
3
3
 
4
4
  class Reports(Client):
@@ -35,7 +35,7 @@ class Reports(Client):
35
35
  ApiResponse
36
36
 
37
37
  """
38
- return self._request(kwargs.pop('path'), data=kwargs.pop('body'), params=kwargs)
38
+ return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs)
39
39
 
40
40
  @sp_endpoint('/reporting/reports/{}', method='GET')
41
41
  def get_report(self, reportId, **kwargs) -> ApiResponse:
@@ -13,7 +13,7 @@ class KeywordsV3(Client):
13
13
  Returns
14
14
  ApiResponse
15
15
  """
16
- json_version = 'application/vnd.spKeyword.v3+json' + str(version) + "+json"
16
+ json_version = 'application/vnd.spKeyword.v' + str(version) + "+json"
17
17
  headers = {
18
18
  "Accept": json_version,
19
19
  "Content-Type": json_version
@@ -39,7 +39,7 @@ class KeywordsV3(Client):
39
39
  Returns
40
40
  ApiResponse
41
41
  """
42
- json_version = 'application/vnd.spKeyword.v3+json' + str(version) + "+json"
42
+ json_version = 'application/vnd.spKeyword.v' + str(version) + "+json"
43
43
 
44
44
  headers = {
45
45
  "Accept": json_version,
@@ -67,7 +67,7 @@ class KeywordsV3(Client):
67
67
  ApiResponse
68
68
  """
69
69
 
70
- json_version = 'application/vnd.spKeyword.v3+json' + str(version) + "+json"
70
+ json_version = 'application/vnd.spKeyword.v' + str(version) + "+json"
71
71
  headers = {
72
72
  "Accept": json_version,
73
73
  "Content-Type": json_version
@@ -93,7 +93,7 @@ class KeywordsV3(Client):
93
93
  ApiResponse
94
94
  """
95
95
 
96
- json_version = 'application/vnd.spKeyword.v3+json' + str(version) + "+json"
96
+ json_version = 'application/vnd.spKeyword.v' + str(version) + "+json"
97
97
  headers = {
98
98
  "Accept": json_version,
99
99
  "Content-Type": json_version
@@ -25,7 +25,7 @@ class ProductAdsV3(Client):
25
25
  ApiResponse
26
26
  """
27
27
 
28
- json_version = 'application/vnd.spCampaign.v' + str(version) + "+json"
28
+ json_version = 'application/vnd.spproductAd.v' + str(version) + "+json"
29
29
 
30
30
  headers = {
31
31
  "Accept": json_version,
@@ -7,6 +7,8 @@ from ad_api.base import BaseClient
7
7
  from .credentials import Credentials
8
8
  from .access_token_response import AccessTokenResponse
9
9
  from .exceptions import AuthorizationError
10
+ # from .credential_provider import CredentialProvider
11
+
10
12
  import os
11
13
 
12
14
  cache = TTLCache(maxsize=int(os.environ.get('AD_API_AUTH_CACHE_SIZE', 10)), ttl=3200)
@@ -20,9 +22,9 @@ class AccessTokenClient(BaseClient):
20
22
  grant_type = 'refresh_token'
21
23
  path = '/auth/o2/token'
22
24
 
23
- def __init__(self, account='default', credentials=None, credentials_class=Credentials, proxies=None, verify=True, timeout=None):
24
- super().__init__(account, credentials)
25
- self.cred = credentials_class(self.credentials)
25
+ def __init__(self, credentials=None, proxies=None, verify=True, timeout=None):
26
+
27
+ self.cred = Credentials(credentials)
26
28
  self.timeout = timeout
27
29
  self.proxies = proxies
28
30
  self.verify = verify
@@ -0,0 +1,6 @@
1
+ class Credentials:
2
+ def __init__(self, credentials):
3
+ self.client_id = credentials['client_id']
4
+ self.client_secret = credentials['client_secret']
5
+ self.refresh_token = credentials['refresh_token']
6
+ self.profile_id = credentials['profile_id']
@@ -1,18 +1,14 @@
1
1
  import ad_api.version as vd
2
- from ad_api.base.credential_provider import CredentialProvider
3
-
4
2
 
5
3
  class BaseClient:
6
4
  scheme = 'https://'
7
5
  method = 'GET'
8
6
  content_type = 'application/x-www-form-urlencoded;charset=UTF-8'
9
7
  user_agent = 'python-ad-api'
10
- credential_provider_class = CredentialProvider
11
8
 
12
- def __init__(self, account='default', credentials=None):
9
+ def __init__(self):
13
10
  try:
14
11
  version = vd.__version__
15
12
  self.user_agent += f'-{version}'
16
13
  except:
17
14
  pass
18
- self.credentials = self.credential_provider_class(account, credentials).credentials
@@ -1,4 +1,5 @@
1
1
  import json
2
+ from json import JSONDecodeError
2
3
  import logging
3
4
  from cachetools import TTLCache
4
5
  from requests import request
@@ -6,7 +7,7 @@ from ad_api.auth.credentials import Credentials
6
7
  from ad_api.auth import AccessTokenClient, AccessTokenResponse
7
8
  from .api_response import ApiResponse
8
9
  from .base_client import BaseClient
9
- from .exceptions import get_exception_for_content
10
+ from .exceptions import get_exception_for_code, get_exception_for_content
10
11
  from .marketplaces import Marketplaces
11
12
  import os
12
13
  import requests
@@ -15,15 +16,13 @@ import gzip
15
16
  from zipfile import ZipFile
16
17
  import zipfile
17
18
  from urllib.parse import urlparse, quote
19
+ from ad_api.base.credential_provider import CredentialProvider
18
20
 
19
21
  log = logging.getLogger(__name__)
20
22
  role_cache = TTLCache(maxsize=int(os.environ.get('AD_API_AUTH_CACHE_SIZE', 10)), ttl=3200)
21
23
 
22
24
 
23
25
  class Client(BaseClient):
24
- access_token_client_class = AccessTokenClient
25
- credentials_class = Credentials
26
- grantless_scope = ''
27
26
 
28
27
  def __init__(
29
28
  self,
@@ -37,17 +36,15 @@ class Client(BaseClient):
37
36
  debug=False
38
37
  ):
39
38
 
40
- super().__init__(account, credentials)
41
- self.endpoint = marketplace.endpoint
42
- self.debug = debug
43
- self._auth = self.access_token_client_class(
44
- account=account,
45
- credentials=credentials,
46
- credentials_class=self.credentials_class,
39
+ self.credentials = CredentialProvider(account, credentials).credentials
40
+ self._auth = AccessTokenClient(
41
+ credentials=self.credentials,
47
42
  proxies=proxies,
48
43
  verify=verify,
49
44
  timeout=timeout,
50
45
  )
46
+ self.endpoint = marketplace.endpoint
47
+ self.debug = debug
51
48
  self.timeout = timeout
52
49
  self.proxies = proxies
53
50
  self.verify = verify
@@ -56,9 +53,9 @@ class Client(BaseClient):
56
53
  def headers(self):
57
54
  return {
58
55
  'User-Agent': self.user_agent,
59
- 'Amazon-Advertising-API-ClientId': self.credentials.client_id,
56
+ 'Amazon-Advertising-API-ClientId': self.credentials['client_id'],
60
57
  'Authorization': 'Bearer %s' % self.auth.access_token,
61
- 'Amazon-Advertising-API-Scope': self.credentials.profile_id,
58
+ 'Amazon-Advertising-API-Scope': self.credentials['profile_id'],
62
59
  'Content-Type': 'application/json'
63
60
  }
64
61
 
@@ -263,6 +260,7 @@ class Client(BaseClient):
263
260
  if self.debug:
264
261
  logging.info(headers or self.headers)
265
262
 
263
+
266
264
  if params:
267
265
  str_query = ""
268
266
  for key, value in params.items():
@@ -275,69 +273,48 @@ class Client(BaseClient):
275
273
  if data is not None:
276
274
  logging.info(data)
277
275
 
278
- return self._check_response(res)
276
+ logging.info(vars(res))
279
277
 
280
- # @staticmethod
281
- def _check_response(self, res) -> ApiResponse:
278
+ return self._check_response(res)
282
279
 
283
- if self.debug:
284
- logging.info(vars(res))
280
+ @staticmethod
281
+ def _check_response(res) -> ApiResponse:
285
282
 
286
283
  content = vars(res).get('_content')
287
- str_content = content.decode('utf8')
284
+ headers = vars(res).get('headers')
285
+ status_code = vars(res).get('status_code')
288
286
 
289
- if type(str_content) is str and str_content[0:50] == '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">' and vars(res).get('_content_consumed') is True:
287
+ if 200 <= res.status_code < 300:
290
288
 
291
- dictionary = {"status_code": vars(res).get('status_code'), "msg": "Unauthorized"}
292
- exception = get_exception_for_content(dictionary)
293
- raise exception(dictionary)
289
+ try:
290
+ js = res.json() or {}
291
+ except JSONDecodeError:
292
+ js = {}
294
293
 
295
- if type(str_content) is str and str_content[0:15] == 'Invalid request' and vars(res).get('_content_consumed') is True:
296
- dictionary = {"status_code": vars(res).get('status_code'), "msg": str_content}
297
- exception = get_exception_for_content(dictionary)
298
- raise exception(dictionary)
294
+ try:
295
+ error = js.get('error', None) # Dict.get(key, default=None)
296
+ except AttributeError:
297
+ error = None
299
298
 
300
- data = json.loads(str_content)
299
+ if error:
300
+ exception = get_exception_for_content(error[0].get('code'))
301
+ raise exception(error[0].get('code'), error[0], headers)
301
302
 
302
- if type(data) is dict and data.get('code') == 'UNAUTHORIZED':
303
- exception = get_exception_for_content(data)
304
- raise exception(data)
303
+ next_token = vars(res).get('_next')
305
304
 
306
- if type(data) is dict and data.get('message') == 'Too Many Requests' and vars(res).get('_content_consumed') is True:
307
- exception = get_exception_for_content(data)
308
- raise exception(data)
305
+ return ApiResponse(js, next_token, headers=headers)
309
306
 
310
- if type(data) is dict and data.get('code') == 'NOT_FOUND' and vars(res).get('_content_consumed') is True:
311
- dictionary = {"status_code": vars(res).get('status_code'), "code": data.get('code'), "details": data.get('details'), "requestId": data.get('requestId')}
312
- exception = get_exception_for_content(data)
313
- raise exception(dictionary)
307
+ else:
314
308
 
315
- if type(data) is dict and data.get('code') == 'SERVER_IS_BUSY' and vars(res).get('_content_consumed') is True:
316
- dictionary = {"status_code": vars(res).get('status_code'), "code": data.get('code'), "details": data.get('details'), "requestId": data.get('requestId')}
317
- exception = get_exception_for_content(data)
318
- raise exception(dictionary)
309
+ exception = get_exception_for_code(res.status_code)
319
310
 
320
- if type(data) is dict and data.get('message') == 'Unauthorized' and vars(res).get('_content_consumed') is True:
321
- dictionary = {"status_code": vars(res).get('status_code'), "message": "Unauthorized"}
322
- exception = get_exception_for_content(dictionary)
323
- raise exception(dictionary)
311
+ try:
312
+ js = res.json()
313
+ except JSONDecodeError:
314
+ js = res.content
324
315
 
325
- if type(data) is dict and data.get('details') == 'Invalid authorization inputs' and vars(res).get('_content_consumed') is True:
326
- dictionary = {"status_code": vars(res).get('status_code'), "message": "Invalid authorization inputs"}
327
- exception = get_exception_for_content(dictionary)
328
- raise exception(dictionary)
329
316
 
330
- if type(data) is dict and data.get('message') == 'Missing Authentication Token' and vars(res).get('_content_consumed') is True:
331
- dictionary = {"status_code": vars(res).get('status_code'), "message": "Missing Authentication Token"}
332
- exception = get_exception_for_content(dictionary)
333
- raise exception(dictionary)
317
+ raise exception(status_code, js, headers)
318
+ exit(res.status_code)
334
319
 
335
- if type(data) is dict and data.get('details') == 'Cannot consume content type' and vars(res).get('_content_consumed') is True:
336
- dictionary = {"status_code": vars(res).get('status_code'), "message": data.get('details')}
337
- exception = get_exception_for_content(dictionary)
338
- raise exception(dictionary)
339
320
 
340
- headers = vars(res).get('headers')
341
- status_code = vars(res).get('status_code')
342
- next_token = vars(res).get('_next')
343
- return ApiResponse(data, next_token, headers=headers)
@@ -0,0 +1,160 @@
1
+ import abc
2
+ import functools
3
+ import json
4
+ import os
5
+ import pprint
6
+
7
+ from typing import Dict, Iterable, Optional, Type
8
+
9
+ import confuse
10
+ import logging
11
+
12
+ logger = logging.getLogger(__name__)
13
+ required_credentials = [
14
+ 'refresh_token',
15
+ 'client_id',
16
+ 'client_secret',
17
+ 'profile_id'
18
+ ]
19
+
20
+
21
+ class MissingCredentials(Exception):
22
+ """
23
+ Credentials are missing, see the error output to find possible causes
24
+ """
25
+ pass
26
+
27
+
28
+ class BaseCredentialProvider(abc.ABC):
29
+
30
+ def __init__(self, credentials: dict or str, *args, **kwargs):
31
+ self.credentials = credentials
32
+
33
+ @abc.abstractmethod
34
+ def load_credentials(self):
35
+ pass
36
+
37
+
38
+ def _get_env(self, key):
39
+ return os.environ.get(f'{key}',
40
+ os.environ.get(key))
41
+
42
+ def check_credentials(self):
43
+ try:
44
+ self.errors = [c for c in required_credentials if
45
+ c not in self.credentials.keys() or not self.credentials[c]]
46
+ except (AttributeError, TypeError):
47
+ raise MissingCredentials(f'Credentials are missing: {", ".join(required_credentials)}')
48
+ if not len(self.errors):
49
+ return self.credentials
50
+ raise MissingCredentials(f'Credentials are missing: {", ".join(self.errors)}')
51
+
52
+
53
+
54
+ class FromEnvCredentialProvider(BaseCredentialProvider):
55
+
56
+ def __init__(self, *args, **kwargs):
57
+ pass
58
+
59
+ def load_credentials(self):
60
+ account_data = dict(
61
+ refresh_token=self._get_env('AD_API_REFRESH_TOKEN'),
62
+ client_id=self._get_env('AD_API_CLIENT_ID'),
63
+ client_secret=self._get_env('AD_API_CLIENT_SECRET'),
64
+ profile_id=self._get_env('AD_API_PROFILE_ID'),
65
+ )
66
+ self.credentials = account_data
67
+ self.check_credentials()
68
+ return self.credentials
69
+
70
+ def _get_env(self, key):
71
+ return os.environ.get(f'{key}',
72
+ os.environ.get(key))
73
+
74
+ class FromCodeCredentialProvider(BaseCredentialProvider):
75
+
76
+ def __init__(self, credentials: dict, *args, **kwargs):
77
+ super().__init__(credentials)
78
+
79
+ def load_credentials(self):
80
+ self.check_credentials()
81
+ return self.credentials
82
+
83
+
84
+ class FromConfigFileCredentialProvider(BaseCredentialProvider):
85
+
86
+ file = 'credentials.yml' # Will moved to default confuse config.yaml
87
+
88
+ def __init__(self, account: str, *args, **kwargs):
89
+ self.account = account
90
+
91
+ def load_credentials(self):
92
+ try:
93
+ config = confuse.Configuration('python-ad-api')
94
+ config_filename = os.path.join(config.config_dir(), self.file)
95
+ config.set_file(config_filename)
96
+ account_data = config[self.account].get()
97
+ super().__init__(account_data)
98
+ self.check_credentials()
99
+ return (account_data)
100
+
101
+
102
+ except confuse.exceptions.NotFoundError:
103
+ raise MissingCredentials(f'The account {self.account} was not setup in your configuration file.')
104
+ except confuse.exceptions.ConfigReadError:
105
+ raise MissingCredentials(
106
+ f'Neither environment variables nor a config file were found. '
107
+ f'Please set the correct variables, or use a config file (credentials.yml). '
108
+ f'See https://confuse.readthedocs.io/en/latest/usage.html#search-paths for search paths.'
109
+ )
110
+
111
+
112
+ class CredentialProvider():
113
+
114
+ def load_credentials(self):
115
+ pass
116
+
117
+ def _get_env(self, key):
118
+ return os.environ.get(f'{key}',
119
+ os.environ.get(key))
120
+
121
+ def __init__(
122
+ self,
123
+ account: str = 'default',
124
+ credentials: Optional[Dict[str, str]] = None,
125
+ ):
126
+
127
+ client_id_env = self._get_env('AD_API_CLIENT_ID'),
128
+ client_secret_env = self._get_env('AD_API_CLIENT_SECRET')
129
+
130
+ if client_id_env is not None and client_secret_env is not None:
131
+
132
+ try:
133
+ self.credentials = FromEnvCredentialProvider().load_credentials()
134
+ except MissingCredentials:
135
+ logging.error("MissingCredentials")
136
+ logging.error(MissingCredentials)
137
+
138
+ elif isinstance(credentials, dict):
139
+ try:
140
+ self.credentials = FromCodeCredentialProvider(credentials).load_credentials()
141
+
142
+ except MissingCredentials:
143
+ logging.error("MissingCredentials")
144
+ logging.error(MissingCredentials)
145
+
146
+ else:
147
+
148
+ try:
149
+ self.credentials = FromConfigFileCredentialProvider(account).load_credentials()
150
+
151
+ except MissingCredentials:
152
+ logging.error("MissingCredentials")
153
+ logging.error(MissingCredentials)
154
+
155
+ class Config:
156
+ def __init__(self, **kwargs):
157
+ self.refresh_token = kwargs.get('refresh_token')
158
+ self.client_id = kwargs.get('client_id')
159
+ self.client_secret = kwargs.get('client_secret')
160
+ self.profile_id = kwargs.get('profile_id')