---
name: create-ewah-airflow-connection
description: Create Airflow connections for EWAH DAGs by reverse-engineering hook files. Use when setting up an EWAH connection, figuring out conn_type and field mappings from hook source code, or configuring Airflow connections for Personio, Facebook, Google Ads, AWS, or Stripe EWAH operators.
disable-model-invocation: true
---

# Create EWAH Airflow Connection

Create Airflow connections required by EWAH DAGs running in a local Docker Compose setup. EWAH hooks define custom connection types with field mappings that must be reverse-engineered to create connections via the CLI.

## Context

EWAH hooks use custom Airflow connection types (e.g., `ewah_personio`, `ewah_shopify`). Each hook maps API credential names to standard Airflow connection fields via `_ATTR_RELABEL`, and may define extra fields via `get_connection_form_widgets()`. To create connections for EWAH DAGs, you must read the hook file and map credentials accordingly.

## Steps

1. Identify required connections from the DAG definition (e.g., `dags.yml`).

   Look for:
   - `dwh_conn_id` -- destination connection (often defaults to `dwh` from `base_config`)
   - `source_conn_id` -- source API connection (in `operator_config.general_config` or per-table overrides)

2. Check which connections already exist.

   ```bash
   docker exec <scheduler_container> airflow connections list 2>/dev/null | grep -E "<conn_id_1>|<conn_id_2>"
   ```

3. Read the EWAH hook file (`ewah/hooks/<connector>.py`) and inspect three things:

   **A. `conn_type` attribute** -- use as `--conn-type` (e.g., `ewah_personio`, `ewah_shopify`)

   **B. `_ATTR_RELABEL` dict** -- maps API credential names to standard Airflow fields. Reverse the mapping to know which `--conn-*` flag to use:

   | Airflow field (dict value) | CLI flag |
   |---|---|
   | `login` | `--conn-login` |
   | `password` | `--conn-password` |
   | `host` | `--conn-host` |
   | `schema` | `--conn-schema` |
   | `port` | `--conn-port` |

   **C. `get_connection_form_widgets()` method** -- defines extra custom fields beyond standard ones. These are passed via `--conn-extra` as a JSON string. Field names follow the pattern `extra__<conn_type>__<field_name>`.

4. Create the destination (DWH) connection if missing.

   Use `--conn-type ewah_postgres`. When running inside Docker, use `host.docker.internal` instead of `localhost`:

   ```bash
   docker exec <scheduler_container> airflow connections add <dwh_conn_id> \
     --conn-type ewah_postgres \
     --conn-host host.docker.internal \
     --conn-port <port> \
     --conn-login <user> \
     --conn-password <password> \
     --conn-schema <database>
   ```

5. Create the source API connection.

   For simple connectors (only `_ATTR_RELABEL`, no extra widgets):

   ```bash
   docker exec <scheduler_container> airflow connections add <conn_id> \
     --conn-type <hook_conn_type> \
     --conn-login <value_for_login_field> \
     --conn-password <value_for_password_field> \
     --conn-host <value_for_host_field> \
     --conn-schema <value_for_schema_field>
   ```

   For connectors with extra fields (from `get_connection_form_widgets()`):

   ```bash
   docker exec <scheduler_container> airflow connections add <conn_id> \
     --conn-type <hook_conn_type> \
     --conn-login <login_value> \
     --conn-password <password_value> \
     --conn-extra '{"extra__<conn_type>__<field1>": "<value1>", "extra__<conn_type>__<field2>": "<value2>"}'
   ```

6. Extract credential values from the dlt connector's secrets using `grep` (never read the full file):

   ```bash
   grep "client_id" dlt-connectors/connectors/<name>/.dlt/secrets.toml
   grep "client_secret" dlt-connectors/connectors/<name>/.dlt/secrets.toml
   ```

   Map the dlt secret names to Airflow fields using `_ATTR_RELABEL` (reversed) and `get_connection_form_widgets()`.

## Validation

- [ ] All required connections created
- [ ] Connection types match EWAH hook `conn_type`
- [ ] Field mapping matches `_ATTR_RELABEL` (reversed)
- [ ] Extra fields included if hook defines `get_connection_form_widgets()`

## Examples

**Common `_ATTR_RELABEL` patterns:**

| Connector | `_ATTR_RELABEL` | Meaning |
|---|---|---|
| Personio | `client_id -> login`, `client_secret -> password` | Simple: login + password |
| Facebook | `app_id -> login`, `app_secret -> password`, `api_version -> host`, `account_id -> schema` | All 4 standard fields repurposed |
| Google Ads | `client_id -> login`, `client_secret -> password`, `login_customer_id -> schema`, `api_version -> host` | All 4 fields + extra fields |
| AWS | `access_key_id -> login`, `secret_access_key -> password`, `region -> schema`, `role_arn -> host` | Non-obvious field usage |
| Stripe | `api_key -> password` | Password only |

**Common extra fields (`get_connection_form_widgets()`):**

| Connector | Extra fields | Purpose |
|---|---|---|
| Google Ads | `developer_token`, `refresh_token` | OAuth tokens |
| Google Analytics | `service_account_json` | Full JSON service account blob |
| Facebook | `access_token` | Long-lived access token |
| Salesforce | `security_token`, `client_id`, `client_secret` | OAuth + security token |
| Snowflake | `database`, `role`, `warehouse`, `private_key` | Connection details + key-pair auth |
| MongoDB | `conn_style`, `tls`, `ssl_cert`, `ssh_conn_id` | TLS/SSH tunneling config |
| Sharepoint | `site_hostname`, `site_path`, `drive_id`, `tenant_id` | Site navigation |

**Full example -- Google Ads connection:**

```bash
docker exec <scheduler_container> airflow connections add google_ads \
  --conn-type ewah_google_ads \
  --conn-login <client_id> \
  --conn-password <client_secret> \
  --conn-host <api_version> \
  --conn-schema <login_customer_id> \
  --conn-extra '{"extra__ewah_google_ads__developer_token": "<token>", "extra__ewah_google_ads__refresh_token": "<token>"}'
```
