amazon-sp-cli 0.2.2__tar.gz → 0.2.4__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 (31) hide show
  1. {amazon_sp_cli-0.2.2/amazon_sp_cli.egg-info → amazon_sp_cli-0.2.4}/PKG-INFO +1 -1
  2. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/README.md +2 -3
  3. amazon_sp_cli-0.2.4/amazon_sp_cli/__init__.py +2 -0
  4. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/models/a_plus.py +47 -2
  5. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4/amazon_sp_cli.egg-info}/PKG-INFO +1 -1
  6. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/setup.py +8 -1
  7. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/tests/test_a_plus.py +103 -0
  8. amazon_sp_cli-0.2.2/amazon_sp_cli/__init__.py +0 -2
  9. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/LICENSE +0 -0
  10. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/MANIFEST.in +0 -0
  11. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/__main__.py +0 -0
  12. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/auth.py +0 -0
  13. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/cli.py +0 -0
  14. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/client.py +0 -0
  15. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/commands/__init__.py +0 -0
  16. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/commands/a_plus.py +0 -0
  17. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/commands/auth.py +0 -0
  18. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/commands/pricing.py +0 -0
  19. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/main.py +0 -0
  20. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli/models/__init__.py +0 -0
  21. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli.egg-info/SOURCES.txt +0 -0
  22. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli.egg-info/dependency_links.txt +0 -0
  23. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli.egg-info/entry_points.txt +0 -0
  24. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli.egg-info/requires.txt +0 -0
  25. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/amazon_sp_cli.egg-info/top_level.txt +0 -0
  26. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/pyproject.toml +0 -0
  27. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/setup.cfg +0 -0
  28. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/tests/__init__.py +0 -0
  29. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/tests/test_auth.py +0 -0
  30. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/tests/test_client.py +0 -0
  31. {amazon_sp_cli-0.2.2 → amazon_sp_cli-0.2.4}/tests/test_pricing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-sp-cli
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: CLI tool for Amazon Selling Partner API (SP-API) operations
5
5
  Home-page: https://github.com/stellaraether/amazon-sp-cli
6
6
  Author: Lunan Li
@@ -120,10 +120,9 @@ python3 -m amazon_sp_cli get-price PAW2603190101-BLU
120
120
 
121
121
  This repository uses automated releases. To publish a new version:
122
122
 
123
- 1. Open a **standalone PR** that only bumps `version` in `setup.py`.
123
+ 1. Open a **standalone PR** that only bumps `__version__` in `amazon_sp_cli/__init__.py`.
124
124
  2. The PR must not include code, test, or documentation changes — a CI check enforces this.
125
- 3. Once the PR is merged to `main`, the `Auto Release` workflow detects the new version, creates a tag (e.g. `v0.2.1`), and pushes it.
126
- 4. The tag push triggers the `Publish to PyPI` workflow, which runs the full test suite and publishes the package.
125
+ 3. Once the PR is merged to `main`, the `Auto Release` workflow runs the full test suite, creates a tag (e.g. `v0.2.1`), creates a GitHub release, and publishes to PyPI.
127
126
 
128
127
  Do not create tags manually.
129
128
 
@@ -0,0 +1,2 @@
1
+ # Amazon SP-API CLI
2
+ __version__ = "0.2.4"
@@ -18,14 +18,26 @@ class TextComponent:
18
18
  class ImageComponent:
19
19
  """Image component for A+ Content modules."""
20
20
 
21
- def __init__(self, upload_destination_id: str, image_crop: dict = None):
21
+ def __init__(
22
+ self,
23
+ upload_destination_id: str,
24
+ image_crop: dict = None,
25
+ alt_text: str = None,
26
+ image_crop_specification: dict = None,
27
+ ):
22
28
  self.upload_destination_id = upload_destination_id
23
29
  self.image_crop = image_crop
30
+ self.alt_text = alt_text
31
+ self.image_crop_specification = image_crop_specification
24
32
 
25
33
  def to_dict(self) -> dict:
26
34
  result = {"uploadDestinationId": self.upload_destination_id}
27
35
  if self.image_crop:
28
36
  result["imageCrop"] = self.image_crop
37
+ if self.alt_text:
38
+ result["altText"] = self.alt_text
39
+ if self.image_crop_specification:
40
+ result["imageCropSpecification"] = self.image_crop_specification
29
41
  return result
30
42
 
31
43
 
@@ -157,6 +169,19 @@ class StandardImageTextOverlayModule:
157
169
  return result
158
170
 
159
171
 
172
+ class StandardCompanyLogoModule:
173
+ """Standard Company Logo module."""
174
+
175
+ def __init__(self, company_logo=None):
176
+ self.company_logo = company_logo
177
+
178
+ def to_dict(self) -> dict:
179
+ result = {}
180
+ if self.company_logo:
181
+ result["companyLogo"] = self.company_logo.to_dict()
182
+ return result
183
+
184
+
160
185
  class ContentModule:
161
186
  """A+ Content module wrapper."""
162
187
 
@@ -168,6 +193,7 @@ class ContentModule:
168
193
  "STANDARD_COMPARISON_TABLE": "standardComparisonTable",
169
194
  "STANDARD_TEXT": "standardText",
170
195
  "STANDARD_IMAGE_TEXT_OVERLAY": "standardImageTextOverlay",
196
+ "STANDARD_COMPANY_LOGO": "standardCompanyLogo",
171
197
  }
172
198
 
173
199
  def __init__(
@@ -180,6 +206,7 @@ class ContentModule:
180
206
  standard_comparison_table: StandardComparisonTableModule = None,
181
207
  standard_text: StandardTextModule = None,
182
208
  standard_image_text_overlay: StandardImageTextOverlayModule = None,
209
+ standard_company_logo: StandardCompanyLogoModule = None,
183
210
  ):
184
211
  self.module_type = module_type
185
212
  self.standard_image_text = standard_image_text
@@ -189,6 +216,7 @@ class ContentModule:
189
216
  self.standard_comparison_table = standard_comparison_table
190
217
  self.standard_text = standard_text
191
218
  self.standard_image_text_overlay = standard_image_text_overlay
219
+ self.standard_company_logo = standard_company_logo
192
220
 
193
221
  def to_dict(self) -> dict:
194
222
  result = {"contentModuleType": self.module_type}
@@ -208,6 +236,8 @@ class ContentModule:
208
236
  result["standardText"] = self.standard_text.to_dict()
209
237
  elif field_name == "standardImageTextOverlay" and self.standard_image_text_overlay:
210
238
  result["standardImageTextOverlay"] = self.standard_image_text_overlay.to_dict()
239
+ elif field_name == "standardCompanyLogo" and self.standard_company_logo:
240
+ result["standardCompanyLogo"] = self.standard_company_logo.to_dict()
211
241
 
212
242
  return result
213
243
 
@@ -287,7 +317,7 @@ def build_content_from_json(name: str, data: dict) -> APlusContentDocument:
287
317
 
288
318
  def build_module_from_json(data: dict) -> ContentModule:
289
319
  """Build ContentModule from JSON dict."""
290
- module_type = data.get("moduleType", "STANDARD_TEXT")
320
+ module_type = data.get("contentModuleType") or data.get("moduleType", "STANDARD_TEXT")
291
321
 
292
322
  if module_type == "STANDARD_IMAGE_TEXT":
293
323
  return ContentModule(
@@ -350,5 +380,20 @@ def build_module_from_json(data: dict) -> ContentModule:
350
380
  image=ImageComponent(data["imageId"]) if data.get("imageId") else None,
351
381
  ),
352
382
  )
383
+ elif module_type == "STANDARD_COMPANY_LOGO":
384
+ return ContentModule(
385
+ module_type=module_type,
386
+ standard_company_logo=StandardCompanyLogoModule(
387
+ company_logo=(
388
+ ImageComponent(
389
+ data["imageId"],
390
+ alt_text=data.get("altText"),
391
+ image_crop_specification=data.get("imageCropSpecification"),
392
+ )
393
+ if data.get("imageId")
394
+ else None
395
+ ),
396
+ ),
397
+ )
353
398
  else:
354
399
  raise ValueError(f"Unsupported moduleType: {module_type}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-sp-cli
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: CLI tool for Amazon Selling Partner API (SP-API) operations
5
5
  Home-page: https://github.com/stellaraether/amazon-sp-cli
6
6
  Author: Lunan Li
@@ -1,8 +1,15 @@
1
+ import os
2
+ import re
3
+
1
4
  from setuptools import find_packages, setup
2
5
 
6
+ here = os.path.abspath(os.path.dirname(__file__))
7
+ with open(os.path.join(here, "amazon_sp_cli", "__init__.py")) as f:
8
+ version = re.search(r'__version__ = "([^"]+)"', f.read()).group(1)
9
+
3
10
  setup(
4
11
  name="amazon-sp-cli",
5
- version="0.2.2",
12
+ version=version,
6
13
  description="CLI tool for Amazon Selling Partner API (SP-API) operations",
7
14
  author="Lunan Li",
8
15
  author_email="lunan@stellaraether.com",
@@ -10,6 +10,7 @@ from amazon_sp_cli.main import cli
10
10
  from amazon_sp_cli.models.a_plus import (
11
11
  APlusContentDocument,
12
12
  ContentModule,
13
+ StandardCompanyLogoModule,
13
14
  StandardTextModule,
14
15
  TextComponent,
15
16
  build_content_from_json,
@@ -103,6 +104,38 @@ class TestBuildFromJson:
103
104
  with pytest.raises(ValueError, match="Unsupported moduleType"):
104
105
  build_module_from_json({"moduleType": "UNKNOWN"})
105
106
 
107
+ def test_build_module_from_json_uses_content_module_type(self):
108
+ data = {"contentModuleType": "STANDARD_TEXT", "headline": "Hello"}
109
+ mod = build_module_from_json(data)
110
+ assert mod.module_type == "STANDARD_TEXT"
111
+
112
+ def test_build_module_company_logo(self):
113
+ data = {
114
+ "contentModuleType": "STANDARD_COMPANY_LOGO",
115
+ "imageId": "logo-123",
116
+ "altText": "Company Logo",
117
+ "imageCropSpecification": {
118
+ "size": {
119
+ "width": {"value": 600, "units": "pixels"},
120
+ "height": {"value": 180, "units": "pixels"},
121
+ },
122
+ "offset": {
123
+ "x": {"value": 0, "units": "pixels"},
124
+ "y": {"value": 0, "units": "pixels"},
125
+ },
126
+ },
127
+ }
128
+ mod = build_module_from_json(data)
129
+ assert mod.module_type == "STANDARD_COMPANY_LOGO"
130
+ result = mod.to_dict()
131
+ assert result["standardCompanyLogo"]["companyLogo"]["uploadDestinationId"] == "logo-123"
132
+ assert result["standardCompanyLogo"]["companyLogo"]["altText"] == "Company Logo"
133
+ assert result["standardCompanyLogo"]["companyLogo"]["imageCropSpecification"]["size"]["width"]["value"] == 600
134
+
135
+ def test_standard_company_logo_module_to_dict_empty(self):
136
+ mod = StandardCompanyLogoModule()
137
+ assert mod.to_dict() == {}
138
+
106
139
  def test_build_module_image_text(self):
107
140
  data = {
108
141
  "moduleType": "STANDARD_IMAGE_TEXT",
@@ -128,6 +161,76 @@ class TestBuildFromJson:
128
161
  assert result["standardComparisonTable"]["headline"]["value"] == "Compare"
129
162
  assert result["standardComparisonTable"]["comparisonTableRows"][0]["name"] == "Feature"
130
163
 
164
+ def test_integration_full_document_from_json(self):
165
+ data = {
166
+ "modules": [
167
+ {
168
+ "moduleType": "STANDARD_COMPANY_LOGO",
169
+ "imageId": "aplus-media/sc/76a7fb65-607b-4e96-a3fa-a145d1397725.jpg",
170
+ "altText": "Pawified Logo",
171
+ "imageCropSpecification": {
172
+ "size": {
173
+ "width": {"value": 600, "units": "pixels"},
174
+ "height": {"value": 180, "units": "pixels"},
175
+ },
176
+ "offset": {
177
+ "x": {"value": 0, "units": "pixels"},
178
+ "y": {"value": 0, "units": "pixels"},
179
+ },
180
+ },
181
+ },
182
+ {
183
+ "moduleType": "STANDARD_IMAGE_TEXT",
184
+ "headline": "Pure Joy in Every Bite",
185
+ "body": "Treat your feline friend to the finest premium catnip...",
186
+ "imageId": "aplus-media/sc/77967e76-a03c-4514-85e1-371e7f2395d1.jpg",
187
+ },
188
+ {
189
+ "moduleType": "STANDARD_IMAGE_TEXT",
190
+ "headline": "100% Natural & Safe",
191
+ "body": "Made in the USA with all-natural...",
192
+ "imageId": "aplus-media/sc/215948ec-b306-4a0b-8491-f1342dc83f91.jpg",
193
+ },
194
+ {
195
+ "moduleType": "STANDARD_IMAGE_TEXT",
196
+ "headline": "Perfect for Playtime",
197
+ "body": "Each 2-pack includes convenient 5g sachets...",
198
+ "imageId": "aplus-media/sc/2e6493d5-e7a4-4c3a-8e47-a78e5f5b4802.jpg",
199
+ },
200
+ {
201
+ "moduleType": "STANDARD_IMAGE_TEXT",
202
+ "headline": "Premium Quality You Can Trust",
203
+ "body": "Pawified is committed to delivering premium pet products...",
204
+ "imageId": "aplus-media/sc/ef864286-0fec-4472-ad20-8b7dfee4922e.jpg",
205
+ },
206
+ ]
207
+ }
208
+ doc = build_content_from_json("pawified-catnip", data)
209
+ assert doc.name == "pawified-catnip"
210
+ assert len(doc.content_module_list) == 5
211
+ assert doc.content_module_list[0].module_type == "STANDARD_COMPANY_LOGO"
212
+ assert doc.content_module_list[1].module_type == "STANDARD_IMAGE_TEXT"
213
+
214
+ result = doc.to_dict()
215
+ modules = result["contentModuleList"]
216
+ assert len(modules) == 5
217
+
218
+ logo_module = modules[0]["standardCompanyLogo"]
219
+ assert "image" not in logo_module["companyLogo"]
220
+ assert (
221
+ logo_module["companyLogo"]["uploadDestinationId"]
222
+ == "aplus-media/sc/76a7fb65-607b-4e96-a3fa-a145d1397725.jpg"
223
+ )
224
+ assert logo_module["companyLogo"]["altText"] == "Pawified Logo"
225
+ assert logo_module["companyLogo"]["imageCropSpecification"]["size"]["width"]["value"] == 600
226
+
227
+ for i in range(1, 5):
228
+ mod = modules[i]["standardImageText"]
229
+ assert "headline" in mod
230
+ assert "body" in mod
231
+ assert "image" in mod
232
+ assert mod["image"]["uploadDestinationId"].startswith("aplus-media/sc/")
233
+
131
234
 
132
235
  class TestAPlusCLI:
133
236
  @pytest.fixture
@@ -1,2 +0,0 @@
1
- # Amazon SP-API CLI
2
- __version__ = "0.2.2"
File without changes
File without changes
File without changes