Skip to main content

AI/LLM-friendly Python client for SAP OData V2 and V4 APIs - single generic function for all operations

Project description

SAP OData Python

Simple, AI/LLM-friendly Python client for SAP OData V2 and V4 services.

PyPI Python License

Why This Library?

  • SAP-First Design: Built specifically for SAP OData services (V2 Gateway & V4 RAP)
  • AI/LLM Friendly: Single generic function design - perfect for AI agents and automation
  • Simple API: One client, one method signature for all operations
  • Automatic URL Building: Handles complex SAP URL patterns automatically
  • Input Validation: Clear error messages for missing parameters
  • Raw Responses: Returns API responses as-is (no transformation)
  • Helper Methods: get_value() and get_next_link() for convenient data extraction

Installation

```bash pip install sap-odata-python ```

Quick Start - SAP Systems

```python from sap_odata import ODataClient

Connect to SAP system (sap_mode=True is the default)

client = ODataClient( "https://sap-system.company.com:44300", username="your_user", password="your_password", client="100" )

SAP OData V4 (RAP Services)

response = client.get( service="zsd_my_service", entity="MyEntity", version="v4", namespace="zsb_my_service", # Required for V4 filter="Status eq 'ACTIVE'", top=10 )

V4 Response: {"@odata.context": "...", "value": [...]}

items = client.get_value(response, "v4")

SAP OData V2 (Gateway Services)

response = client.get( service="ZMY_SALESORDER_SRV", entity="SalesOrderSet", version="v2", filter="Status eq 'OPEN'", top=10 )

V2 Response: {"d": {"results": [...]}} or {"d": [...]}

items = client.get_value(response, "v2")

for item in items: print(item) ```

Response Formats

This library returns raw API responses without transformation:

Version Collection Response Single Entity Response
V4 {"@odata.context": "...", "value": [...]} {"@odata.context": "...", "ID": 1, ...}
V2 {"d": {"results": [...]}} or {"d": [...]} {"d": {"ID": 1, ...}}

Helper Methods

Use helper methods to extract data consistently:

```python

get_value() - Extract entities as a list (works for both V2 and V4)

response = client.get("ZMY_SRV", "Products", version="v2", top=10) items = client.get_value(response, "v2") # Returns list of products

get_next_link() - Get pagination URL

next_url = client.get_next_link(response, "v4") # @odata.nextLink next_url = client.get_next_link(response, "v2") # d.__next ```

SAP URL Patterns (Handled Automatically)

The library automatically builds correct SAP URLs:

Version URL Pattern
V4 /sap/opu/odata4/sap/{namespace}/srvd_a2x/sap/{service}/0001/{entity}
V2 /sap/opu/odata/sap/{service}/{entity}

You just provide service, entity, and namespace (for V4) - the library builds the full URL.

API Reference

ODataClient Constructor

```python client = ODataClient( host, # SAP system URL (e.g., "https://sap.company.com:44300") username="", # SAP username password="", # SAP password client="", # SAP client number (e.g., "100", "120") sap_mode=True, # True for SAP systems (default), False for other OData services verify_ssl=True, # SSL certificate verification timeout=60 # Request timeout in seconds ) ```

Methods

Method Description
get(service, entity, version, namespace, **params) Read data (GET)
post(service, entity, data, version, namespace) Create record (POST)
patch(service, entity, data, version, namespace) Update record (PATCH)
delete(service, entity, version, namespace) Delete record (DELETE)
metadata(service, version, namespace) Get service metadata (XML)
get_value(response, version) Extract entities as list from response
get_next_link(response, version) Extract pagination URL from response

Query Parameters (for GET)

Parameter Example Description
top top=10 Limit number of results
skip skip=20 Skip records (pagination)
filter filter="Price gt 100" OData filter expression
select select="ID,Name,Price" Select specific fields
expand expand="Customer,Items" Expand navigation properties
orderby orderby="Name asc" Sort results

SAP OData V4 (RAP Services)

```python client = ODataClient( "https://sap-system.company.com:44300", username="user", password="pass", client="100" )

Simple query

response = client.get( service="zmy_product_api", entity="Products", version="v4", namespace="zsb_product_api", filter="ProductID eq '12345'" )

Raw V4: {"@odata.context": "...", "value": [...]}

products = client.get_value(response, "v4")

Single entity by key

response = client.get( service="zmy_product_api", entity="Products('12345')", version="v4", namespace="zsb_product_api" )

Raw V4 single: {"@odata.context": "...", "ProductID": "12345", ...}

product = client.get_value(response, "v4")[0]

Complex nested $expand

response = client.get( service="zmy_order_api", entity="Orders", version="v4", namespace="zsb_order_api", filter="OrderID eq '12345'", expand="Customer($expand=Contacts),LineItems($expand=Product)" ) orders = client.get_value(response, "v4") for order in orders: print(f"Order: {order['OrderID']}") for line in order.get("LineItems", []): print(f" Line Item: {line['ProductName']}") ```

SAP OData V2 (Gateway Services)

```python

Simple query

response = client.get( service="ZMY_SALESORDER_SRV", entity="SalesOrderSet", version="v2", top=10, filter="Status eq 'OPEN'" )

Raw V2: {"d": {"results": [...]}} or {"d": [...]}

orders = client.get_value(response, "v2")

Single entity by key

response = client.get( service="ZMY_CUSTOMER_SRV", entity="CustomerSet(CustomerID='CUST001')", version="v2" )

Raw V2 single: {"d": {"CustomerID": "CUST001", ...}}

customer = client.get_value(response, "v2")[0]

Complex nested $expand (V2 style with /)

response = client.get( service="ZMY_ORDER_SRV", entity="OrderSet(OrderID='12345')", version="v2", expand="OrderToCustomer/CustomerToContacts,OrderToItems/ItemToProduct" ) orders = client.get_value(response, "v2") for order in orders: print(f"Order: {order['OrderID']}") for item in order.get("OrderToItems", {}).get("results", []): print(f" Item: {item['ProductName']}") ```

Pagination

```python

V4 Pagination

response = client.get("zmy_service", "Products", version="v4", namespace="zsb_service", top=100) all_items = client.get_value(response, "v4")

while next_link := client.get_next_link(response, "v4"): # Next link is a full URL, extract path for next call # Or use requests directly with next_link break # Simplified example

V2 Pagination

response = client.get("ZMY_SRV", "Products", version="v2", top=100) all_items = client.get_value(response, "v2")

next_link = client.get_next_link(response, "v2") # d.__next ```

Write Operations

```python

POST - Create

response = client.post( service="ZMY_SALESORDER_SRV", entity="SalesOrderSet", data={"CustomerID": "CUST001", "Amount": 1000}, version="v2" )

PATCH - Update

client.patch( service="ZMY_SALESORDER_SRV", entity="SalesOrderSet('12345')", data={"Status": "APPROVED"}, version="v2" )

DELETE

client.delete( service="ZMY_SALESORDER_SRV", entity="SalesOrderSet('12345')", version="v2" ) ```

Non-SAP OData Services

```python

Public OData services (Northwind, TripPin, etc.)

client = ODataClient("https://services.odata.org", sap_mode=False)

V4 example

response = client.get("TripPinRESTierService", "People", top=3) people = client.get_value(response, "v4")

V2 example

response = client.get("V2/Northwind/Northwind.svc", "Products", version="v2", top=5) products = client.get_value(response, "v2") ```

Error Handling

```python from sap_odata import ODataClient, ODataError, ODataConnectionError

try: response = client.get("zmy_service", "Products", version="v4", namespace="zsb_service") except ODataError as e: # Validation errors (missing service, entity, namespace, invalid version) print(f"Validation error: {e}") except ODataConnectionError as e: # Connection/network errors print(f"Connection error: {e}") ```

Validation Rules

Error Condition
"Service name is required" Empty service name
"Entity name is required" Empty entity name
"Invalid version" Version not 'v2' or 'v4'
"Namespace is required for SAP V4" SAP V4 without namespace

Input Validation

```python

These raise ODataError with clear messages:

Empty service

client.get("", "Products") # ODataError: Service name is required

Empty entity

client.get("ZMY_SRV", "") # ODataError: Entity name is required

Invalid version

client.get("ZMY_SRV", "Products", version="v3") # ODataError: Invalid version

SAP V4 without namespace

client.get("zmy_service", "Products", version="v4") # ODataError: Namespace is required ```

Context Manager

```python with ODataClient("https://sap-system.com", username="user", password="pass") as client: response = client.get("ZMY_SRV", "Products", version="v2", top=10) products = client.get_value(response, "v2")

Session is automatically closed

```

License

MIT License - see LICENSE file.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sap_odata_python-1.0.6.tar.gz (18.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sap_odata_python-1.0.6-py3-none-any.whl (12.8 kB view details)

Uploaded Python 3

File details

Details for the file sap_odata_python-1.0.6.tar.gz.

File metadata

  • Download URL: sap_odata_python-1.0.6.tar.gz
  • Upload date:
  • Size: 18.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for sap_odata_python-1.0.6.tar.gz
Algorithm Hash digest
SHA256 f34091381854e18255b124848672157daf9147bcabc3ac9b82b707000e0e5a5b
MD5 b3256370d437508c9dc649a71577563a
BLAKE2b-256 a9579b2d7a0c45d68ee80fc9463b161826de788ade88050b679c33a30023f55b

See more details on using hashes here.

File details

Details for the file sap_odata_python-1.0.6-py3-none-any.whl.

File metadata

File hashes

Hashes for sap_odata_python-1.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 c04b76c5cb8dd679593c4a7f82465b9816c734f6c126606d4d708cb461fdb480
MD5 a5c4cf0d9bd21de5df70b303f9132c8d
BLAKE2b-256 4b7e055a9569451a6f5ed116def04fa4ac519ff4999f9c52a07e5769574bcee6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page