Skip to content

fix: treat server_group_ids=None as not supplied instead of iterating it - #577

Open
hackerboey wants to merge 1 commit into
zscaler:masterfrom
hackerboey:fix/guard-none-server-group-ids-ba-pra
Open

hackerboey wants to merge 1 commit into
zscaler:masterfrom
hackerboey:fix/guard-none-server-group-ids-ba-pra

Conversation

@hackerboey

Copy link
Copy Markdown

Fixes #575

Every ZPA application segment resource reshapes server_group_ids into the wire-shaped
serverGroups list behind a key-presence test:

if "server_group_ids" in body:
    body["serverGroups"] = [{"id": gid} for gid in body.pop("server_group_ids")]

The test is for presence, not for a usable value. A caller who forwards an optional keyword
whose default is None — the natural result of wrapping these methods — gets

TypeError: 'NoneType' object is not iterable

raised inside the SDK before any request is built. So a partial update that does not intend to
change server groups cannot be performed.

Scope

The pattern appears in five modules, so fixing one would leave the API inconsistent:

  • zscaler/zpa/application_segment.py
  • zscaler/zpa/app_segments_ba.py
  • zscaler/zpa/app_segments_ba_v2.py
  • zscaler/zpa/app_segments_pra.py
  • zscaler/zpa/app_segments_inspection.py

None is now treated as "not supplied". An explicitly-passed empty list is preserved as
serverGroups: [], since that is a meaningful instruction to detach all server groups and is
distinguishable from omission — please tell me if you would rather it were normalised.

Test

Adds tests/unit/test_zpa_app_segment_server_group_ids.py, matching the flat tests/unit/
convention. It asserts, per module, that None no longer raises and that passing a real list
still produces serverGroups exactly as before. The request executor is mocked — no credentials
or network access required.

33 passed on this branch. Reverting the source makes the None cases fail with the
TypeError above. ruff and black --check --line-length 127 clean.

The ZPA application segment resources reformatted server_group_ids into
the wire-shaped serverGroups list behind a key-presence test:

    if "server_group_ids" in body:
        body["serverGroups"] = [{"id": gid} for gid in body.pop("server_group_ids")]

Presence is not the same as usability. Forwarding an optional keyword
argument whose default is None — the natural shape of a partial update
that does not intend to change server groups — put the key in body with
a None value, so the comprehension raised

    TypeError: 'NoneType' object is not iterable

inside the SDK before any request was built. There was no way to perform
such a partial update at all: omitting the argument entirely required the
caller to branch on it and build kwargs conditionally.

Each site now pops the value first and only reformats when it is not
None, so None means "not supplied" and the snake_case key never leaks
into the request body. Callers passing a real list are unaffected, and an
explicitly supplied empty list is still sent as "serverGroups": [] —
these resources deliberately omit serverGroups when the caller does not
mention it, which makes [] the only way to clear the server groups on an
update, so it must keep its meaning.

Fixed at all eleven sites of the pattern, in both the create and update
paths of each resource:

- application_segment.py — add_segment, update_segment,
  add_segment_provision
- app_segments_ba_v2.py — add_segment_ba, update_segment_ba
- app_segments_ba.py — add_segment_ba, update_segment_ba
- app_segments_inspection.py — add_segment_inspection,
  update_segment_inspection
- app_segments_pra.py — add_segment_pra, update_segment_pra

tests/unit/zpa/test_app_segment_server_group_ids.py parametrizes all
eleven entry points over the three inputs: None is dropped, a real list
still produces serverGroups exactly as before, and [] is still sent. The
None cases are the eleven that failed beforehand; the other twenty-two
assertions pass both before and after and pin the unchanged behavior.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ZPA application segments: server_group_ids=None raises TypeError: 'NoneType' object is not iterable before any request is built

1 participant