# Resource Registry — API Access & Query Templates

## Query Template Management — base path `/query-templates`

### Authorization & Token Selection

| Action | HTTP | Path | Query Parameters | IS-M / Infra-M | IS-Admin / Ctx-M | IS-Editor | IS-Contrib | IS-Member |
|--------|------|------|------------------|:--------------:|:----------------:|:---------:|:----------:|:---------:|
| `List` | GET | `/query-templates` | `limit`, `offset`, `metadataPolicy` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Execute JSON Query` | QUERY | `/query-templates` (`POST /query-templates/query` fallback) | `limit`, `offset`, `metadataPolicy` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Create/Update` | PUT | `/query-templates/{query-template-name}` | `metadataPolicy` | ✅ | ❌ | ❌ | ❌ | ❌ |
| `Read` | GET | `/query-templates/{query-template-name}` | `metadataPolicy` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Execute` | QUERY | `/query-templates/{query-template-name}` (`POST` fallback) | `limit`, `offset`, `polymorphic`, `hierarchical`, `metadataPolicy`, `includeContexts` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Delete` | DELETE | `/query-templates/{query-template-name}` | *None* | ✅ | ❌ | ❌ | ❌ | ❌ |

**Token Scope Rule:**

| Operation Group | Token must be issued for |
|----------------|---------------------------|
| `List`, `Execute JSON Query`, `Read` | No specific target Context is required; authorization is evaluated from the token role |
| `Create/Update`, `Delete` | No specific target Context is required; authorization is evaluated from the token role (requires `IS-M` / `Infra-M`) |
| `Execute` | Token Context acts as visibility scope |

### HTTP Contract

| Action | Additional Request Headers | Response Headers | Success HTTP Code | Error HTTP Code |
|--------|----------------------------|------------------|-------------------|-----------------|
| `List` | `Accept` | `Content-Type`, `Link`, `X-Warning` | `200 OK` | *None* |
| `Execute JSON Query` | `Content-Type`, `Accept` | `Content-Type`, `Link`, `X-Warning` | `200 OK` | `400 Bad Request` |
| `Create/Update` | `Content-Type`, `Accept` | `Content-Type`, `X-Warning` | `201 Created` / `200 OK` | `400 Bad Request`, `403 Forbidden`, `409 Conflict` |
| `Read` | `Accept` | `Content-Type`, `Link`, `X-Warning` | `200 OK` | `404 Not Found` |
| `Execute` | `Content-Type`, `Accept` | `Content-Type`, `Link`, `X-Warning`, `X-Result-Scope` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found` |
| `Delete` | *None* | `X-Warning` | `204 No Content` | `403 Forbidden`, `404 Not Found` |

### Operational Notes

```eval_rst
- **Global List Semantics:** ``GET /query-templates`` and its ``/access`` mirror list the global Query Template collection for every operational role; the token Context is not a visibility boundary. Pagination is applied using ``limit`` and ``offset``. When ``metadataPolicy`` includes metadata, ``createdBy`` and ``lastUpdateBy`` are redacted for non-privileged callers. Serialization is all-or-nothing: an unreadable row fails the request instead of producing a partial successful page.
- **Global Definition Query Semantics:** ``QUERY /query-templates`` and its ``/access`` mirror search the global Query Template definitions, not the instances returned by executing a stored template. They accept a required JSON QBE object and support the canonical operators and ``_emit`` rules. Full metadata and projected metadata-identity aliases are redacted for non-privileged callers; ``metadataPolicy`` is ignored with ``X-Warning`` when ``_emit`` determines the response shape. Result serialization is fail-fast rather than returning a silently incomplete page.
- **Target Limitation (Instances Only):** Although the JSON Query engine supports querying Contexts, Type Schemas, Query Templates, and Instances, the Query Template mechanism is *by design* strictly restricted to **Instances**. Any query saved within a template is implicitly executed to find instances. Consequently, the root ``"type"`` in a saved template must always specify a valid **Entity** or **Relation** type (e.g., **Resource**, **EService**). Query templates cannot be used to save queries targeting Contexts, Type Schemas, or other Query Templates.
- **Payload Consistency:** ``name`` in the PUT path parameter must match the ``name`` field in the JSON body.
- **Internal ``id`` ownership during upsert:** The ``id`` field inside the Query Template JSON represents its internal global UUID. The REST endpoint URL strictly uses the readable ``name``, never the UUID. If omitted or null during creation, the backend generates a valid UUID. A supplied UUID already used by another Query Template produces ``409 Conflict``; reuse across different element types produces ``400 Bad Request``, following the registry-wide UUID ownership rule. During an update, an explicitly supplied ``id`` must match the persisted UUID for that ``name``, otherwise the request is rejected with ``400 Bad Request``.
- **Upsert Semantics:** ``PUT /query-templates/{name}`` follows upsert semantics for admin roles: it creates when absent (``201 Created``) and updates when already present (``200 OK``). *(Contrast with Type definition: while Types strictly forbid online structural updates and return ``409 Conflict``, Query Templates fully support online ``PUT`` updates).* Non-admin roles cannot create or update templates; any such attempt returns ``403 Forbidden`` regardless of whether the template already exists.
- **Global Point Read Semantics:** ``GET /query-templates/{name}`` and its ``/access`` mirror resolve the exact, case-sensitive global Query Template name for every operational role without applying token-Context visibility. ``metadataPolicy=none|root|all`` controls metadata inclusion; when metadata is present, ``createdBy`` and ``lastUpdateBy`` are redacted for every non-global role. A missing or case-mismatched name returns ``404 Not Found``. Successful responses include the canonical name-addressed ``Content-Location`` and a ``Link: rel="collection"`` to ``/query-templates``.
- **Parameter Validation:** Template query parameters (prefixed with \$) must be declared in ``templateVariables``; default values are used for validation at save time.
- **Execute Payload:** The optional request body for ``QUERY /query-templates/{name}`` contains a JSON object of template-variable substitutions and, when present, must use ``Content-Type: application/json``. An absent or empty body uses template defaults. Omitted variables use their declared defaults; malformed JSON, a non-object JSON value, an invalid substitution, or an omitted mandatory variable without a default produces ``400 Bad Request``.
- **Hierarchical Execution:** ``hierarchical`` in ``Execute`` expands the search boundary and is available to ``IS-M / Infra-M``, ``IS-Admin / Ctx-M``, ``IS-Editor``, ``IS-Contrib``, and ``IS-Member`` (see [``hierarchical`` parameter](11-api-query-parameters.md#hierarchical)). Administrative roles retain unrestricted descendant visibility; other roles use the active-only hierarchical channel, omit non-``ACTIVE`` descendant memberships, and receive ``X-Result-Scope: state-filtered`` when such descendants exist.
- **Execute Visibility and Privacy:** Execution uses the same instance-query engine and role-derived visibility directives as ``QUERY /instances``. Non-privileged callers receive redacted metadata identities, and ``includeContexts=true`` exposes only authorized descendant memberships and reports ``X-Result-Scope: role-filtered``. Projection queries preserve the canonical ``_emit`` precedence and report ignored enrichment parameters through ``X-Warning``.
- **Permanent Exact-Name Delete:** ``DELETE /query-templates/{name}`` permanently and atomically removes the Query Template identified by its exact, case-sensitive global name and makes that name reusable. Only ``IS-M`` and ``Infra-M`` may delete; token Context does not restrict the target. The operation accepts neither a request body nor query parameters. Supplied query parameters are ignored with ``X-Warning``; an unknown or already deleted exact name returns ``404 Not Found``.
```

## Access API — base path `/access`

Read-only facade that mirrors the readable collection methods and adds query execution endpoints.

> **Architectural Note on Dual-Paths (Network-Level Exposure):** The existence of duplicated read-only endpoints (e.g., `GET /instances` vs `GET /access/instances`) with identical authorization rules serves a critical infrastructure purpose: network-level segregation. The D4Science security model dictates that all write-capable endpoints (e.g., `/instances`, `/contexts`) are restricted solely to internal microservice traffic. Conversely, the `/access` facade (along with its query execution endpoints) is the **only** branch exposed to the external internet. This architectural split ensures external clients can discover and read instances but cannot physically reach the write API surfaces.

> **Important — DB-Level Read-Only Restriction:** All query execution operations initiated through this facade (including `jsonQuery` and running query templates via `/access`) are strictly read-only. The underlying database connection uses a read-only user. Any attempt to execute non-idempotent operations (`INSERT`, `UPDATE`, `DELETE`, etc.) within these queries or templates will be rejected outright at the database engine level.

> All endpoints in this class that mirror existing management endpoints (`/contexts`, `/types`, `/instances`, `/query-templates`) share the same authorization rules as their counterparts.

> **Normative Mirror-Parity Rule:** For mirrored endpoints, status-code semantics are contractually identical to their core counterparts (including `403` vs `404` distinction where applicable). The facade exists for network exposure and read-only separation, not for semantic rewriting.

The tables below enumerate normative facade-to-core Java method bindings. The hidden
`Access.readCurrentContextLegacy()` compatibility alias mirrors
`ContextManager.readCurrentContextLegacy()` and is intentionally excluded as a separate action.
Transport-derived behavior that has no dedicated Java method is also kept outside the tables.

### Contexts, Types, Query Templates (mirrors)

| Action | HTTP | Path | Facade → Core Method | IS-M / Infra-M | IS-Admin / Ctx-M | IS-Editor | IS-Contrib | IS-Member |
|--------|------|------|---|:-:|:-:|:-:|:-:|:-:|
| `List Contexts` | GET | `/access/contexts` | `Access.listContexts()` → `ContextManager.listContexts()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Execute Context JSON Query` | QUERY | `/access/contexts` | `Access.queryContexts()` → `ContextManager.query()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Read Context` | GET | `/access/contexts/{context-uuid}` | `Access.readContext()` → `ContextManager.read()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Read Current Context` | GET | `/access/contexts/@current` | `Access.readCurrentContext()` → `ContextManager.readCurrentContext()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Read Context History` | GET | `/access/contexts/{context-uuid}/history` | `Access.readContextHistory()` → `ContextManager.readHistory()` | ✅ | ✅ | ❌ | ❌ | ❌ |
| `Execute Type JSON Query` | QUERY | `/access/types` | `Access.queryTypes()` → `TypeManager.query()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Read Type` | GET | `/access/types/{type-name}` | `Access.readType()` → `TypeManager.read()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `List Query Templates` | GET | `/access/query-templates` | `Access.listQueryTemplates()` → `QueryTemplateManager.all()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Execute Query Template JSON Query` | QUERY | `/access/query-templates` | `Access.queryQueryTemplates()` → `QueryTemplateManager.query()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Read Query Template` | GET | `/access/query-templates/{name}` | `Access.readQueryTemplate()` → `QueryTemplateManager.read()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Execute Query Template` | QUERY | `/access/query-templates/{name}` | `Access.runQueryTemplate()` → `QueryTemplateManager.run()` | ✅ | ✅ | ✅ | ✅ | ✅ |

### Instances (mirrors)

| Action | HTTP | Path | Facade → Core Method | IS-M / Infra-M | IS-Admin / Ctx-M | IS-Editor | IS-Contrib | IS-Member |
|--------|------|------|---|:-:|:-:|:-:|:-:|:-:|
| `List Instances` | GET | `/access/instances/{type}` | `Access.listInstances()` → `InstancesManager.readAll()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Execute Instance JSON Query` | QUERY | `/access/instances` | `Access.jsonQuery()` → `InstancesManager.query()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Read Instance` | GET | `/access/instances/{type}/{instance-uuid}` | `Access.readInstance()` → `InstancesManager.read()` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `List Instance Contexts` | GET | `/access/instances/{type}/{instance-uuid}/contexts` | `Access.getInstanceContexts()` → `InstancesManager.getInstanceContexts()` | ✅ | ✅ | ✅ | ✅ | ✅ |

`HEAD /access/instances/{type}/{instance-uuid}` may be served by Jersey as the automatic bodyless
view of the corresponding `GET`. It is not listed as a facade mirror because neither `Access` nor
`InstancesManager` declares a separate Java method for it.

### Token Selection (Quick Rule)

| Operation Group | Token must be issued for |
|----------------|---------------------------|
| Mirror reads and JSON Queries (`/access/contexts`, `/access/types`, `/access/instances`, `/access/query-templates`) | Same authorization logic as mirrored endpoints (e.g., Token Context acts as visibility scope for Instances, but acts merely as role-bearer for Types and Templates) |
| Template execution via `/access` | Token Context acts as visibility scope |


**Notes for `jsonQuery`:**
- `_emit` precedence and privacy masking rules are defined canonically in [JSON Query Syntax Reference (Canonical `_emit` Precedence Rules)](appendix/07-json-query-syntax-reference.html#canonical-emit-precedence-rules).
- **Reserved leading characters (normative):** domain field names starting with `@` or `_` are not allowed in the IS-Model. These prefixes are reserved for system/query semantics and cannot be used as user-defined field names. In accordance with the API's lenient processing mode, if a client submits a payload containing custom fields with these reserved prefixes, the fields are ignored and an `X-Warning` header is emitted. They are not strictly rejected with a `400 Bad Request`.
