amazon-sp-cli 0.2.8__tar.gz → 0.3.0__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 (36) hide show
  1. {amazon_sp_cli-0.2.8/amazon_sp_cli.egg-info → amazon_sp_cli-0.3.0}/PKG-INFO +1 -1
  2. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/__init__.py +1 -1
  3. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/cli.py +2 -0
  4. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/client.py +16 -0
  5. amazon_sp_cli-0.3.0/amazon_sp_cli/commands/inventory.py +31 -0
  6. amazon_sp_cli-0.3.0/amazon_sp_cli/commands/update.py +34 -0
  7. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/main.py +4 -0
  8. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0/amazon_sp_cli.egg-info}/PKG-INFO +1 -1
  9. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli.egg-info/SOURCES.txt +5 -1
  10. amazon_sp_cli-0.3.0/tests/test_inventory.py +118 -0
  11. amazon_sp_cli-0.3.0/tests/test_update.py +55 -0
  12. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/LICENSE +0 -0
  13. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/MANIFEST.in +0 -0
  14. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/README.md +0 -0
  15. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/__main__.py +0 -0
  16. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/auth.py +0 -0
  17. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/commands/__init__.py +0 -0
  18. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/commands/a_plus.py +0 -0
  19. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/commands/auth.py +0 -0
  20. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/commands/listings.py +0 -0
  21. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/commands/pricing.py +0 -0
  22. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/models/__init__.py +0 -0
  23. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli/models/a_plus.py +0 -0
  24. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli.egg-info/dependency_links.txt +0 -0
  25. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli.egg-info/entry_points.txt +0 -0
  26. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli.egg-info/requires.txt +0 -0
  27. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/amazon_sp_cli.egg-info/top_level.txt +0 -0
  28. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/pyproject.toml +0 -0
  29. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/setup.cfg +0 -0
  30. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/setup.py +0 -0
  31. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/tests/__init__.py +0 -0
  32. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/tests/test_a_plus.py +0 -0
  33. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/tests/test_auth.py +0 -0
  34. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/tests/test_client.py +0 -0
  35. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/tests/test_listings.py +0 -0
  36. {amazon_sp_cli-0.2.8 → amazon_sp_cli-0.3.0}/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.8
3
+ Version: 0.3.0
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,3 +1,3 @@
1
1
  # Amazon SP-API CLI tool
2
2
 
3
- __version__ = "0.2.8"
3
+ __version__ = "0.3.0"
@@ -8,6 +8,7 @@ from pathlib import Path
8
8
 
9
9
  import click
10
10
 
11
+ from . import __version__
11
12
  from .auth import SPAPIAuth
12
13
  from .client import SPAPIClient
13
14
 
@@ -82,6 +83,7 @@ def handle_errors(f):
82
83
  return wrapper
83
84
 
84
85
 
86
+ @click.version_option(version=__version__, prog_name="amz-sp")
85
87
  @click.group()
86
88
  @click.option("--credentials", "-c", help="Path to credentials YAML file")
87
89
  @click.pass_context
@@ -140,6 +140,22 @@ class SPAPIClient:
140
140
 
141
141
  return self.request("PUT", path, data)
142
142
 
143
+ def get_fba_inventory(self, seller_skus: str = None, next_token: str = None, details: bool = True) -> dict:
144
+ """Get FBA inventory summaries."""
145
+ path = "/fba/inventory/v1/getInventorySummaries"
146
+ params = {
147
+ "details": "true" if details else "false",
148
+ "granularityType": "Marketplace",
149
+ "granularityId": self.marketplace_id,
150
+ "marketplaceIds": self.marketplace_id,
151
+ }
152
+ if seller_skus:
153
+ params["sellerSkus"] = seller_skus
154
+ if next_token:
155
+ params["nextToken"] = next_token
156
+ path += "?" + urlencode(params)
157
+ return self.request("GET", path)
158
+
143
159
  def get_catalog_item(self, asin: str) -> dict:
144
160
  """Get catalog item details."""
145
161
  path = f"/catalog/2022-04-01/items/{asin}"
@@ -0,0 +1,31 @@
1
+ """Inventory commands."""
2
+
3
+ import json
4
+
5
+ import click
6
+
7
+ from ..cli import handle_errors
8
+
9
+
10
+ def register_inventory_commands(cli_group, ensure_auth_client):
11
+ """Register inventory CLI commands."""
12
+
13
+ @cli_group.command("list-fba-inventory")
14
+ @click.option("--sku", help="Filter by specific SKU (comma-separated for multiple)")
15
+ @click.option("--next-token", help="Pagination token from a previous response")
16
+ @click.pass_context
17
+ @handle_errors
18
+ def list_fba_inventory(ctx, sku, next_token):
19
+ """List FBA inventory summaries."""
20
+ _, client = ensure_auth_client(ctx)
21
+ response = client.get_fba_inventory(seller_skus=sku, next_token=next_token)
22
+
23
+ payload = response.get("payload", {})
24
+ summaries = payload.get("inventorySummaries", [])
25
+ result = {"inventory": summaries}
26
+
27
+ pagination = payload.get("pagination", {})
28
+ if pagination.get("nextToken"):
29
+ result["nextToken"] = pagination["nextToken"]
30
+
31
+ click.echo(json.dumps(result, indent=2))
@@ -0,0 +1,34 @@
1
+ """Self-update command."""
2
+
3
+ import subprocess
4
+ import sys
5
+
6
+ import click
7
+
8
+ from ..cli import handle_errors
9
+
10
+ PACKAGE_NAME = "amazon-sp-cli"
11
+
12
+
13
+ def register_update_commands(cli_group):
14
+ """Register update CLI commands."""
15
+
16
+ @cli_group.command()
17
+ @click.option("--dry-run", is_flag=True, help="Show the command without running it")
18
+ @handle_errors
19
+ def update(dry_run):
20
+ """Update to the latest version from PyPI."""
21
+ cmd = [sys.executable, "-m", "pip", "install", "--upgrade", PACKAGE_NAME]
22
+
23
+ if dry_run:
24
+ click.echo("Would run: " + " ".join(cmd))
25
+ return
26
+
27
+ click.echo("Updating amazon-sp-cli...")
28
+ result = subprocess.run(cmd, capture_output=False)
29
+
30
+ if result.returncode != 0:
31
+ click.echo("Update failed.", err=True)
32
+ raise click.Abort()
33
+
34
+ click.echo("Update complete. Run 'amz-sp --version' to verify.")
@@ -3,10 +3,14 @@
3
3
  from .cli import _ensure_auth_client, cli
4
4
  from .commands.a_plus import register_a_plus_commands
5
5
  from .commands.auth import register_auth_commands
6
+ from .commands.inventory import register_inventory_commands
6
7
  from .commands.listings import register_listings_commands
7
8
  from .commands.pricing import register_pricing_commands
9
+ from .commands.update import register_update_commands
8
10
 
9
11
  register_auth_commands(cli)
10
12
  register_listings_commands(cli, _ensure_auth_client)
11
13
  register_pricing_commands(cli, _ensure_auth_client)
12
14
  register_a_plus_commands(cli, _ensure_auth_client)
15
+ register_inventory_commands(cli, _ensure_auth_client)
16
+ register_update_commands(cli)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-sp-cli
3
- Version: 0.2.8
3
+ Version: 0.3.0
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
@@ -18,13 +18,17 @@ amazon_sp_cli.egg-info/top_level.txt
18
18
  amazon_sp_cli/commands/__init__.py
19
19
  amazon_sp_cli/commands/a_plus.py
20
20
  amazon_sp_cli/commands/auth.py
21
+ amazon_sp_cli/commands/inventory.py
21
22
  amazon_sp_cli/commands/listings.py
22
23
  amazon_sp_cli/commands/pricing.py
24
+ amazon_sp_cli/commands/update.py
23
25
  amazon_sp_cli/models/__init__.py
24
26
  amazon_sp_cli/models/a_plus.py
25
27
  tests/__init__.py
26
28
  tests/test_a_plus.py
27
29
  tests/test_auth.py
28
30
  tests/test_client.py
31
+ tests/test_inventory.py
29
32
  tests/test_listings.py
30
- tests/test_pricing.py
33
+ tests/test_pricing.py
34
+ tests/test_update.py
@@ -0,0 +1,118 @@
1
+ """Tests for inventory commands."""
2
+
3
+ import json
4
+ from unittest.mock import Mock, patch
5
+
6
+ import pytest
7
+ from click.testing import CliRunner
8
+
9
+ from amazon_sp_cli.main import cli
10
+
11
+
12
+ class TestListFbaInventory:
13
+ """Test list-fba-inventory command."""
14
+
15
+ @pytest.fixture
16
+ def runner(self):
17
+ """Create Click test runner."""
18
+ return CliRunner()
19
+
20
+ @pytest.fixture
21
+ def mock_inventory_response(self):
22
+ """Mock FBA inventory response."""
23
+ return {
24
+ "payload": {
25
+ "inventorySummaries": [
26
+ {
27
+ "sellerSku": "SKU-123",
28
+ "asin": "B09BBL8T4Z",
29
+ "fnSku": "X123456789",
30
+ "productName": "Test Product",
31
+ "condition": "NewItem",
32
+ "inventoryDetails": {
33
+ "fulfillableQuantity": 10,
34
+ "inboundWorkingQuantity": 5,
35
+ },
36
+ }
37
+ ],
38
+ "pagination": {"nextToken": "token123"},
39
+ }
40
+ }
41
+
42
+ @patch("amazon_sp_cli.cli.SPAPIAuth")
43
+ @patch("amazon_sp_cli.cli.SPAPIClient")
44
+ def test_list_fba_inventory(self, mock_client_class, mock_auth_class, runner, mock_inventory_response):
45
+ """Test listing FBA inventory."""
46
+ mock_client = Mock()
47
+ mock_client.get_fba_inventory.return_value = mock_inventory_response
48
+ mock_client_class.return_value = mock_client
49
+
50
+ mock_auth = Mock()
51
+ mock_auth_class.return_value = mock_auth
52
+
53
+ result = runner.invoke(cli, ["list-fba-inventory"])
54
+
55
+ assert result.exit_code == 0
56
+ # Strip PATH warning if present
57
+ output_text = result.output
58
+ if "{" in output_text:
59
+ brace_idx = output_text.index("{")
60
+ output_text = output_text[brace_idx:]
61
+ output = json.loads(output_text)
62
+ assert len(output["inventory"]) == 1
63
+ assert output["inventory"][0]["sellerSku"] == "SKU-123"
64
+ assert output["nextToken"] == "token123"
65
+
66
+ @patch("amazon_sp_cli.cli.SPAPIAuth")
67
+ @patch("amazon_sp_cli.cli.SPAPIClient")
68
+ def test_list_fba_inventory_with_sku_filter(
69
+ self, mock_client_class, mock_auth_class, runner, mock_inventory_response
70
+ ):
71
+ """Test listing FBA inventory filtered by SKU."""
72
+ mock_client = Mock()
73
+ mock_client.get_fba_inventory.return_value = mock_inventory_response
74
+ mock_client_class.return_value = mock_client
75
+
76
+ mock_auth = Mock()
77
+ mock_auth_class.return_value = mock_auth
78
+
79
+ result = runner.invoke(cli, ["list-fba-inventory", "--sku", "SKU-123,SKU-456"])
80
+
81
+ assert result.exit_code == 0
82
+ mock_client.get_fba_inventory.assert_called_once_with(seller_skus="SKU-123,SKU-456", next_token=None)
83
+
84
+ @patch("amazon_sp_cli.cli.SPAPIAuth")
85
+ @patch("amazon_sp_cli.cli.SPAPIClient")
86
+ def test_list_fba_inventory_with_next_token(
87
+ self, mock_client_class, mock_auth_class, runner, mock_inventory_response
88
+ ):
89
+ """Test listing FBA inventory with pagination token."""
90
+ mock_client = Mock()
91
+ mock_client.get_fba_inventory.return_value = mock_inventory_response
92
+ mock_client_class.return_value = mock_client
93
+
94
+ mock_auth = Mock()
95
+ mock_auth_class.return_value = mock_auth
96
+
97
+ result = runner.invoke(cli, ["list-fba-inventory", "--next-token", "token456"])
98
+
99
+ assert result.exit_code == 0
100
+ mock_client.get_fba_inventory.assert_called_once_with(seller_skus=None, next_token="token456")
101
+
102
+ @patch("amazon_sp_cli.cli.SPAPIAuth")
103
+ @patch("amazon_sp_cli.cli.SPAPIClient")
104
+ def test_list_fba_inventory_empty(self, mock_client_class, mock_auth_class, runner):
105
+ """Test listing FBA inventory with no results."""
106
+ mock_client = Mock()
107
+ mock_client.get_fba_inventory.return_value = {"payload": {"inventorySummaries": []}}
108
+ mock_client_class.return_value = mock_client
109
+
110
+ mock_auth = Mock()
111
+ mock_auth_class.return_value = mock_auth
112
+
113
+ result = runner.invoke(cli, ["list-fba-inventory"])
114
+
115
+ assert result.exit_code == 0
116
+ output = json.loads(result.output)
117
+ assert output["inventory"] == []
118
+ assert "nextToken" not in output
@@ -0,0 +1,55 @@
1
+ """Tests for update and version commands."""
2
+
3
+ from unittest.mock import patch
4
+
5
+ import pytest
6
+ from click.testing import CliRunner
7
+
8
+ from amazon_sp_cli import __version__
9
+ from amazon_sp_cli.main import cli
10
+
11
+
12
+ class TestUpdate:
13
+ """Test self-update command."""
14
+
15
+ @pytest.fixture
16
+ def runner(self):
17
+ """Create Click test runner."""
18
+ return CliRunner()
19
+
20
+ @patch("amazon_sp_cli.commands.update.subprocess.run")
21
+ def test_update(self, mock_run, runner):
22
+ """Test update command succeeds."""
23
+ mock_run.return_value.returncode = 0
24
+
25
+ result = runner.invoke(cli, ["update"])
26
+
27
+ assert result.exit_code == 0
28
+ assert "Updating amazon-sp-cli" in result.output
29
+ mock_run.assert_called_once()
30
+ args = mock_run.call_args[0][0]
31
+ assert args[-1] == "amazon-sp-cli"
32
+
33
+ @patch("amazon_sp_cli.commands.update.subprocess.run")
34
+ def test_update_failure(self, mock_run, runner):
35
+ """Test update command aborts on failure."""
36
+ mock_run.return_value.returncode = 1
37
+
38
+ result = runner.invoke(cli, ["update"])
39
+
40
+ assert result.exit_code != 0
41
+
42
+ def test_update_dry_run(self, runner):
43
+ """Test update --dry-run prints command without running."""
44
+ result = runner.invoke(cli, ["update", "--dry-run"])
45
+
46
+ assert result.exit_code == 0
47
+ assert "Would run:" in result.output
48
+ assert "amazon-sp-cli" in result.output
49
+
50
+ def test_version_flag(self, runner):
51
+ """Test --version outputs version."""
52
+ result = runner.invoke(cli, ["--version"])
53
+
54
+ assert result.exit_code == 0
55
+ assert f"amz-sp, version {__version__}" in result.output
File without changes
File without changes
File without changes
File without changes
File without changes