Skip to main content

scaffold-module

Scaffold a new BTP service module for the SAP Cloud SDK for Python. Use when a contributor wants to add a new service integration and needs the standard directory layout, stubs, and telemetry wiring generated automatically.

Datos de origen

Repositorio
SAP/cloud-sdk-python
Última actividad en el origen
25 de mayo de 2026 a las 12:06
Idioma detectado de SKILL.md
inglés
Estrellas
29
Forks
45

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
scaffold-module
description
Scaffold a new BTP service module for the SAP Cloud SDK for Python. Use when a contributor wants to add a new service integration and needs the standard directory layout, stubs, and telemetry wiring generated automatically.
tools
Bash, Read, Write, Edit
compatibility
uv, local cloud-sdk-python checkout (run from repo root)
# New Module Scaffold: SAP Cloud SDK for Python Generates the full standard module layout with lint-clean stubs, wires telemetry, then runs a self-review pass on the generated files so you start from a clean baseline. Run from the root of your `cloud-sdk-python` checkout. --- ## Phase 1: Collect Inputs Ask the user for the following. Confirm before writing any files. | Variable | What to ask | Example | |----------|-------------|---------| | `MODULE_NAME` | "Module directory name (snake_case)?" | `document_management` | | `SERVICE_NAME` | "Full service display name?" | `Document Management Service` | | `SHORT` | "Short prefix for class names (e.g. DMS, AGW)?" | `DMS` | | `DESCRIPTION` | "One-sentence description of what this service does?" | `Provides document storage and retrieval on SAP BTP.` | Derive automatically: - `SHORT_LOWER` = `SHORT` lowercased (e.g., `dms`) - `MODULE_UPPER` = `MODULE_NAME` uppercased (e.g., `DOCUMENT_MANAGEMENT`) --- ## Phase 2: Check for Existing Files ```bash ls src/sap_cloud_sdk/<MODULE_NAME>/ 2>/dev/null && echo "EXISTS" || echo "OK" ls tests/<MODULE_NAME>/ 2>/dev/null && echo "EXISTS" || echo "OK" ``` If either exists, warn and ask whether to overwrite before continuing. --- ## Phase 3: Generate Files Write all files below, substituting all `<PLACEHOLDERS>`. ### `src/sap_cloud_sdk/<MODULE_NAME>/__init__.py` ```python """SAP Cloud SDK for Python - <SERVICE_NAME> module. <DESCRIPTION> Usage: from sap_cloud_sdk.<MODULE_NAME> import create_client client = create_client() """ from __future__ import annotations import logging from typing import Optional from sap_cloud_sdk.<MODULE_NAME>.client import <SHORT>Client from sap_cloud_sdk.<MODULE_NAME>.config import load_from_env_or_mount, <SHORT>Config from sap_cloud_sdk.<MODULE_NAME>.exceptions import ( <SHORT>Error, ClientCreationError, ConfigError, HttpError, ) logger = logging.getLogger(__name__) def create_client( *, instance: Optional[str] = None, config: Optional[<SHORT>Config] = None, ) -> <SHORT>Client: """Create a <SERVICE_NAME> client with automatic cloud configuration. Args: instance: Instance name for secret resolution. Defaults to "default". config: Optional explicit <SHORT>Config bypassing secret resolution. Returns: <SHORT>Client ready to use. Raises: ClientCreationError: If client creation fails. """ try: binding = config or load_from_env_or_mount(instance) return <SHORT>Client(binding) except Exception as e: raise ClientCreationError(f"failed to create <SHORT_LOWER> client: {e}") from e __all__ = [ "create_client", "<SHORT>Client", "<SHORT>Config", "<SHORT>Error", "ClientCreationError", "ConfigError", "HttpError", ] ``` ### `src/sap_cloud_sdk/<MODULE_NAME>/client.py` ```python """<SERVICE_NAME> client. Do not instantiate directly: use create_client() from the module root. """ from __future__ import annotations from sap_cloud_sdk.<MODULE_NAME>.config import <SHORT>Config class <SHORT>Client: """Client for the SAP BTP <SERVICE_NAME>. Use :func:`sap_cloud_sdk.<MODULE_NAME>.create_client` instead of instantiating this class directly. """ def __init__(self, config: <SHORT>Config) -> None: self._config = config # TODO: implement service methods. Add to imports when ready: # from sap_cloud_sdk.core.telemetry import Module, Operation, record_metrics # Then decorate each method: # @record_metrics(Module.<MODULE_UPPER>, Operation.<OPERATION_CONSTANT>) # def my_operation(self, ...) -> ...: # ... ``` ### `src/sap_cloud_sdk/<MODULE_NAME>/config.py` ```python """Configuration and secret resolution for <SERVICE_NAME>.""" from __future__ import annotations from dataclasses import dataclass from typing import Optional from sap_cloud_sdk.core.secret_resolver.resolver import ( read_from_mount_and_fallback_to_env_var, ) from sap_cloud_sdk.<MODULE_NAME>.exceptions import ConfigError @dataclass class <SHORT>Config: """Service binding for <SERVICE_NAME>. Attributes: url: Service base URL. client_id: OAuth2 client ID. client_secret: OAuth2 client secret. token_url: OAuth2 token endpoint URL. """ url: str client_id: str client_secret: str token_url: str @dataclass class BindingData: """Raw binding fields read by the secret resolver.""" url: str = "" clientid: str = "" clientsecret: str = "" def validate(self) -> None: """Validate that required fields are present.""" if not self.url: raise ValueError("url is required") if not self.clientid: raise ValueError("clientid is required") if not self.clientsecret: raise ValueError("clientsecret is required") def to_config(self) -> <SHORT>Config: """Transform raw binding into a unified <SHORT>Config.""" # TODO: BTP service bindings vary — check the actual binding schema. # Some services provide a separate UAA URL (e.g. binding["uaa"]["url"]); # others include it as a top-level "url" field. Do not derive token_url # from the service URL; read it from the binding instead. return <SHORT>Config( url=self.url, client_id=self.clientid, client_secret=self.clientsecret, token_url="", # TODO: populate from the correct binding field ) def load_from_env_or_mount(instance: Optional[str] = None) -> <SHORT>Config: """Load <SERVICE_NAME> configuration from mount or environment variables. Mount path: /etc/secrets/appfnd/<MODULE_NAME>/{instance}/ Env fallback: CLOUD_SDK_CFG_<MODULE_UPPER>_{INSTANCE}_{FIELD_KEY} Args: instance: Logical instance name. Defaults to "default". Returns: <SHORT>Config Raises: ConfigError: If loading or validation fails. """ inst = instance or "default" binding = BindingData() try: read_from_mount_and_fallback_to_env_var( base_volume_mount="/etc/secrets/appfnd", base_var_name="CLOUD_SDK_CFG", module="<MODULE_NAME>", instance=inst, target=binding, ) binding.validate() return binding.to_config() except Exception as e: raise ConfigError( f"failed to load <MODULE_NAME> configuration for instance='{inst}': {e}" ) from e ``` ### `src/sap_cloud_sdk/<MODULE_NAME>/exceptions.py` ```python """Exception classes for the <SERVICE_NAME> module.""" class <SHORT>Error(Exception): """Base exception for all <SERVICE_NAME> module errors.""" class ClientCreationError(<SHORT>Error): """Raised when client creation fails.""" class ConfigError(<SHORT>Error): """Raised when configuration or secret resolution fails.""" class HttpError(<SHORT>Error): """Raised for HTTP-related errors from <SERVICE_NAME>. Attributes: status_code: HTTP status code, if available. response_text: Raw response payload for diagnostics, if available. """ def __init__( self, message: str, status_code: int | None = None, response_text: str | None = None, ) -> None: super().__init__(message) self.status_code = status_code self.response_text = response_text ``` ### `src/sap_cloud_sdk/<MODULE_NAME>/_models.py` ```python """Pydantic models for <SERVICE_NAME> API requests and responses.""" from __future__ import annotations # TODO: define Pydantic models for service API responses. Add to imports when ready: # from pydantic import BaseModel # Example: # # class <SHORT>Item(BaseModel): # id: str # name: str ``` ### `src/sap_cloud_sdk/<MODULE_NAME>/py.typed` Empty file (PEP 561 marker). ### `src/sap_cloud_sdk/<MODULE_NAME>/user-guide.md` ```markdown # <SERVICE_NAME>: User Guide <DESCRIPTION> ## Prerequisites Add the service binding to your `app.yaml`: ```yaml requires: - name: my-<SHORT_LOWER>-instance service: <SHORT_LOWER> # TODO: replace with the actual BTP service name plan: standard ``` ## Installation ```bash uv add sap-cloud-sdk ``` ## Quick Start ```python from sap_cloud_sdk.<MODULE_NAME> import create_client client = create_client() # TODO: add the most common usage example ``` ## Configuration Credentials are resolved automatically from: - **Cloud (mounted secret)**: `/etc/secrets/appfnd/<MODULE_NAME>/{instance}/` - **Env fallback**: `CLOUD_SDK_CFG_<MODULE_UPPER>_{INSTANCE}_{FIELD}` To target a specific binding instance: ```python client = create_client(instance="my-<SHORT_LOWER>-instance") ``` ## API Reference ### `create_client(*, instance=None, config=None) → <SHORT>Client` Creates and returns a configured `<SHORT>Client`. <!-- TODO: document each public client method --> ## Error Handling ```python from sap_cloud_sdk.<MODULE_NAME>.exceptions import <SHORT>Error, ConfigError, HttpError try: result = client.my_operation() except HttpError as e: print(f"HTTP {e.status_code}: {e}") except ConfigError as e: print(f"Config error: {e}") except <SHORT>Error as e: print(f"<SHORT_LOWER> error: {e}") ``` ``` ### `tests/<MODULE_NAME>/__init__.py` Empty file. ### `tests/<MODULE_NAME>/unit/__init__.py` Empty file. ### `tests/<MODULE_NAME>/unit/test_client.py` ```python """Unit tests for <SHORT>Client.""" from unittest.mock import MagicMock import pytest from sap_cloud_sdk.<MODULE_NAME> import create_client from sap_cloud_sdk.<MODULE_NAME>.client import <SHORT>Client from sap_cloud_sdk.<MODULE_NAME>.config import <SHORT>Config @pytest.fixture def mock_config() -> <SHORT>Config: return <SHORT>Config( url="https://example.ondemand.com", client_id="test-client-id", client_secret="test-client-secret", token_url="https://example.authentication.eu10.hana.ondemand.com/oauth/token", ) def test_create_client_returns_client(mock_config: <SHORT>Config) -> None: client = create_client(config=mock_config) assert isinstance(client, <SHORT>Client) # TODO: add tests following test_<functionality>_<condition>_<expected_result> ``` --- ## Phase 4: Update Telemetry Module Registry Read `src/sap_cloud_sdk/core/telemetry/module.py` and insert the new entry into the `Module` enum in **alphabetical order by key**: ```python <MODULE_UPPER> = "<MODULE_NAME>" ``` --- ## Phase 5: Self-Review Check the generated files against these criteria. Fix any issues found before reporting to the user. **E3: Module structure**: verify all required files exist:
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub