devYRPauli opened a new pull request, #74885:
URL: https://github.com/apache/airflow/pull/74885
closes: #61594
A Dag author cannot make the options of one drop-down depend on the value of
another param. This PR adds two schema keys for a param: `depends_on` and
`options_map`. The `Param` class does not change. The keys pass through to the
schema, like `values_display` and `section`.
```python
"country": Param("Germany", type="string", enum=["Germany", "France"]),
"city": Param(
"Berlin",
type=["null", "string"],
depends_on="country",
options_map={"Germany": ["Berlin", "Munich"], "France": ["Paris",
"Lyon"]},
),
```
Changes:
- UI: `FieldSelector` shows a param with `depends_on` as a drop-down.
`FieldDropdown` reads the options from `options_map` for the current parent
value. When the child value is not an option for the parent value, the form
sets the child value to null. This happens after a parent change, and also on
load for a default that does not match. A chain such as country -> region ->
city works, because each child reads only its own parent.
- Backend: `SerializedParamsDict.validate()` and the Task SDK
`ParamsDict.validate()` check that the child value is an option for the parent
value. A REST API or CLI trigger with a mismatched pair fails with a
`ParamValidationError`. A null child value passes the pair check. The `type` of
the child must still allow null.
- The serialized Dag stores the keys of `options_map` with `str()`. The UI
and the Task SDK look up the parent value with the same `str()` form. The
parent value must be a string, an integer, a boolean or null. The docs say this.
- Docs: a new entry in `params.rst` next to `values_display`.
- Example: `country` and `city` params in `example_params_ui_tutorial.py`.
The issue asks to reset the child to the first option. This PR resets it to
null. A required child then shows the "required" error. The user must pick the
value, so the form never submits a value the user did not choose.
I did not use JSON Schema `dependentSchemas`. Airflow validates each param
against its own schema, and there is no Dag-level object schema for a
conditional to attach to.
Two effects of the pair check on a Dag that uses `depends_on`:
- A REST API or CLI trigger that sends only the parent fails when the child
default does not fit the new parent value.
- A scheduled Dag with a default pair that does not match fails to parse.
The error is the existing one for invalid default values.
Limits:
- In a HITL form the filtering is UI-only, because
`HITLOperator.validate_params_input` does not call `validate()`. I did not test
the HITL form.
- A child with more than one non-null type and no `enum` shows the
multi-type field, not the drop-down. The backend still checks the pair.
Tests:
- `airflow-core/tests/unit/serialization/definitions/test_param.py`: valid
and invalid pairs after a full serialize and deserialize round trip, a null
child, a null parent, integer keys, and an unknown parent.
- `task-sdk/tests/task_sdk/definitions/test_param.py`: valid and invalid
pairs, integer keys, and `suppress_exception`.
- `FieldDropdownDependsOn.test.tsx`: options for the current parent, the
reset on a parent change, a three-level chain, and a boolean parent.
With the pair check replaced by a no-op, all 9 new Python test cases fail. I
also reverted each of the 4 UI changes one at a time, and each revert made a
new UI test fail.
Checks I ran:
| command | result |
|---|---|
| `pytest airflow-core/tests/unit/serialization` | 1218 passed, 1 skipped |
| `pytest airflow-core/tests/unit/models/test_dag.py
airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py -k
"param or conf"` | 386 passed |
| `pytest task-sdk/tests/task_sdk/definitions` | 1000 passed, 3 failed |
| `pytest airflow-core/tests/unit/always/test_example_dags.py -k
params_ui_tutorial` | 3 passed |
| `pnpm exec vitest run` (UI) | 1943 passed |
| `tsc`, `eslint`, `prettier` (UI) | clean |
| `mypy` on the 3 changed Python source files | clean |
The 3 Task SDK failures are
`TestParam::test_string_datetime_invalid_format[date, time, rfc2822-datetime]`.
They also fail on `main` in my environment.
I also ran `airflow standalone` with the tutorial Dag:
- A REST trigger with `country=France, city=Paris` creates the Dag run.
- A REST trigger with `country=France, city=Berlin` returns HTTP 400:
`Invalid input for param city: 'Berlin' is not an option for country='France'`.
Screenshots (new field, so there is no before state; without this change the
form shows `city` as a text input):
SCREENSHOT: after-germany-city-options.jpg - Country Germany, City offers
Berlin and Munich
SCREENSHOT: after-france-city-cleared.jpg - Country changed to France, City
is cleared
SCREENSHOT: after-france-city-options.jpg - City offers Paris and Lyon
---
##### Was generative AI tooling used to co-author this PR?
- [X] Yes (please specify the tool below)
Generated-by: Claude Code CLI with Opus 5.5 following [the
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
---
* Read the **[Pull Request
Guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#pull-request-guidelines)**
for more information. Note: commit author/co-author name and email in commits
become permanently public when merged.
* For fundamental code changes, an Airflow Improvement Proposal
([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvement+Proposals))
is needed.
* When adding dependency, check compliance with the [ASF 3rd Party License
Policy](https://www.apache.org/legal/resolved.html#category-x).
* For significant user-facing changes create newsfragment:
`{pr_number}.significant.rst`, in
[airflow-core/newsfragments](https://github.com/apache/airflow/tree/main/airflow-core/newsfragments).
You can add this file in a follow-up commit after the PR is created so you
know the PR number.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]