Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ShipthisAPI/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# __variables__ with double-quoted values will be available in setup.py
__version__ = "2.1.0"
__version__ = "2.2.0"

from .shipthisapi import (
ShipthisAPI,
Expand Down
83 changes: 72 additions & 11 deletions ShipthisAPI/shipthisapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Usage:
from ShipthisAPI import ShipthisAPI

# Initialize the client
client = ShipthisAPI(
organisation="your_org_id",
x_api_key="your_api_key",
Expand All @@ -20,6 +21,9 @@

# Get a single item
item = client.get_one_item("shipment", doc_id="abc123")

# Webhook sync update
client.webhook_sync("fcl_load", doc_id, fields=[{"status": "completed"}])
"""

from typing import Any, Dict, List, Optional, Union
Expand Down Expand Up @@ -60,6 +64,7 @@ class ShipthisAPI:
region_id: Region ID for requests.
location_id: Location ID for requests.
timeout: Request timeout in seconds.
custom_headers: Custom headers to override defaults.
"""

DEFAULT_TIMEOUT = 30
Expand All @@ -68,28 +73,28 @@ class ShipthisAPI:
def __init__(
self,
organisation: str,
x_api_key: str,
x_api_key: str = None,
user_type: str = "employee",
region_id: str = None,
location_id: str = None,
timeout: int = None,
base_url: str = None,
custom_headers: Dict[str, str] = None,
) -> None:
"""Initialize the Shipthis API client.

Args:
organisation: Your organisation ID.
x_api_key: Your API key.
x_api_key: Your API key (optional if using custom_headers with auth).
user_type: User type for requests (default: "employee").
region_id: Region ID for requests.
location_id: Location ID for requests.
timeout: Request timeout in seconds (default: 30).
base_url: Custom base URL (optional, for testing).
custom_headers: Custom headers that override defaults (e.g., for server-to-server auth).
"""
if not organisation:
raise ValueError("organisation is required")
if not x_api_key:
raise ValueError("x_api_key is required")

self.x_api_key = x_api_key
self.organisation_id = organisation
Expand All @@ -98,6 +103,7 @@ def __init__(
self.location_id = location_id
self.timeout = timeout or self.DEFAULT_TIMEOUT
self.base_api_endpoint = base_url or self.BASE_API_ENDPOINT
self.custom_headers = custom_headers or {}
self.organisation_info = None
self.is_connected = False

Expand All @@ -111,23 +117,32 @@ def set_region_location(self, region_id: str, location_id: str) -> None:
self.region_id = region_id
self.location_id = location_id

def _get_headers(self) -> Dict[str, str]:
def _get_headers(self, override_headers: Dict[str, str] = None) -> Dict[str, str]:
"""Build request headers.

Args:
override_headers: Headers to override for this specific request.

Returns:
Dictionary of headers.
"""
headers = {
"x-api-key": self.x_api_key,
"organisation": self.organisation_id,
"usertype": self.user_type,
"Content-Type": "application/json",
"Accept": "application/json",
}
if self.x_api_key:
headers["x-api-key"] = self.x_api_key
if self.region_id:
headers["region"] = self.region_id
if self.location_id:
headers["location"] = self.location_id
# Apply custom headers from init
headers.update(self.custom_headers)
# Apply per-request override headers
if override_headers:
headers.update(override_headers)
return headers

def _make_request(
Expand All @@ -136,6 +151,7 @@ def _make_request(
path: str,
query_params: Dict[str, Any] = None,
request_data: Dict[str, Any] = None,
headers: Dict[str, str] = None,
) -> Dict[str, Any]:
"""Make an HTTP request to the API.

Expand All @@ -144,6 +160,7 @@ def _make_request(
path: API endpoint path.
query_params: Query parameters.
request_data: Request body data.
headers: Headers to override for this request.

Returns:
API response data.
Expand All @@ -153,23 +170,23 @@ def _make_request(
ShipthisRequestError: If the request fails.
"""
url = self.base_api_endpoint + path
headers = self._get_headers()
request_headers = self._get_headers(headers)

try:
if request_data:
response = requests.request(
method,
url,
json=request_data,
headers=headers,
headers=request_headers,
params=query_params,
timeout=self.timeout,
)
else:
response = requests.request(
method,
url,
headers=headers,
headers=request_headers,
params=query_params,
timeout=self.timeout,
)
Expand Down Expand Up @@ -466,16 +483,26 @@ def patch_item(
) -> Dict[str, Any]:
"""Patch specific fields of an item.

This is the recommended way to update document fields. It goes through
full field validation, workflow triggers, audit logging, and business logic.

Args:
collection_name: Name of the collection.
collection_name: Name of the collection (e.g., "sea_shipment", "fcl_load").
object_id: Document ID.
update_fields: Fields to update.
update_fields: Dictionary of field_id to value mappings.

Returns:
Updated document data.

Raises:
ShipthisAPIError: If the request fails.

Example:
client.patch_item(
"fcl_load",
"68a4f906743189ad061429a7",
update_fields={"container_no": "CONT123", "seal_no": "SEAL456"}
)
"""
return self._make_request(
"PATCH",
Expand Down Expand Up @@ -959,4 +986,38 @@ def upload_file(
raise ShipthisRequestError(
message=f"Upload failed with status {response.status_code}",
status_code=response.status_code,
)

# ==================== Reference Linked Fields ====================

def create_reference_linked_field(
self,
collection_name: str,
doc_id: str,
payload: Dict[str, Any],
) -> Dict[str, Any]:
"""Create a reference-linked field on a document.

Args:
collection_name: Collection name.
doc_id: Document ID.
payload: Field data to create.

Returns:
API response.

Raises:
ShipthisAPIError: If the request fails.

Example:
client.create_reference_linked_field(
"sea_shipment",
"68a4f906743189ad061429a7",
payload={"field_name": "containers", "data": {...}}
)
"""
return self._make_request(
"POST",
f"incollection/create-reference-linked-field/{collection_name}/{doc_id}",
request_data=payload,
)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setuptools.setup(
name='shipthisapi-python',
version='2.1.0',
version='2.2.0',
author="Mayur Rawte",
author_email="mayur@shipthis.co",
description="ShipthisAPI utility package",
Expand Down
Loading