Files
wehub-resource-sync 4b6817381b
Benchmark image — build + push to ECR (any adapter) / build + push (push) Waiting to run
CI / quality (ubuntu-latest) (push) Waiting to run
CI / test (tools-runtime) (push) Waiting to run
CI / test (e2e-general) (push) Waiting to run
CI / test (cli-runtime) (push) Waiting to run
CI / test (e2e-provider-and-openclaw) (push) Waiting to run
CI / test (integrations-and-misc) (push) Waiting to run
CI / coverage-report (push) Blocked by required conditions
CI / test-kubernetes (push) Waiting to run
CI / should-run-thorough (push) Waiting to run
CI / test-thorough (cloudwatch-demo) (push) Blocked by required conditions
CI / test-thorough (flink-ecs) (push) Blocked by required conditions
CI / test-thorough (upstream-lambda) (push) Blocked by required conditions
CI / test-thorough (prefect-ecs-fargate) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Blocked by required conditions
Release / publish-release (push) Blocked by required conditions
Release / publish-main-release (push) Blocked by required conditions
Release / prepare (push) Waiting to run
Release / verify (push) Blocked by required conditions
Release / build-python-dist (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Blocked by required conditions
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Waiting to run
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

135 lines
4.7 KiB
Python

"""Helpers to select instances from a ``resolved_integrations`` dict.
``classify_integrations`` publishes:
- ``resolved[service]`` — flat config dict of the DEFAULT (first) instance
(backward compat with all code written before multi-instance support)
- ``resolved[f"_all_{service}_instances"]`` — list of all instances as
``[{"name": str, "tags": dict, "config": dict, "integration_id": str}, ...]``
(only present when > 1 instance OR an instance has a non-``default`` name)
These helpers give consumers a clean API for multi-instance selection. They
never mutate the input and tolerate missing keys (returning ``None`` or ``[]``
as appropriate).
"""
from __future__ import annotations
from typing import Any
from pydantic import BaseModel
def _instances_key(service: str) -> str:
return f"_all_{service}_instances"
def _as_dict(config: Any) -> dict[str, Any] | None:
"""Normalize a config value (dict or BaseModel) to a plain dict, or None."""
if isinstance(config, BaseModel):
return config.model_dump(exclude_none=True)
if isinstance(config, dict):
return config
return None
def get_instances(resolved: dict[str, Any] | None, service: str) -> list[dict[str, Any]]:
"""Return all instance entries for ``service``.
When a service has only one instance and that instance has the default
name, ``_all_{service}_instances`` is omitted for tidiness. In that
case we synthesise a single-entry list from the flat ``resolved[service]``
so callers can treat single- and multi-instance cases uniformly.
"""
if not resolved:
return []
explicit = resolved.get(_instances_key(service))
if isinstance(explicit, list):
return [item for item in explicit if isinstance(item, dict)]
flat = resolved.get(service)
flat_dict = _as_dict(flat)
if flat_dict is not None:
return [
{
"name": "default",
"tags": {},
"config": flat_dict,
"integration_id": str(flat_dict.get("integration_id", "")),
}
]
return []
def get_default_instance(resolved: dict[str, Any] | None, service: str) -> dict[str, Any] | None:
"""Return the flat config dict for the default (first) instance, or None."""
if not resolved:
return None
flat = resolved.get(service)
return _as_dict(flat)
def get_instance_by_name(
resolved: dict[str, Any] | None, service: str, name: str
) -> dict[str, Any] | None:
"""Return the config dict of the instance named ``name``, or None."""
target = (name or "").strip().lower()
if not target:
return None
for inst in get_instances(resolved, service):
if str(inst.get("name", "")).lower() == target:
return _as_dict(inst.get("config"))
return None
def get_instances_by_tag(
resolved: dict[str, Any] | None, service: str, key: str, value: str
) -> list[dict[str, Any]]:
"""Return config dicts of every instance whose ``tags[key] == value``."""
target_key = (key or "").strip().lower()
target_value = (value or "").strip().lower()
if not target_key or not target_value:
return []
out: list[dict[str, Any]] = []
for inst in get_instances(resolved, service):
tags = inst.get("tags", {}) if isinstance(inst.get("tags"), dict) else {}
if tags.get(target_key) == target_value:
config = _as_dict(inst.get("config"))
if config is not None:
out.append(config)
return out
def select_instance(
resolved: dict[str, Any] | None,
service: str,
*,
name: str | None = None,
tags: dict[str, str] | None = None,
) -> dict[str, Any] | None:
"""Select an instance by name, then by tags, then default.
``name`` takes precedence if supplied. If ``name`` is set but no match is
found, return None (do NOT silently fall back to default — callers who
want that can chain to ``get_default_instance``). If ``tags`` is supplied
without ``name``, return the first instance whose tags are a superset of
the filter. If neither is supplied, return the default instance.
"""
if name:
return get_instance_by_name(resolved, service, name)
if tags:
normalized = {str(k).strip().lower(): str(v).strip().lower() for k, v in tags.items()}
for inst in get_instances(resolved, service):
inst_tags = inst.get("tags", {}) if isinstance(inst.get("tags"), dict) else {}
if all(inst_tags.get(k) == v for k, v in normalized.items()):
return _as_dict(inst.get("config"))
return None
return get_default_instance(resolved, service)
__all__ = [
"get_default_instance",
"get_instance_by_name",
"get_instances",
"get_instances_by_tag",
"select_instance",
]