P
PUGUH

Python SDK

PUGUH Python SDK menyediakan client async-first untuk mengintegrasikan autentikasi, organisasi, aplikasi, dan billing ke layanan backend Python Anda. SDK ini mendukung pola autentikasi user-session dan service-to-service.

Instalasi

bash
pip install puguh-sdk

Mulai Cepat

Konteks User (Frontend / Sesi User)

Gunakan access token yang diperoleh dari login user untuk membuat request atas nama user:

python
from puguh_sdk import PuguhClient

async with PuguhClient(
    base_url="https://api-puguh.arsaka.io",
    access_token="user_jwt_token"
) as client:
    # List organizations the user belongs to
    orgs = await client.organizations.list()
    for org in orgs:
        print(f"{org.name} ({org.slug})")

Konteks Service (Backend / Service-to-Service)

Gunakan API key untuk layanan backend yang perlu memanggil PUGUH tanpa sesi user:

python
from puguh_sdk import PuguhClient

async with PuguhClient(
    base_url="https://api-puguh.arsaka.io",
    api_key="pk_live_xxxxxxxxxxxx"
) as client:
    # Fetch available billing plans
    plans = await client.billing.get_plans()
    for plan in plans:
        print(f"{plan.name}: {plan.price}/mo")

Dua Mode Autentikasi

Gunakan access_token saat bertindak atas nama user yang sudah login (contoh: di route API yang menerima JWT dari frontend). Gunakan api_key untuk layanan backend, cron job, atau skrip admin yang beroperasi tanpa sesi user.

Modul Layanan

SDK menyediakan empat modul layanan, masing-masing dapat diakses sebagai properti pada instance client.

Auth (client.auth)

Menangani autentikasi user, siklus hidup token, MFA, dan magic link.

python
# Login — returns access_token and refresh_token
tokens = await client.auth.login(
    email="user@example.com",
    password="secure_password"
)
print(tokens.access_token)

# Register a new user
user = await client.auth.register(
    email="new@example.com",
    password="secure_password",
    full_name="Jane Doe"
)
print(user.user_id)

# Refresh an expired access token
new_tokens = await client.auth.refresh_token(
    refresh_token=tokens.refresh_token
)

# Validate a token and get user context
user_ctx = await client.auth.validate_token(token=tokens.access_token)
print(user_ctx.email, user_ctx.roles)

# Magic link authentication
await client.auth.request_magic_link(email="user@example.com")
tokens = await client.auth.verify_magic_link(token="ml_token_from_email")

# Multi-Factor Authentication
setup = await client.auth.setup_mfa()
print(setup.qr_code_url)  # Show QR to user
await client.auth.confirm_mfa(code="123456")
await client.auth.disable_mfa()

Catatan

Tidak ada metode get_me(). Informasi user tersedia dari response login atau dengan mendekode JWT claims secara lokal menggunakan endpoint PUGUH JWKS.

Organizations (client.organizations)

Kelola organisasi dan anggotanya.

python
# List organizations
orgs = await client.organizations.list()

# Create a new organization
org = await client.organizations.create(
    name="Acme Corp",
    slug="acme-corp"
)

# Get organization details
org = await client.organizations.get(org_id="org-uuid")

# Update organization
await client.organizations.update(
    org_id="org-uuid",
    name="Acme Corporation"
)

# Delete organization
await client.organizations.delete(org_id="org-uuid")

# Member management
members = await client.organizations.list_members(org_id="org-uuid")

await client.organizations.invite_member(
    org_id="org-uuid",
    email="colleague@example.com",
    role="member"
)

await client.organizations.update_member_role(
    org_id="org-uuid",
    user_id="user-uuid",
    role="admin"
)

await client.organizations.remove_member(
    org_id="org-uuid",
    user_id="user-uuid"
)

Applications (client.applications)

Kelola aplikasi dalam organisasi.

python
# List applications in an organization
apps = await client.applications.list(org_id="org-uuid")

# Create an application
app = await client.applications.create(
    org_id="org-uuid",
    name="My SaaS App",
    slug="my-saas-app"
)

# Get application details
app = await client.applications.get(app_id="app-uuid")

# Update application
await client.applications.update(
    app_id="app-uuid",
    name="My SaaS App v2"
)

# Delete application
await client.applications.delete(app_id="app-uuid")

# Application member management
members = await client.applications.list_members(app_id="app-uuid")

await client.applications.add_member(
    app_id="app-uuid",
    user_id="user-uuid",
    role="member"
)

Billing (client.billing)

Kelola subscription, paket, invoice, dan metering penggunaan.

python
# List available plans
plans = await client.billing.get_plans()
for plan in plans:
    print(f"{plan.name}: {plan.price}")

# Get a specific plan
plan = await client.billing.get_plan(plan_id="plan-uuid")

# Get current subscription for an organization
sub = await client.billing.get_subscription(org_id="org-uuid")
print(f"Plan: {sub.plan_name}, Status: {sub.status}")

# Create a new subscription
sub = await client.billing.create_subscription(
    org_id="org-uuid",
    plan_id="plan-uuid"
)

# Upgrade to a different plan
await client.billing.upgrade(
    org_id="org-uuid",
    plan_id="new-plan-uuid"
)

# Cancel subscription
await client.billing.cancel(org_id="org-uuid")

# Invoices and usage
invoices = await client.billing.get_invoices(org_id="org-uuid")
usage = await client.billing.get_usage(org_id="org-uuid")

Manajemen Konteks

Atur konteks organisasi dan aplikasi agar tidak perlu memasukkan ID di setiap panggilan. Setelah diatur, konteks dikirim otomatis dengan semua request berikutnya.

python
from puguh_sdk import PuguhClient

async with PuguhClient(
    base_url="https://api-puguh.arsaka.io",
    api_key="pk_live_xxxxxxxxxxxx"
) as client:
    # Set organization context
    client.set_organization_context("org-uuid")

    # Set application context
    client.set_application_context("app-uuid")

    # Now calls automatically include these IDs
    members = await client.organizations.list_members()

    # Clear context when switching
    client.clear_context()

Penanganan Error

SDK melempar exception bertipe untuk skenario error yang berbeda. Selalu bungkus panggilan SDK dalam blok try/except untuk kode produksi.

python
from puguh_sdk import PuguhClient
from puguh_sdk.exceptions import (
    PuguhError,
    AuthError,
    RateLimitError,
    NetworkError,
)

async with PuguhClient(
    base_url="https://api-puguh.arsaka.io",
    access_token="user_jwt_token"
) as client:
    try:
        orgs = await client.organizations.list()
    except AuthError as e:
        # Token expired or invalid — redirect to login
        print(f"Authentication failed: {e}")
    except RateLimitError as e:
        # Too many requests — back off and retry
        print(f"Rate limited. Retry after {e.retry_after}s")
    except NetworkError as e:
        # Connection failed — check network or API status
        print(f"Network error: {e}")
    except PuguhError as e:
        # Catch-all for any other PUGUH API error
        print(f"API error {e.status_code}: {e.message}")

Hierarki Exception

ExceptionKapanTindakan Tipikal
AuthError 401 Unauthorized atau token tidak valid Refresh token atau redirect ke login
RateLimitError 429 Too Many Requests Tunggu retry_after detik, lalu coba lagi
NetworkError Timeout koneksi atau kegagalan DNS Coba ulang dengan backoff atau alert ops
PuguhError Response 4xx/5xx lainnya Log dan tangani berdasarkan kode status

Environment Variables

Cara yang direkomendasikan untuk mengkonfigurasi SDK di produksi:

bash
# .env
PUGUH_BASE_URL=https://api-puguh.arsaka.io
PUGUH_API_KEY=pk_live_xxxxxxxxxxxx
python
import os
from puguh_sdk import PuguhClient

async with PuguhClient(
    base_url=os.environ["PUGUH_BASE_URL"],
    api_key=os.environ["PUGUH_API_KEY"],
) as client:
    plans = await client.billing.get_plans()

Praktik Terbaik

1. Gunakan Environment Variables untuk Kredensial

Jangan pernah hardcode API key atau token di source code:

python
# Bad — credentials in source code
client = PuguhClient(api_key="pk_live_abc123")

# Good — credentials from environment
client = PuguhClient(api_key=os.environ["PUGUH_API_KEY"])

2. Selalu Gunakan Async Context Manager

Context manager memastikan koneksi HTTP ditutup dengan benar:

python
# Bad — connection may leak
client = PuguhClient(base_url="...", api_key="...")
orgs = await client.organizations.list()
# forgot to close

# Good — auto-cleanup on exit
async with PuguhClient(base_url="...", api_key="...") as client:
    orgs = await client.organizations.list()
# connection closed automatically

3. Tangani Error di Level yang Tepat

Tangkap exception spesifik dekat dengan call site, dan biarkan error tak terduga naik ke atas:

python
async def get_user_orgs(access_token: str) -> list:
    async with PuguhClient(
        base_url=os.environ["PUGUH_BASE_URL"],
        access_token=access_token,
    ) as client:
        try:
            return await client.organizations.list()
        except AuthError:
            # Expected: token expired
            raise HTTPException(status_code=401, detail="Session expired")
        # Let NetworkError, PuguhError bubble up to global handler

4. Gunakan Ulang Instance Client

Untuk layanan yang berjalan lama, buat client sekali dan gunakan ulang di seluruh request:

python
# In your FastAPI app
from contextlib import asynccontextmanager
from puguh_sdk import PuguhClient

puguh_client: PuguhClient | None = None

@asynccontextmanager
async def lifespan(app):
    global puguh_client
    puguh_client = PuguhClient(
        base_url=os.environ["PUGUH_BASE_URL"],
        api_key=os.environ["PUGUH_API_KEY"],
    )
    await puguh_client.__aenter__()
    yield
    await puguh_client.__aexit__(None, None, None)

app = FastAPI(lifespan=lifespan)

@app.get("/plans")
async def list_plans():
    return await puguh_client.billing.get_plans()

Persyaratan Versi Python

PUGUH Python SDK memerlukan Python 3.11+ dan menggunakan asyncio secara native. Semua metode bersifat async dan harus di-await.

Terkait