# Enriching a flow

> Goal: set a header, rewrite a path, add a parameter - without touching the upstream or the gateway's code.

Guide - Since version 0.1.0 - Flexway v1.6.3 - https://flexway.io/en/docs/operations-policies/

> Operations guide: configure, deploy and observe the gateway. What the capability does, product side: [product guide](https://flexway.io/en/docs/product-overview/); calling it: [developer guide](https://flexway.io/en/docs/developer-overview/).

**Goal**: set a header, rewrite a path, add a parameter - without touching the upstream or the
gateway's code.

**Command** - a named policy in `flexway.yaml`, attached in the contract to **one** operation:

```yaml
policies:
  definitions:
    enrich:
      type: header-inject
      when: "has(auth.subject)"            # CEL: authenticated callers only
      config:
        set:
          x-plan: "{{ auth.claims.plan }}"   # a CEL expression between braces
        on_missing: skip                     # skip | empty | fail - required as soon as a value is an expression
```

```yaml
paths:
  /orders/{id}:
    get:
      x-flexway-policies: [enrich]           # this operation, and this one only
```

Levels add up - `policies.chains.global`, `.hosts.<host>`, `.routes."/orders/**"`, then the
operation - and **the most precise wins**. `!name` removes an inherited policy:
`x-flexway-policies: ["!enrich"]`.

**Check**: the upstream receives `x-plan` on `GET /orders/{id}` and on no other operation; `/config`
(admin port) shows each operation's chain under `effective_chains`. A policy writing a protected
header - `Authorization`, `X-Forwarded-*`, tracing, the tenant header - **prevents the start**.

> The types of `0.1.0`: `header-inject`, `header-remove` (request, response, refusal),
> `rewrite-path`, `query-inject`, `query-remove`. None reads the body: a stream stays unbuffered.

**A value that is not text** in a `{{ … }}` template (`header-inject`, `query-inject`) always
renders the same way:

| Value | Rendering |
| --- | --- |
| Text | As is; `null` or absent: nothing (then `on_missing` if the expression fails) |
| Number, boolean | Decimal, never in exponent form (`1700000000`, not `1.7e+09`); `true`, `false` |
| List | Elements separated by a comma, the list syntax of HTTP fields (RFC 9110 §5.6.1): `["seller","admin"]` -> `seller,admin`; an empty or `null` element left out; a list or object element as JSON |
| Object | Compact JSON, keys sorted |

For another delimiter, `join` - the function of CEL's strings extension, the one Kubernetes enables:
`"{{ auth.claims.roles.join(' ') }}"` -> `seller admin`, `join()` with no delimiter. A list holding
anything but text makes `join` fail: the value is missing, `on_missing` decides. An unknown function
**refuses the load**. An element holding a comma is not quoted: pick another delimiter with `join`.
