Skip to main content

sapsf-shared

Shared Python SDK for SAP SuccessFactors tools.

A single, well-tested library that extracts the common patterns repeated across every SAP SF tool in your workspace: OData HTTP client, authentication, config loading, logging, utilities, and Flask boilerplate.

Why this exists

Every SAP SF tool in your workspace reimplements:

  • OData v2 HTTP client with retries and pagination
  • Basic Auth / OAuth2 / Certificate auth handling
  • Keyring vs file-based credential storage
  • Config loader (YAML/JSON with env var substitution)
  • Coloured logging setup
  • Flask CSRF, error handlers, health endpoint

sapsf-shared consolidates all of this into one package. When you fix a bug in the auth layer, it's fixed everywhere.

Installation

pip install sapsf-shared          # from PyPI
pip install "sapsf-shared[flask]"  # with Flask base

# for local development
cd sapsf/_shared
pip install -e ".[dev,flask]"

Quick Start

1. Connect to SAP SuccessFactors

from sapsf_shared import AuthConfig, SFClient

config = AuthConfig(
    base_url="https://api4.successfactors.com/odata/v2",
    username="admin@companyId",
    password="secret",
    company_id="companyId",
    auth_type="basic",
)

with SFClient(config) as client:
    # Fetch all departments
    depts = client.get("FODepartment")
    print(f"Found {len(depts)} departments")

    # Fetch with filter and pagination
    positions = client.get(
        "Position",
        filter_expr="cust_Country eq 'GBR'",
        select=["code", "externalName", "cust_JobFunction"],
        expand=["cust_JobFunction"],
    )

2. OAuth 2.0

config = AuthConfig(
    base_url="https://api4.successfactors.com/odata/v2",
    auth_type="oauth2",
    client_id="my_client_id",
    client_secret="my_secret",
    company_id="companyId",
)
with SFClient(config) as client:
    ok, msg = client.test_connection()
    print(ok, msg)

3. Secure credential storage

from sapsf_shared.auth import CredentialStore

store = CredentialStore(service="my_tool")
store.set("prd:password", "secret123")
pwd = store.get("prd:password")

Automatically uses OS keyring when available; falls back to a chmod-600 JSON file on headless systems.

4. Config from YAML

from sapsf_shared.config import load_config

cfg = load_config("config.yaml")
# Supports ${ENV_VAR} substitution inside the YAML file

5. Flask base app

from sapsf_shared.flask_base import create_app

app = create_app(__name__, log_dir="logs", enable_csrf=True)

@app.route("/")
def index():
    return {"status": "ok"}

if __name__ == "__main__":
    app.run(port=5050)

Comes with built-in:

  • /api/health endpoint
  • CSRF token generation + validation
  • JSON error handlers (400, 403, 404, 500)
  • CORS preflight support
  • Rotating file logging

API Reference

SFClient

Method Description
get(entity_set, **kwargs) Fetch all records with auto-pagination
get_entity_by_code(entity_set, external_code, **kwargs) Filter by externalCode
post(entity_set, payload) Create a record
patch(entity_set, payload) Update a record
delete(entity_set, key) Delete a record
test_connection() Quick connectivity probe
entity_exists(entity_set, external_code) Check existence

AuthConfig

Dataclass that normalises auth settings across all your tools. Fields: base_url, company_id, auth_type, username, password, client_id, client_secret, token_url, cert_path, key_path, timeout_sec.

SFEnvConfig

Loads configuration from environment variables with the standard SF_* prefix:

export SF_BASE_URL=https://api4.successfactors.com/odata/v2
export SF_USERNAME=admin
export SF_PASSWORD=secret
export SF_COMPANY_ID=companyId
cfg = SFEnvConfig.from_env()

Standard tool environment variables

Tools that connect to one tenant should use these shared variables:

Variable Required Description
SF_BASE_URL Yes Tenant API host or OData v2 URL
SF_AUTH_TYPE No basic (default), oauth2, or certificate
SF_COMPANY_ID Basic/OAuth2 when usernames omit company suffix SuccessFactors company ID
SF_USERNAME Basic only API username
SF_PASSWORD Basic only API password
SF_CLIENT_ID OAuth2 only OAuth client ID
SF_CLIENT_SECRET OAuth2 only OAuth client secret
SF_TOKEN_URL OAuth2 only OAuth token endpoint
SF_CERT_PATH Certificate only Client certificate path
SF_KEY_PATH Certificate only Client private key path

Tools that compare source and target tenants should use the same names with SF_SOURCE_ and SF_TARGET_ prefixes, for example SF_SOURCE_URL, SF_SOURCE_USERNAME, SF_SOURCE_PASSWORD, SF_TARGET_URL, SF_TARGET_USERNAME, and SF_TARGET_PASSWORD. Legacy aliases such as SF_SOURCE_USER may be accepted during migration, but new code should prefer USERNAME.

CredentialStore

Keyring-backed secret storage with automatic fallback to a local .secrets.json file (chmod 600). Use store.clear_alias(alias) to delete all secrets for a tenant.

Utilities

Function Description
parse_sf_date(raw) Parse /Date(millis)/ and ISO formats
is_active_today(record) Check effective dating + status
flatten_record(record) Flatten nested OData for CSV export
build_odata_filter(dict) Build $filter strings from dicts

Tenant Snapshots

The shared package includes an offline-first snapshot store used as the foundation for sf snapshot and future analyzer --snapshot support.

sf snapshot pull --tenant demo --from-dir ./demo-snapshot --only metadata,picklists,positions
sf snapshot list --tenant demo
sf snapshot diff <snapshot-a> <snapshot-b>

Current scope: --from-dir imports JSON files named <collection>.json into an immutable SQLite snapshot under ~/.sf-toolkit/snapshots/<tenant>/. Snapshots are content-addressed, so importing unchanged content reuses the existing snapshot instead of creating another copy. Credential-like fields such as password, token, secret, and API keys are rejected before anything is written.

Live tenant pulling is intentionally not wired in this layer yet; it should be added on top of SFClient with resumable progress once the first analyzer reads snapshots offline.

Assurance Exchange

New tools can exchange engagement context, findings, actions and evidence through sapsf-assurance/v1:

from sapsf_shared import new_assurance_document, validate_assurance_document

document = new_assurance_document(
    engagement_id="ENG-001",
    engagement_name="Migration Assurance",
    client_alias="CLIENT-A",
    run_id="RUN-001",
    tool="migration-tool",
    tool_version="1.0.0",
)
validate_assurance_document(document)

See docs/assurance-exchange-v1.md, the machine-readable schemas/sapsf-assurance-v1.schema.json, and the safe example in examples/. The runtime validator rejects broken references, duplicate IDs, raw tenant identifiers and obvious credential/PII fields.

Development

pip install -e ".[dev,flask]"
pytest -v                  # 13 tests
mypy src/sapsf_shared      # Type checking
ruff check src tests       # Linting
ruff format src tests      # Formatting

Roadmap

  • Vectorised batch operations (upsert_many, delete_many)
  • Connection pooling tuning
  • Async support (httpx-based client)
  • SAP SF API v4 support

License

MIT

Adoption status

Tool Status
sf-config-compare Adopted - parse_sf_date via sapsf_shared.utils
sf-object-sync Adopted - OData client and filter escaping via sapsf_shared.SFClient
sf-position-integrity-checker Next - client/pagination migration pending tenant testing

Depend on it from any tool:

sapsf-shared>=0.1.0

Release files for sapsf-shared 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sapsf-shared 0.2.0
File Size Uploaded
sapsf_shared-0.2.0.tar.gz 121.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sapsf-shared 0.2.0
File Interpreter ABI Platform
sapsf_shared-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 172.2 kB

Release files / sapsf_shared-0.2.0.tar.gz

Download URL sapsf_shared-0.2.0.tar.gz
Size 121.3 kB
Tags Source
SHA-256 checksum
How to use checksums
5a0070a51fa44b15bf44f7e7f382ddd996f6e5c01cb82b10582f8c69c3442102
BLAKE2b-256 checksum
How to use checksums
940281463f8ec0f868f46b133216c586a7dfa4a5c0ad4a142c0ff2993445a6a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 23, 2026.

Transparency log

Release files / sapsf_shared-0.2.0-py3-none-any.whl

Download URL sapsf_shared-0.2.0-py3-none-any.whl
Size 50.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
19214f41c58be899ff29df808373c03586f95035de5a56133f543a7fc76e8e54
BLAKE2b-256 checksum
How to use checksums
e9a57795c83ea7a6d3d85de0b2f0b4194e49a2e761e4cdd168eb0f91ea954ea6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 23, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page