Generic REST API Layer
The generic REST API layer provides a unified interface for interacting with Dell Automation Platform (DAP) resources across different components, such as the portal and the orchestrator. This layer abstracts the underlying HTTP communication, manages a static registry of available resource types and their endpoints, and implements kubectl-style verbs to allow users to perform operations like get, create, delete, and describe on any registered resource.
HTTP Client
Section titled “HTTP Client”The RestClient class in src/dap/rest/client.py serves as the core HTTP client, targeting a specific component server (either the portal or a selected orchestrator outcome) 1. It handles authentication by attaching the bearer token as a raw Authorization header, noting that DAP does not use the Bearer scheme on these endpoints. The client supports several request-preview flags: --curl prints the equivalent curl command for any verb instead of executing it, --dry-run=client validates a mutation locally without calling the API, and --dry-run=api prints a mutation’s request without executing it. Read verbs ignore --dry-run settings, matching the reference CLI behavior, while --curl applies to reads as well.
The client also handles cursor pagination for list operations via the list_all method, which follows next links in the response until all items are retrieved or a page limit is reached 2. It uses httpx for making requests and raises DapError for HTTP errors, including specific handling for 401 unauthorized responses.
Resource Registry
Section titled “Resource Registry”The resource registry, defined in src/dap/rest/registry.py, maintains an immutable index of resource types for one or more components 3. Since DAP ships no resource-discovery endpoint, the catalog of resource types, their endpoint paths, and their verb sets are carried in the codebase. Some endpoints are irregular; for example, clusters is served at /rest/v1/endpoints?type=CLUSTER rather than /rest/v1/clusters.
The Resource dataclass defines a resource type with attributes such as name, kind, component, verbs, get_path, shortnames, list_key, and default_query. The Registry class indexes these resources by component and token (name, kind, or shortname) for efficient lookup. The load_registry function builds the registry by combining resources from per-component catalog modules, portal and orchestrator. These catalogs are generated by scripts/gen_catalog.py from the API registry (docs/api-registry/registry.json) and should not be edited by hand 4 5.
Verb Implementations
Section titled “Verb Implementations”The registry defines the set of verbs available for each resource type, such as get, describe, create, apply, delete, patch, action, wait, logs, events, diff, and explain 4 5. These verbs are implemented as kubectl-style commands, allowing users to interact with resources in a consistent manner. For example, the blueprints resource in the orchestrator supports get, describe, create, apply, delete, patch, action, diff, and explain verbs 4. Similarly, the deployments resource supports get, describe, create, apply, delete, patch, action, wait, logs, events, diff, and explain verbs.
The explain verb is available for all resources, providing information about the resource type 5. Some resources, like alerts and audit-logs in the orchestrator, only support the explain verb, indicating they are explain/describe-only with no list call 3 4. The get_path attribute specifies the endpoint relative to the component server, and list_key indicates the envelope key holding the list in the response 3. For resources with irregular endpoints, default_query provides additional query parameters, such as type=CLUSTER for clusters.
"""HTTP client for the generic ``/rest/v1`` command layer.
One :class:`RestClient` targets one component server (portal or the selected
orchestrator outcome). It attaches the bearer as a raw ``Authorization`` header
(DAP does not use the ``Bearer`` scheme on these endpoints), follows cursor
pagination, and honours request-preview flags:
- ``--curl`` prints the equivalent ``curl`` for any verb instead of executing.
- ``--dry-run=client`` validates a mutation locally without calling the API.
- ``--dry-run=api`` prints a mutation's request without executing it.
Read verbs ignore ``--dry-run`` (matching the reference CLI); ``--curl`` applies
to reads too.
"""
from __future__ import annotations
import shlex
from dataclasses import dataclass
import httpx
from ..errors import DapError
MUTATING = {"POST", "PUT", "PATCH", "DELETE"}
@dataclass
class ClientOptions:
curl: bool = False
dry_run: str = "none" # none | client | api
include_token: bool = False
timeout: float = 30.0
class RestClient:
def __init__(
self,
server: str,
token: str,
data = self.get_json(next_path, params=params if page == 0 else None)
if data is None:
break
chunk = data.get(list_key, []) if (list_key and isinstance(data, dict)) else data
if isinstance(chunk, list):
items.extend(chunk)
else:
return chunk # singleton/object resource
nxt = (data.get("paging") or {}).get("next") if isinstance(data, dict) else None
next_path = nxt or None
page += 1
return items
"""Client-side resource registry for the generic ``/rest/v1`` command layer.
The Dell Automation Platform ships no resource-discovery endpoint, so the
catalog of resource types, their endpoint paths, and their verb sets are
carried here. Some endpoints are irregular -- e.g. ``clusters`` is served at
``/rest/v1/endpoints?type=CLUSTER`` rather than ``/rest/v1/clusters``.
"""
from __future__ import annotations
from dataclasses import dataclass, field
@dataclass(frozen=True)
class Resource:
"""One resource type exposed by a DAP API component.
``get_path`` is the list/get endpoint relative to the component server
(portal or the selected orchestrator outcome). ``None`` means the type is
explain/describe-only with no list call.
"""
name: str
kind: str
component: str # "portal" | "orchestrator"
verbs: tuple[str, ...]
get_path: str | None = None
shortnames: tuple[str, ...] = ()
list_key: str | None = None # envelope key holding the list, e.g. "results"
default_query: str | None = None # e.g. "type=CLUSTER" for clusters
def matches(self, token: str) -> bool:
return token == self.name or token == self.kind or token in self.shortnames
@dataclass(frozen=True)
class Registry:
"""An immutable index over a set of resources for one or more components."""
resources: tuple[Resource, ...]
"""Generated resource catalog -- do not edit by hand.
Produced by ``scripts/gen_catalog.py`` from the API registry
(``docs/api-registry/registry.json``).
"""
from dap.rest.registry import Resource
RESOURCES = [
Resource(name="alerts", kind="Alert", component="orchestrator", verbs=("explain",)),
Resource(
name="audit-logs",
kind="AuditLog",
component="orchestrator",
verbs=("explain",),
shortnames=("auditlog", "auditlogs"),
),
Resource(
name="blueprints",
kind="Blueprint",
component="orchestrator",
verbs=("get", "describe", "create", "apply", "delete", "patch", "action", "diff", "explain"),
get_path="/rest/v1/blueprints",
shortnames=("bp",),
list_key="results",
),
Resource(
name="catalog-blueprints",
kind="CatalogBlueprint",
component="orchestrator",
verbs=("get", "describe", "explain"),
get_path="/rest/v1/catalog/blueprints",
shortnames=("catbp",),
list_key="results",
),
Resource(
name="catalog-plugins",
kind="CatalogPlugin",
component="orchestrator",
verbs=("get", "describe", "explain"),
"""Generated resource catalog -- do not edit by hand.
Produced by ``scripts/gen_catalog.py`` from the API registry
(``docs/api-registry/registry.json``).
"""
from dap.rest.registry import Resource
RESOURCES = [
Resource(name="alerts", kind="Alert", component="portal", verbs=("get", "explain"), get_path="/rest/v1/alerts"),
Resource(
name="assets",
kind="Asset",
component="portal",
verbs=("get", "describe", "create", "apply", "delete", "patch", "action", "explain"),
get_path="/rest/v1/assets",
list_key="results",
),
Resource(
name="audit-log-retention-configs",
kind="AuditLogRetentionConfig",
component="portal",
verbs=("get", "patch", "explain"),
get_path="/rest/v1/audit-logs/retention-config",
shortnames=("alrc",),
),
Resource(
name="audit-logs",
kind="AuditLog",
component="portal",
verbs=("get", "explain"),
get_path="/rest/v1/audit-logs",
shortnames=("auditlog", "auditlogs"),
),
Resource(
name="blueprint-revisions",
kind="BlueprintRevision",
component="portal",
verbs=("explain",),
shortnames=("bprev",),