mm-mobile-sdk 0.1.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.
@@ -0,0 +1 @@
1
+ venv/
File without changes
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.5
2
+ Name: mm-mobile-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the Marketplace Mobile API
5
+ Author-email: Manali Rathod <manali.empiric98@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.9
9
+ Requires-Dist: requests>=2.31.0
File without changes
@@ -0,0 +1,32 @@
1
+ from mm_mobile_sdk.http import HTTPClient
2
+ from mm_mobile_sdk.auth import AuthManager
3
+
4
+
5
+ http = HTTPClient(base_url="https://marketplacemoniter-staging.azurewebsites.net/")
6
+ auth = AuthManager(http)
7
+
8
+ # Step 1: login - should populate auth.access_token and attach the header
9
+ login_response = auth.login("niralempiric6@gmail.com", "Test@123")
10
+ print("Logged in. is_authenticated =", auth.is_authenticated)
11
+ print("Authorization header set to:", http.session.headers.get("Authorization")[:30], "...")
12
+
13
+ # Step 2: make an authenticated call WITHOUT manually passing a token-
14
+ # this proves the header attached in step 1 is actually being reused
15
+ user_details = http.post("users-details/")
16
+ print("Authenticated call result:", user_details)
17
+
18
+ # Step 3: logout - should clear the token and remove the header
19
+ auth.logout()
20
+ print("After logout, is_authenticated = ", auth.is_authenticated)
21
+ print("Authorization header now:", http.session.headers.get("Authorization"))
22
+
23
+ # try:
24
+ # result = client.post("login/", json={
25
+ # "username" : "Aniralempiric6@gmail.com",
26
+ # "password" : "Test@123",
27
+ # })
28
+ # print("Sucess", result)
29
+ # except AuthenticationError as e:
30
+ # print("Correctly raised AuthenticationError:", e.status_code, e.message)
31
+ # except MarketplaceAPIError as e:
32
+ # print("Raised a different MarketplaceAPIError subtype:", type(e).__name__, e.status_code, e.message)
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+ [project]
5
+ name = "mm-mobile-sdk"
6
+ version = "0.1.0"
7
+ description = "Python SDK for the Marketplace Mobile API"
8
+ readme = "README.md"
9
+ requires-python = ">=3.9"
10
+ license = "MIT"
11
+ authors = [
12
+ { name = "Manali Rathod", email = "manali.empiric98@gmail.com" }
13
+ ]
14
+ dependencies = [
15
+ "requests>=2.31.0"
16
+ ]
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/mm_mobile_sdk"]
File without changes
@@ -0,0 +1,36 @@
1
+ from .http import HTTPClient
2
+ from .exceptions import AuthenticationError
3
+
4
+
5
+ class AuthManager:
6
+ """Owns the current auth token and attaches it to the shared HTTP session."""
7
+
8
+ def __init__(self, http_client:HTTPClient):
9
+ self._http = http_client
10
+ self.access_token = None
11
+ self.refresh_token = None
12
+
13
+ def login(self, username, password):
14
+ response = self._http.post("login/", json={
15
+ "username": username,
16
+ "password": password,
17
+ })
18
+
19
+ token_data = response.get("token") if response else None
20
+ if not token_data or "access" not in token_data:
21
+ raise AuthenticationError("Login response did not contain a token", response_body=response)
22
+
23
+ self.access_token = token_data["access"]
24
+ self.refresh_token = token_data.get("refresh")
25
+ self._http.session.headers["Authorization"] = self.access_token
26
+ return response
27
+
28
+
29
+ def logout(self):
30
+ self.access_token = None
31
+ self.refresh_token = None
32
+ self._http.session.headers.pop("Authorization", None)
33
+
34
+ @property
35
+ def is_authenticated(self):
36
+ return self.access_token is not None
@@ -0,0 +1,10 @@
1
+ from .http import HTTPClient
2
+ from .auth import AuthManager
3
+
4
+
5
+ class MarketplaceClient:
6
+ """Single entry point to the Marketplace Mobile API."""
7
+
8
+ def __init__(self, base_url, timeout=10):
9
+ self._http = HTTPClient(base_url, timeout=timeout)
10
+ self.auth = AuthManager(self._http)
@@ -0,0 +1,32 @@
1
+ class MarketplaceAPIError(Exception):
2
+ """Base exception for all SDK errors."""
3
+
4
+ def __init__(self, message, status_code=None, response_body=None):
5
+ super().__init__(message)
6
+ self.message = message
7
+ self.status_code = status_code
8
+ self.response_body = response_body
9
+
10
+
11
+ class AuthenticationError(MarketplaceAPIError):
12
+ """Raised on 401 - invalid/expired/blacklisted token, or bad login credentials."""
13
+
14
+
15
+ class ValidationError(MarketplaceAPIError):
16
+ """Raised on 400/422 - the server rejected the request payload."""
17
+
18
+
19
+ class NotFoundError(MarketplaceAPIError):
20
+ """Raised on 404 - resource does not exist."""
21
+
22
+
23
+ class RateLimitError(MarketplaceAPIError):
24
+ """Raised on 429 - request was throttled"""
25
+
26
+
27
+ class ServerError(MarketplaceAPIError):
28
+ """Raised on 5xx — something failed on the server side."""
29
+
30
+
31
+ class NetworkError(MarketplaceAPIError):
32
+ """Raised when the request never reached the server (timeout, DNS, connection refused)."""
@@ -0,0 +1,62 @@
1
+ import requests
2
+ from . import exceptions
3
+
4
+
5
+ class HTTPClient:
6
+ """Low-level request/response layer. knows nothing about auth, token, or specific endpoints."""
7
+
8
+ def __init__(self, base_url, timeout=10):
9
+ self.base_url = base_url.rstrip("/")
10
+ self.timeout = timeout
11
+ self.session = requests.Session()
12
+ self.session.headers.update({"Content-Type": "application/json"})
13
+
14
+
15
+ def request(self, method, path, **kwargs):
16
+ url = f"{self.base_url}/{path.lstrip('/')}"
17
+ try:
18
+ response = self.session.request(method, url, timeout=self.timeout, **kwargs)
19
+ except requests.exceptions.RequestException as exc:
20
+ raise exceptions.NetworkError(str(exc)) from exc
21
+ return self._handle_response(response)
22
+
23
+
24
+ def get(self, path, **kwargs):
25
+ return self.request("GET", path, **kwargs)
26
+
27
+
28
+ def post(self, path, **kwargs):
29
+ return self.request("POST", path, **kwargs)
30
+
31
+
32
+ def patch(self, path, **kwargs):
33
+ return self.request("PATCH", path, **kwargs)
34
+
35
+
36
+ def delete(self, path, **kwargs):
37
+ return self.request("DELETE", path, **kwargs)
38
+
39
+
40
+ def _handle_response(self, response):
41
+ try:
42
+ body = response.json()
43
+ except ValueError:
44
+ body = None
45
+
46
+ if response.status_code < 400:
47
+ return body
48
+
49
+ message = (body or {}).get("message") or (body or {}).get("error") or response.text
50
+
51
+ if response.status_code == 401:
52
+ raise exceptions.AuthenticationError(message, response.status_code, body)
53
+ if response.status_code in (400, 422):
54
+ raise exceptions.ValidationError(message, response.status_code, body)
55
+ if response.status_code == 404:
56
+ raise exceptions.NotFoundError(message, response.status_code, body)
57
+ if response.status_code == 429:
58
+ raise exceptions.RateLimitError(message, response.status_code, body)
59
+ if response.status_code >= 500:
60
+ raise exceptions.ServerError(message, response.status_code, body)
61
+
62
+ raise exceptions.MarketplaceAPIError(message, response.status_code, body)
@@ -0,0 +1,12 @@
1
+ from mm_mobile_sdk.client import MarketplaceClient
2
+
3
+ client = MarketplaceClient(base_url="https://marketplacemoniter-staging.azurewebsites.net/")
4
+
5
+ client.auth.login("niralempiric6@gmail.com", "Test@123")
6
+ print("is_authenticated:", client.auth.is_authenticated)
7
+
8
+ keywords = client._http.get("keywords/")
9
+ print("Authenticated call result:", keywords)
10
+
11
+ client.auth.logout()
12
+ print("is_authenticated after logout:", client.auth.is_authenticated)