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 actual API response - use helper methods to extract data
  • Helper Methods: get_value() and get_next_link() for easy data extraction

Installation

pip install sap-odata-python

Quick Start - SAP Systems

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)
data = client.get(
    service="zsd_my_service",
    entity="MyEntity",
    version="v4",
    namespace="zsb_my_service",  # Required for V4
    filter="Status eq 'ACTIVE'",
    top=10
)

# SAP OData V2 (Gateway Services)
data = client.get(
    service="ZMY_SALESORDER_SRV",
    entity="SalesOrderSet",
    version="v2",
    filter="Status eq 'OPEN'",
    top=10
)

# Use helper methods to extract data
for item in client.get_value(data, "v2"):
    print(item)

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

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 entity array 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)

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

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

# Complex nested $expand
data = 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,Discounts($expand=Details)),Payments($expand=BankAccount)"
)

# Access nested data using helper method
for order in client.get_value(data, "v4"):
    print(f"Order: {order['OrderID']}")
    for line in order.get("LineItems", []):
        print(f"  Line Item: {line['ProductName']}")

SAP OData V2 (Gateway Services)

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

# Entity with key in path
data = client.get(
    service="ZMY_CUSTOMER_SRV",
    entity="CustomerSet(CustomerID='CUST001',Region='US')",
    version="v2"
)

# Complex nested $expand (V2 style with /)
data = client.get(
    service="ZMY_ORDER_SRV",
    entity="OrderSet(OrderID='12345')",
    version="v2",
    expand="OrderToCustomer/CustomerToContacts,OrderToItems/ItemToProduct,OrderToItems/ItemToDiscounts,OrderToPayments/PaymentToBankAccount"
)

# V2 nested results are in "results" arrays
for order in client.get_value(data, "v2"):
    print(f"Order: {order['OrderID']}")
    for item in order.get("OrderToItems", {}).get("results", []):
        print(f"  Item: {item['ProductName']}")

Write Operations

# POST - Create
new_order = 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"
)

Get Service Metadata

# V4 metadata
xml = client.metadata(service="zmy_product_api", namespace="zsb_product_api")

# V2 metadata
xml = client.metadata(service="ZMY_SALESORDER_SRV", version="v2")

print(xml)  # Returns XML string with entity definitions

Using with Non-SAP OData Services

For public OData services like Northwind or TripPin, set sap_mode=False:

from sap_odata import ODataClient

# Non-SAP OData service - set sap_mode=False
client = ODataClient("https://services.odata.org", sap_mode=False)

# Northwind V4
data = client.get("V4/Northwind/Northwind.svc", "Products", top=5)

# Northwind V2
data = client.get("V2/Northwind/Northwind.svc", "Products", version="v2", top=5)

# TripPin
data = client.get("TripPinRESTierService", "People", top=3)
data = client.get("TripPinRESTierService", "Airlines")

Note: sap_mode=True is the default since this library is designed for SAP systems.

Response Format

Responses are returned raw (as received from the API):

# V4 response format
{"@odata.context": "...", "value": [{...}, {...}], "@odata.nextLink": "..."}

# V2 response format  
{"d": [{...}, {...}]}  # or {"d": {"results": [...], "__next": "..."}}

Helper Methods

Use helper methods to extract data consistently:

# Extract entities from response
items = client.get_value(response, "v4")  # Returns list
items = client.get_value(response, "v2")  # Returns list

# Get pagination URL
next_url = client.get_next_link(response, "v4")  # Returns URL or ""
next_url = client.get_next_link(response, "v2")  # Returns URL or ""

Pagination Example

# Fetch all pages
all_items = []
response = client.get("ZMY_SRV", "ItemSet", version="v2", top=100)

while True:
    all_items.extend(client.get_value(response, "v2"))
    next_link = client.get_next_link(response, "v2")
    if not next_link:
        break
    # Fetch next page using the full URL
    response = client.session.get(next_link).json()

print(f"Total items: {len(all_items)}")

Error Handling

from sap_odata import ODataClient, ODataError, ODataConnectionError, ODataAuthError

try:
    client = ODataClient("https://sap.company.com", "user", "pass", client="100")
    data = client.get("zsd_my_service", "MyEntity", version="v4", namespace="zsb_my_service")
except ODataAuthError:
    print("Authentication failed - check username/password")
except ODataConnectionError as e:
    print(f"Connection failed: {e}")
except ODataError as e:
    print(f"OData error: {e}")

Validation Errors

The library validates inputs and provides clear error messages:

# Missing namespace for V4
client.get("zsd_my_service", "MyEntity", version="v4")
# ODataError: Namespace is required for SAP OData V4 services.

# Invalid version
client.get("service", "entity", version="v3")
# ODataError: Invalid version 'v3'. Must be 'v2' or 'v4'

Context Manager

with ODataClient("https://sap-system.com", "user", "pass", client="100") as client:
    data = client.get("ZMY_SALESORDER_SRV", "SalesOrderSet", version="v2")
# Session automatically closed

For AI/LLM Integration

This library is designed to be AI-friendly with a simple, consistent API:

# Single function signature for all SAP OData calls
client.get(
    service="<service_name>",
    entity="<entity_name>",
    version="v2" | "v4",
    namespace="<namespace>",  # Required for V4
    filter="<odata_filter>",
    select="<fields>",
    expand="<navigations>",
    top=<number>
)

License

Apache 2.0

Links

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.7.tar.gz (18.8 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.7-py3-none-any.whl (12.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: sap_odata_python-1.0.7.tar.gz
  • Upload date:
  • Size: 18.8 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.7.tar.gz
Algorithm Hash digest
SHA256 bdda9d8aecddf78a8569c962852da4d9e8f7ef9f0747dc43daf1d3fbcf233c02
MD5 9d81da0784fa48da245cc0b2bdea2136
BLAKE2b-256 1307843aa498cbb6411367708e5119d0652f43decba4872dc536b37f838b22d8

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for sap_odata_python-1.0.7-py3-none-any.whl
Algorithm Hash digest
SHA256 e42133729d5024962277358b1ad5dab7fe5606d66bf2bfeb8694ce2448de99ea
MD5 e3eba93316fab9e1be2d82203b42aaa9
BLAKE2b-256 40a9a189b7e8f2f3cd38b8e5c3063476fc21aa674466e09d8804598047b93232

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