Skip to content

fix: normalize set_indexing mode (lowercase key) and empty target ([]) for Data API#414

Open
erichare wants to merge 3 commits into
mainfrom
fix-set-indexing-empty-target
Open

fix: normalize set_indexing mode (lowercase key) and empty target ([]) for Data API#414
erichare wants to merge 3 commits into
mainfrom
fix-set-indexing-empty-target

Conversation

@erichare

@erichare erichare commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

CollectionDefinition.set_indexing had two related normalization bugs that produced an
indexing payload the Data API does not expect. CollectionDefinition.as_dict() forwards
the indexing value verbatim (only outer None/{} entries are filtered), so a malformed
dict reaches the wire. The Data API expects {"allow"|"deny": [<paths>]} — a lowercase
mode key mapping to a list of paths (as the attribute's own docstring states).

Bug 1 — target not normalized (value)

It computes a normalized local _i_target = indexing_target or [], but then builds the
returned definition from the raw indexing_target:

_i_target: list[str] = indexing_target or []
return CollectionDefinition(
    ...
    indexing={indexing_mode: indexing_target},   # raw param, not _i_target
    ...
)

So _i_target was dead code, and set_indexing("deny") / set_indexing("allow") with no
target stored {mode: None} instead of the intended {mode: []}.

Bug 2 — mode not normalized (key)

The mode is validated case-insensitively (_i_mode = indexing_mode.lower(), checked
against INDEXING_ALLOWED_MODES = {"allow", "deny"}), but the dict is then keyed by the
raw indexing_mode:

_i_mode = indexing_mode.lower()
if _i_mode not in INDEXING_ALLOWED_MODES: ...
return CollectionDefinition(
    ...
    indexing={indexing_mode: ...},   # raw param, not _i_mode
    ...
)

So set_indexing("DENY") / set_indexing("Allow") pass validation but store
{"DENY": ...} / {"Allow": ...} instead of the lowercase {"deny": ...} / {"allow": ...}
the Data API expects.

Fix

Construct the definition from the already-computed normalized locals:
indexing={_i_mode: _i_target}. Both fixes simply activate normalization that was already
written (.lower() and or []) but never wired into the returned object.

Behavior

Before:

set_indexing("deny")  -> {'deny': None}
set_indexing("DENY")  -> {'DENY': []}    # after the target fix: still a wrong-cased key

After:

set_indexing("deny")  -> {'deny': []}
set_indexing("DENY")  -> {'deny': []}

Explicitly-provided targets are preserved; only their normalization (key case, empty value)
changes.

Notes

  • Both are pre-existing bugs tracing to the original implementation; not introduced by any
    open PR. Same class of latent "normalize-then-discard" defect.
  • Scope deliberately minimal: only the set_indexing constructor arguments change.

Test plan

  • test_set_indexing_empty_target_normalization{"deny": []} / {"allow": []} for
    both .indexing and .as_dict(), plus an explicit-target guard.
  • test_set_indexing_mode_case_normalization"DENY" / "Allow" normalize to
    lowercase keys for both .indexing and .as_dict(), including with an explicit target.
  • uv run pytest tests/base/unit/test_collection_options.py — 4 passed
  • ruff check + ruff format --check (astrapy + tests) — clean
  • mypy astrapy tests — no issues found (214 source files)
  • CHANGES updated under the main section (covers both normalizations)

erichare added 2 commits June 5, 2026 11:44
CollectionDefinition.set_indexing computed a normalized `_i_target`
(indexing_target or []) but then built the returned definition from the
raw `indexing_target`. As a result, set_indexing("deny") / ("allow")
with no target stored {mode: None} instead of the intended {mode: []},
leaving `_i_target` as dead code.

as_dict() forwards this value verbatim into the request, so the
malformed {"indexing": {"deny": None}} reached the Data API, which
expects a list of paths. Wire the existing _i_target normalization into
the returned definition and add a unit test covering both modes.
set_indexing validates the indexing mode case-insensitively
(`_i_mode = indexing_mode.lower()`) but built the returned definition
from the raw `indexing_mode`. So set_indexing("DENY") / ("Allow")
passed validation yet stored {"DENY": ...} / {"Allow": ...} instead of
the intended lowercase {"deny": ...} / {"allow": ...}.

as_dict() forwards the key verbatim into the request, so the
unexpected-cased key reached the Data API, which expects "allow"/"deny".
Use the normalized `_i_mode` for the stored key and add a unit test
covering case normalization for both modes.
@erichare erichare changed the title fix: normalize empty set_indexing target to [] instead of None fix: normalize set_indexing mode (lowercase key) and empty target ([]) for Data API Jun 5, 2026
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.

1 participant