4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
92 lines
2.9 KiB
Python
92 lines
2.9 KiB
Python
"""Internal helpers shared by relational integrations."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from collections.abc import Callable
|
|
from typing import Any
|
|
|
|
from pydantic import field_validator
|
|
|
|
from config.strict_config import StrictConfigModel
|
|
|
|
_TRUE_ENV_VALUES = frozenset({"true", "1", "yes"})
|
|
|
|
|
|
def env_bool(name: str, default: bool) -> bool:
|
|
"""Return a boolean environment variable with common truthy handling."""
|
|
fallback = "true" if default else "false"
|
|
return os.getenv(name, fallback).strip().lower() in _TRUE_ENV_VALUES
|
|
|
|
|
|
def env_int(name: str, default: int) -> int:
|
|
"""Return an integer environment variable, falling back on invalid input."""
|
|
raw = os.getenv(name, "").strip()
|
|
return int(raw) if raw.isdecimal() else default
|
|
|
|
|
|
def env_str(name: str, default: str = "") -> str:
|
|
"""Return a stripped environment variable with an optional fallback."""
|
|
normalized = os.getenv(name, default).strip()
|
|
return normalized or default
|
|
|
|
|
|
class RelationalConfigBase(StrictConfigModel):
|
|
"""Shared field validators for relational DB config models (host, database, username)."""
|
|
|
|
@field_validator("host", mode="before", check_fields=False)
|
|
@classmethod
|
|
def _normalize_host(cls, value: Any) -> str:
|
|
return str(value or "").strip()
|
|
|
|
@field_validator("database", mode="before", check_fields=False)
|
|
@classmethod
|
|
def _normalize_database(cls, value: Any) -> str:
|
|
return str(value or "").strip()
|
|
|
|
@field_validator("username", mode="before", check_fields=False)
|
|
@classmethod
|
|
def _normalize_username(cls, value: Any) -> str:
|
|
return str(value or "").strip()
|
|
|
|
|
|
def resolve_stored_or_env_config[ConfigT](
|
|
service: str,
|
|
*,
|
|
host: str,
|
|
database: str,
|
|
port: int,
|
|
build_config: Callable[[dict[str, Any] | None], ConfigT],
|
|
env_loader: Callable[[], ConfigT | None],
|
|
extra_from_credentials: Callable[[dict[str, Any]], dict[str, Any]],
|
|
extra_from_env: Callable[[ConfigT], dict[str, Any]],
|
|
) -> ConfigT:
|
|
"""Resolve a relational config from store first, then env, then identifiers only."""
|
|
from integrations.store import get_integration
|
|
|
|
stored = get_integration(service)
|
|
if stored:
|
|
credentials = stored.get("credentials", {})
|
|
if isinstance(credentials, dict):
|
|
return build_config(
|
|
{
|
|
"host": host,
|
|
"port": credentials.get("port", port),
|
|
"database": database,
|
|
**extra_from_credentials(credentials),
|
|
}
|
|
)
|
|
|
|
env_config = env_loader()
|
|
if env_config is not None:
|
|
return build_config(
|
|
{
|
|
"host": host,
|
|
"port": port,
|
|
"database": database,
|
|
**extra_from_env(env_config),
|
|
}
|
|
)
|
|
|
|
return build_config({"host": host, "port": port, "database": database})
|