Conversation
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
Thanks for opening this PR! Could you please take a look at Claude's review comments and either address the points it raised or reply with a short explanation where you think a comment doesn’t apply? That context would be really helpful for the review. Thanks! |
…eaning (fixes netbox-community#22750) * Perform Script input validation and convert ObjectVar/MultiObjectVar IDs -> model instances in extras/api/views.py: ScriptViewSet.post instead of in ScriptJob.run. * Return HTTP 400 for invalid script input (form errors) so API clients receive immediate feedback instead of enqueuing failing background jobs. * Revert ScriptJob.run to its original behavior so UI, management command, and EventRule callers keep their existing contracts (avoids breaking event-driven scripts). * Explicitly pop known execution parameters (_schedule_at, _interval, _notifications, _commit) rather than generically stripping underscore-prefixed keys. * Preserve uploaded files by adding them to cleaned data when the form did not claim them (so legacy scripts that expect files remain compatible). * Update unit tests to reflect the new contract (ScriptJob.run receives cleaned data). * Adds recommended follow-up tests (API integration, EventRule, MultiObjectVar) as follow-ups. Fixes: netbox-community#22750 Portions of this PR (initial code and tests) were drafted with assistance from an AI assistant (GitHub Copilot).
|
@pheus Taking Claude's review into consideration, I recrafted the code to do the conversion in the REST API view (as suggested), reverting the changes to ScriptJob.run |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
arthanson
left a comment
There was a problem hiding this comment.
Thanks @mburggraf. It looks like you are on the right track here, but note the CI tests are failing.
Moving the conversion into the API view looks correct. The problem is how the execution parameters (commit, schedule_at, interval, notifications) are read: pulling them out of the form instead of the request means the form's defaults win, so commit is always False. This means scripts silently do nothing and scheduling is dropped. An existing test already catches this, which is why CI is failing.
Several suggested things to fix it: I think you can read those four parameters from validated_data instead of the form
Also clean up the CodeQL/error-format items. As noted in by the code scans - the two new tests in test_jobs.py don't execute any changed code - they re-implement the view's form-building/pop/merge logic inline and call runner.run(). Nothing imports ScriptViewSet, so they pass regardless of what views.py
Added new method which is called by extras/views.py and extras/api/views.py so these won't differ. Added correct tests.
|
@claude /review Only report on issues of potential concern; we don't need commentary on anything that doesn't require further consideration. |
|
Claude finished @arthanson's task in 5m 10s —— View job Review: #22861 — Convert raw IDs to ObjectVars/MultiObjectVars
OverviewThe API The approach is right. The items below are the ones I think still need consideration. Issues1. Non-field form errors are silently swallowed → empty-body 400 — errors = {k: v for k, v in form.errors.items() if not k.startswith('_')}
raise ValidationError(errors)Django keys non-field errors under The explicit tuple two lines below is the correct filter — reuse it rather than a prefix heuristic: EXEC_PARAMS = ('_commit', '_schedule_at', '_interval', '_notifications')
errors = {k: v for k, v in form.errors.items() if k not in EXEC_PARAMS}
if not errors:
errors = {'data': form.errors.get(NON_FIELD_ERRORS, [_('Invalid script input.')])}2. Undeclared payload keys are now silently dropped — backwards-incompatible —
Two things worth deciding explicitly: (a) should unknown keys 400 rather than be dropped silently, and (b) this needs a note in the release notes / 3. Form errors are returned as top-level response keys, colliding with execution parameters — Serializer errors ( raise ValidationError({'data': errors})4. Test gaps on the exact regression that was flagged — The new tests cover 5. Script.python_class = self.TestScriptClassThis permanently replaces the model's Minor
|
arthanson
left a comment
There was a problem hiding this comment.
@mburggraf looks like it needs a few more fixes from the last changes:
Item 1 the 400 issue looks like the biggest issue - get_vars() (extras/scripts.py:451-473) applies no name filtering, so startswith('') swallows both NON_FIELD_ERRORS ('all') and any script variable legitimately named _foo. raise ValidationError({}) gives DRF self.detail = {} → 400 with an empty body.
2 and 3 I think are more minor, you can check. 4 can do with another couple tests for completeness. Also 5 looks like an easy one-liner.
- Replace the '_' prefix heuristic for filtering ScriptForm errors with an
explicit exclusion list (EXEC_PARAM_FIELDS). The heuristic also stripped
Django's NON_FIELD_ERRORS ('__all__'), so a pure form-level error (e.g.
ScriptForm.clean()'s "Scheduled time must be in the future.") resulted
in an empty 400 response body
- Nest script-variable errors under 'data' so they can't collide with
ScriptInputSerializer's own top-level fields (commit, interval, ...)
- Use input_serializer.is_valid(raise_exception=True) for consistency
with the rest of the method
- Clarify the "script class could not be loaded" error message
- Move EXEC_PARAM_FIELDS to extras/scripts.py as the single source of
truth for both the API view and the runscript command; drop
prepare_script_form from extras.scripts.__all__ as internal plumbing
- Fix runscript only popping 3 of 4 internal exec fields from
cleaned_data, leaking '_notifications' into the script's own data
- Restore Script.python_class via patch.object()/addCleanup() in
ScriptRunExecutionTestCase instead of a permanent override
- Add test coverage for schedule_at/interval forwarding and for
rejecting a nonexistent object ID
|
@arthanson: Fixed the mentioned things. About 2: I'd say the backwards incompatibility is intentional and should be mentioned in the changelog. |
|
Closing this as opening #23119 off of these changes to resolve merge conflicts and add doc string. |
Closes: #22750
Raw IDs from API calls are now converted to Objects, using the scripts form.
Note, that this may also taint execution as event handlers, so I like to point to feature request #21619.