Skip to content

Fix: validate automation config keys - #2563

Merged
Flix6x merged 6 commits into
mainfrom
fix/2498-validate-automation-config-keys
Sep 25, 2026
Merged

Flix6x merged 6 commits into
mainfrom
fix/2498-validate-automation-config-keys

Conversation

@BelhsanHmida

@BelhsanHmida BelhsanHmida commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Description

  • A mistake in a data generator's config was reported against parameters, for forecast and report automations alike, sending the caller looking for a mistake in a part of the request that was fine. The create endpoint wrapped every ValidationError from the service as {"parameters": ...}, discarding which schema had failed.
  • A scheduling automation accepted config and data-generator with 201 Created and silently ignored both. It now refuses each by name with a 422: a schedule automation's scheduler, and the flex config it runs under, follow from the asset and its flex context, so accepting either would record a choice that nothing goes on to read.
  • A report automation without a reporter raised a bare string, naming no field. It is now reported under data-generator.
  • An unsupported automation type is now reported under type.
  • Added an errors_reported_for(section) context manager in flexmeasures/data/services/automations.py, which re-raises a ValidationError keyed by the section it came from, and wrapped each raise site with the section it belongs to: the window check, the forecast and report parameter loads, the forecast window resolution, the schedule trigger message and the schedule sensor resolution report against parameters; the forecaster and reporter setups report against config.
  • Three error paths that reached the caller unkeyed are now keyed as parameters: the unknown-field error from AssetTriggerSchema (raised inside resolve_schedule_automation_sensors, whose docstring already promised the caller would report it against the parameters), the bare messages from validate_automation_window, and the offset and window errors from resolve_automation_window, which was evaluated as an argument and so sat outside the wrapper.
  • flexmeasures add automation now reports "Invalid automation:" instead of "Invalid parameters:", since the fault is not always in the parameters, and the keyed messages now say where it is.
  • The scheduling rejection lives in refuse_fields_a_schedule_automation_cannot_use, so that create_automation stays within the C901 complexity budget.

This is not a breaking change. A client parsing message.json.parameters will find config errors under message.json.config, but the automation endpoints landed in v3.0-37 and v3.0-38, both inside the unreleased v1.1.0, so no released client can be parsing this shape.

The issue predates #2297 and #2536 and is partly out of date: unknown parameters keys were already rejected with a field-specific 422, with nothing persisted. Criterion 6 is covered by a new test rather than by new code.

  • Added changelog item in documentation/changelog.rst

Look & Feel

Before, a forecast automation with a bad config key:

{"message": {"json": {"parameters": {"not-a-config-field": ["Unknown field."]}}}}

After:

{"message": {"json": {"config": {"not-a-config-field": ["Unknown field."]}}}}

Before, a schedule automation sent a config and a data-generator:

201 Created

After:

{"message": {"json": {
  "config": ["A schedule automation configures no data generator of its own: its scheduler and flex config follow from the asset and its flex context."],
  "data-generator": ["A schedule automation does not choose a data generator: its scheduler follows from the asset."]
}}}

Before, a report automation with no reporter:

{"message": {"json": {"parameters": ["A reporter is required for report automations (e.g. PandasReporter)."]}}}

After:

{"message": {"json": {"data-generator": ["A reporter is required for report automations (e.g. PandasReporter)."]}}}

On the command line:

$ flexmeasures add automation --asset 1 --name "Bad forecaster config" --cron "0 6 * * *" --sensor 1 --config config.yml
Invalid forecast automation: {'config': {'not-a-config-field': ['Unknown field.']}}
Aborted!

How to test

pytest flexmeasures/api/v3_0/tests/test_automations_api.py
pytest flexmeasures/cli/tests/test_automations.py
pytest flexmeasures/data/tests/

New tests:

  • test_post_automation_reports_a_config_error_against_the_config — forecasting and reporting; the error is keyed config and no parameters key is present.
  • test_post_automation_reports_a_parameter_error_against_the_parameters — forecasting, reporting and scheduling; the error is keyed parameters and no config key is present.
  • test_post_schedule_automation_rejects_a_data_generator_and_its_config — each field is refused by name, and nothing is persisted.
  • test_post_report_automation_without_a_reporter_names_the_field_to_fill_in — keyed data-generator.
  • test_a_refused_automation_leaves_nothing_behind — three failing requests leave the Automation, DataSource and AssetAuditLog row counts unchanged.
  • test_add_forecast_automation_reports_a_config_error_against_the_config — the CLI equivalent.

Updated: seven CLI tests asserted the old wording, "Invalid parameters", which contradicts itself once the messages can name the config instead. They now assert the new wording and the key the message is reported under.

Each new test was proven to fail before it was called done:

What was broken What went red
The endpoint re-wraps as {"parameters": e.messages} again the two config tests, the two schedule-rejection tests and the reporter-less test
errors_reported_for("parameters") renamed to "mislabelled" all three parameter tests, including the scheduling one
The CLI string put back to "Invalid <noun> parameters" all eight CLI tests
An audit record written before validation, and committed on the way out of the 422 test_a_refused_automation_leaves_nothing_behind

Each break was restored and the suite re-run.

Full suites, run one at a time: flexmeasures/api/v3_0/tests/ 518 passed; flexmeasures/cli/tests/ 234 passed, 1 xfailed; flexmeasures/data/tests/ 389 passed.

Further Improvements

Merge order: #2554 should merge first. Main's "New automation" form shows the Data generator and Config fields for every automation type; #2554 gates them so that a schedule automation sends neither. Without that gating, this PR's new rejection turns leftover text in a hidden field into a 422 as soon as the user picks scheduling.

The API changelog section here is numbered against #2554's v3.0-38. If this PR lands first, renumber it to v3.0-38.

Related Items

Closes #2498.


Sign-off

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on code under GPL or other license that is incompatible with FlexMeasures

… is at fault

Context:
- Issue #2498: a mistake in a data generator's config was reported against
  "parameters", sending the caller looking in a part of the request that was fine.
- A scheduling automation accepted a config and a data-generator with 201 Created,
  and silently ignored both.
- A report automation without a reporter raised a bare string, naming no field at all.

Change:
- Added an `errors_reported_for(section)` context manager, which re-raises a
  ValidationError keyed by the section it came from.
- Wrapped each raise site with the section it belongs to: the window check, the
  forecast and report parameter loads, the schedule trigger load and the schedule
  sensor resolution report against "parameters"; the forecaster and reporter setups
  report against "config".
- A scheduling automation now refuses a non-empty config or data-generator by name,
  in `refuse_fields_a_schedule_automation_cannot_use`: its scheduler and flex config
  follow from the asset.
- A missing reporter is reported against "data-generator", and an unsupported
  automation type against "type".
- The create endpoint returns the service's messages as they are, rather than
  re-wrapping every one of them as {"parameters": ...}.
- The CLI reads "Invalid <noun> automation:", since the fault is not always in the
  parameters, and the keyed messages now say where it is.

Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context:
- The create endpoint and `flexmeasures add automation` now key each validation
  error by the part of the request it came from (issue #2498).
- Seven CLI tests asserted the old wording, "Invalid <noun> parameters", which
  contradicts itself once the messages can name the config instead.

Change:
- Added API tests: a config error is keyed "config" and a parameter error
  "parameters" (forecasting and reporting, plus scheduling for the parameters);
  a scheduling automation refuses "config" and "data-generator" by name; a report
  automation without a reporter is keyed "data-generator"; and three refused
  requests leave the automation, data source and audit log counts unchanged.
- Added a CLI test for a forecaster config error keyed "config".
- Updated the CLI tests that asserted the old wording to assert the new one, plus
  the key the message is now reported under.

Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
…t fault

Context:
- PR #2563 changes which key an automation's validation errors are reported under,
  which an API client reads.

Change:
- Added an entry to the "Automations, in detail" section of the main changelog,
  rather than to Bugfixes, since automations have not shipped in a release yet.
- Added a v3.0-39 section to the API change log, saying explicitly that this is not
  a breaking change, as the automation endpoints are not part of a release.
- Numbered v3.0-39 against PR #2554's v3.0-38, which merges first.

Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context:
- PR #2542 landed on main, and adds `_stored_sensor_id` to
  `flexmeasures/data/services/automations.py` in the same place this branch adds
  `refuse_fields_a_schedule_automation_cannot_use` and `errors_reported_for`.

Change:
- Resolved the add/add conflict by keeping both sides: this branch's two helpers,
  then main's `_stored_sensor_id`, which main placed just above
  `_prepare_forecast_automation`.
- Everything else merged cleanly, including `_prepare_forecast_automation`, where
  main's SensorReference unwrap now follows this branch's wrapped parameter load.

Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
@BelhsanHmida
BelhsanHmida marked this pull request as ready for review September 21, 2026 11:55
Context:
- Main moved 8 commits ahead, among them PR #2554, which this branch waited on:
  it gates the "New automation" form's data generator and config fields by type.

Change:
- Resolved three add/add conflicts by keeping both sides.
- documentation/api/change_log.rst: this branch's v3.0-39 above #2554's v3.0-38,
  which is the numbering this branch had already assumed.
- documentation/changelog.rst: both entries in "Automations, in detail", #2554's
  first, following the order they merged in.
- flexmeasures/api/v3_0/tests/test_automations_api.py: both sides appended their
  own tests, with no overlapping names, so both sets are kept.
- No production code conflicted.

Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
@BelhsanHmida

Copy link
Copy Markdown
Contributor Author

Tested this locally,

Each automation type now names the part of the request at fault. A bad key in a forecaster's or a reporter's config comes back under config, where it used to come back under parameters. A bad parameter key, a negative duration and a window given as all three of start-offset, end-offset and duration all come back under parameters, the last two of which used to arrive with no field named at all. A report automation that names no reporter comes back under data-generator rather than as a bare string.

A schedule automation refuses a config and a data-generator by name, singly and together, where it used to answer 201 Created and quietly ignore both. Sending neither still creates the automation, so the ordinary path is untouched; with #2554 in, the form gates both fields by type and never sends either for a schedule.

The CLI reports the same keys, and --data-generator --type scheduling is still caught by the usage check before the service sees it.

Permissions still come first: a bad config on an asset I may not write to answers 403, with nothing in the response about the asset. And a refusal leaves nothing behind the automation, data source and audit log counts are unchanged across the failing requests.

@BelhsanHmida
BelhsanHmida requested a review from Flix6x September 22, 2026 21:33

@Flix6x Flix6x left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only request to keep changelog verbosity at bay.

Comment thread documentation/changelog.rst Outdated
Comment thread documentation/api/change_log.rst Outdated
Automations are unreleased, so drop this PR's own bullet from "Automations, in detail", append its number to the bullet that already covers creating automations, and drop the v3.0-39 API change log section.

Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
@Flix6x
Flix6x merged commit 6dd0460 into main Sep 25, 2026
13 checks passed
@Flix6x
Flix6x deleted the fix/2498-validate-automation-config-keys branch September 25, 2026 05:34
Flix6x added a commit that referenced this pull request Sep 25, 2026
Main's automation config validation (#2563) names the part of a request each error came from, which the plugin payload loader does too, so the API passes those messages on as they are.
A plugin-defined type is not in the static mapping of result nouns, so the refusal of a fixed moment, and the CLI's message about an invalid automation, ask the handler what its automation produces.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129WrXeJ5gia2pctFH93BqC
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate automation config and parameter keys before persisting

2 participants