rjgoyln commented on PR #71782:
URL: https://github.com/apache/airflow/pull/71782#issuecomment-5380889189

   > LGTM — approving. The fix is minimal, applied consistently to all three 
forwarding paths, and the tests pin exactly the behaviour that changes. 
Verified rather than read:
   > 
   > * The six new tests all fail on the pre-fix code and pass on `e2a90690`. On
   >   `DatabricksSubmitRunOperator` the pre-fix failure is verbatim the report 
in #71776:
   >   `AirflowException: Type <class 'NoneType'> used for parameter 
json[python_wheel_task][named_parameters][start_date_str] is not a number or a 
string`.
   >   All 250 tests in the operators file pass on this head.
   > * The asymmetry the description describes is real: 
`DatabricksCreateJobsOperator.execute` normalises
   >   at `:582` and injects at `:586`, and 
`DatabricksRunNowOperator._build_run_now_payload` normalises at
   >   `:1375` and injects at `:1390`, so in those two the `None` skipped local 
validation entirely, while
   >   `DatabricksSubmitRunOperator` injects at `:932` ahead of the 
`normalise_json_content` at `:945`.
   > 
   > One correction for the PR body, because the case is stronger than it 
currently reads. "The `None` reached the Databricks API as `null` instead of 
failing locally" can be read as "and Databricks accepted it". It does not. I 
sent both pre-fix shapes at a real workspace (throwaway job, deleted 
afterwards):
   > 
   > * `POST /api/2.2/jobs/create` with `parameters: [{"name": "probe_null", 
"default": null}]` →
   >   `Job–level parameters 'probe_null' is missing default value.`
   > * `POST /api/2.2/jobs/run-now` with `job_parameters: {"probe_null": null}` 
→
   >   `Could not parse request object: Expected both 'key' and 'value' to be 
set on elements in the field 'job_parameters'`
   > * The same two calls with a string value succeed (control).
   > 
   > So all three operators were genuinely broken by an unset nullable `Param`; 
the only difference was whether the user got the local `AirflowException` or an 
opaque API error. Worth a sentence, since it also settles that nobody could 
have been relying on the `null` being forwarded.
   > 
   > The fixed side is checked against the same workspace, not only against 
mocks: I had the post-fix operators build the payloads (`_prepare_submit_json` 
and `_build_run_now_payload`, real code, hook mocked only as transport) and 
sent those exact payloads to the live API.
   > 
   > * `runs/submit` accepted it, and the run came back with `base_parameters: 
{"env": "prod"}` — the null
   >   param gone, the other one forwarded.
   > * `run-now` accepted it, and the run's `job_parameters` came back as
   >   `[{"default": "job_default_env", "name": "env", "value": "prod"}, 
{"default": "job_default_date", "name": "start_date_str"}]`
   >   — the skipped param carries no `value`, so the job-level default applies 
to it. That is the behaviour
   >   I would want from "there is no value to forward", now observed rather 
than assumed.
   > 
   > On whether this loosens payload validation: it does not, and I think the 
split is the right one. Measured on this head, an explicit 
`json={"notebook_task": {"base_parameters": {"x": None}}}` still raises 
`AirflowException`; only auto-forwarded params are dropped. Auto-forwarding is 
something the operator does on the user's behalf, so it should degrade quietly, 
whereas a null the user wrote themselves is a request Databricks cannot express 
and deserves the error. Dropping `None` while building a provider payload is 
also an established Airflow idiom — core's `prune_dict` 
(`utils/helpers.py:259`), amazon's `trim_none_values`, and this provider 
already does the same inline in `hooks/databricks_sql.py:282`.
   > 
   > Three line-level notes left inline: one to protect the helper from a 
future "simplification", one question about the `jobs/create` case, and one 
doc-wording nit.
   > 
   > Drafted-by: Claude Code (Opus 5); reviewed by @moomindani before posting
   
   Thanks for the thorough review, and especially for testing the payloads 
against the actual Databricks API. That was very helpful!
   
   Here’s how I addressed the points:
   
   *   **Docs/Wording** — Fixed in 350955d across all three pages. `resolves to 
None` is more accurate for both explicit `null` and `NOTSET`.
   *   **`dict(params)` & nested `None`** — Added a comment explaining the 
`dict()` round-trip. I agree that handling nested `None` is better left out of 
this PR, since Databricks expects `Dict[str, str]` here.
   *   **CreateJobs** — I kept the `skip` behavior to avoid silently turning an 
unset value into an empty string. Since the job definition is rebuilt from JSON 
on each run, I also prefer keeping the behavior consistent across all three 
operators. 
   
   That said, I’m happy to switch to `default: ""` if you think that would be 
safer. Thanks again for the great review!


-- 
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]

Reply via email to