Skip to content

Add /collections endpoint - #32

Merged
bradwindy merged 3 commits into
mainfrom
add-collections-endpoint
Jul 9, 2026
Merged

bradwindy merged 3 commits into
mainfrom
add-collections-endpoint

Conversation

@bradwindy

@bradwindy bradwindy commented Jul 9, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Registers a GET /collections route (Swift handler switch + SAM HttpApi event) that returns the list of collection names from NZImageApi.collectionImageCounts.
  • Adds exclude support to GET /image: a comma-separated exclude query param filters those collections out of the random pick and re-normalizes weights over the remaining set (NZImageApi.filteredCollectionWeights), rather than subtracting counts and leaving weights unnormalized.
  • Adds creator/date/subject to NZRecordsResult (decoded straight from the existing DigitalNZ response shape; no new fetch/mapping needed) for a fuller "more info" view.

Why

nzimage-web's Settings modal already calls /api/collections to populate the "Hidden Collections" list, and its NZImage model already parses creator/date/subject/exclude — none of that has ever worked in production because this repo's main never had a /collections route or exclude/richer-metadata support. The work existed on a stale, unmerged branch (settings-metadata-and-exclude, diverged 2026-07-06/07) that predates several main changes since then (the swift-aws-lambda-events v1 upgrade making queryStringParameters non-optional, the AL2023 runtime migration, and the code-review security fixes). This PR rebases just the endpoint/feature work (2 commits: Add /collections endpoint, exclude-filtered image picks, and richer metadata and Register the /collections route on the HTTP API) onto current main, fixing one resulting compile break (a leftover ? on queryStringParameters that's no longer optional).

Test plan

  • swift build succeeds
  • swift test — all 131 tests pass (including the new FilteredCollectionWeightsTests)
  • After merge/deploy, confirm nzimage-web's Settings → Hidden Collections list populates in production

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a new GET /collections endpoint to list available collections.
    • Enhanced image requests to support excluding one or more collections during random selection.
    • Returned record metadata now includes additional details such as creator, date, and subject when available.
  • Bug Fixes
    • Improved collection selection behavior, including proper rebalancing after exclusions and a graceful fallback when no eligible collections remain.

bradwindy added 2 commits July 9, 2026 23:41
…etadata

Lets the frontend list collections to hide and exclude them from random
picks (weights re-normalized over the remaining set, not just filtered
client-side), and surfaces creator/date/subject for a fuller "more info"
view.
The Swift handler already switched on it, but SAM's HttpApi only wires
up explicitly declared routes -- without this the API Gateway itself
404s before the request ever reaches the Lambda.
@coderabbitai

coderabbitai Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1c2842ca-a64a-4d66-a574-11611434a84f

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9a4eb and 6c5a1f2.

📒 Files selected for processing (1)
  • Sources/NZImageApiLambda/NZImageApi.swift
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • bradwindy/RichError (auto-detected)

📝 Walkthrough

Walkthrough

Adds collection exclusion support to random image selection, a new /collections route, expanded NZRecordsResult metadata fields, and tests for weight filtering and exclusion behavior.

Changes

Collection Exclusion Feature

Layer / File(s) Summary
Weight override in data source
Sources/NZImageApiLambda/Data Sources/DigitalNZAPIDataSource.swift
newResult accepts an optional weightsOverride used for random collection selection instead of always using init-captured collectionWeights.
filteredCollectionWeights and image exclude parameter
Sources/NZImageApiLambda/NZImageApi.swift
Adds NoEligibleCollectionsError and filteredCollectionWeights(excluding:) to renormalize weights after exclusion; image accepts exclude and passes computed weightsOverride to the data source.
Lambda routing for /collections and exclude parsing
Sources/NZImageApiLambda/NZImageApiLambda.swift, template.yaml
Adds a GET /collections route returning collection names as JSON, parses exclude for /image (overridden by explicit collection), and declares the new route in the SAM template.
NZRecordsResult metadata fields
Sources/NZImageApiLambda/Models/NZRecordsResult.swift
Adds optional creator, date, and subject fields with initializer, CodingKeys, and customDescription() updates, alongside sourceUrl.
Filtered weights tests
Tests/NZImageApiLambdaTests/FilteredCollectionWeightsTests.swift
New XCTest suite covers weight normalization, proportional redistribution, all-excluded error, and empty-exclude behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant NZImageApiLambda
  participant NZImageApi
  participant DigitalNZAPIDataSource
  Client->>NZImageApiLambda: GET /image?exclude=a,b
  NZImageApiLambda->>NZImageApi: image(collection: nil, exclude: [a,b])
  NZImageApi->>NZImageApi: filteredCollectionWeights(excluding: [a,b])
  NZImageApi->>DigitalNZAPIDataSource: newResult(weightsOverride: filteredWeights)
  DigitalNZAPIDataSource-->>NZImageApi: NZRecordsResult
  NZImageApi-->>NZImageApiLambda: NZRecordsResult
  NZImageApiLambda-->>Client: JSON response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the new /collections endpoint, which is one of the main changes in the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-collections-endpoint
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch add-collections-endpoint

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/NZImageApiLambda/NZImageApi.swift (1)

119-133: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

NoEligibleCollectionsError is logged as an "unexpected error."

Since NoEligibleCollectionsError doesn't conform to RichError, it falls into the generic else branch and gets logged as "An unexpected error occurred: ...", even though the docstring on Line 99-100 frames it as an expected, user-triggerable condition (all collections excluded). This makes it harder to distinguish genuine bugs from user misconfiguration in logs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/NZImageApiLambda/NZImageApi.swift` around lines 119 - 133,
`NoEligibleCollectionsError` is currently treated as a generic unexpected
failure in `NZImageApi.newResult` because it does not match the `RichError`
branch. Update the error handling around the
`digitalNZAPIDataSource.newResult(...)` call so this expected, user-triggerable
case is logged distinctly from real bugs; use the existing
`NoEligibleCollectionsError` symbol (and the surrounding `do/catch` in
`NZImageApi.swift`) to add a specific catch or equivalent check, and keep the
generic `"unexpected error"` path only for truly unknown errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/NZImageApiLambda/Data` Sources/DigitalNZAPIDataSource.swift:
- Around line 51-53: `weightedRandomPick()` can trap on an empty dictionary
because it falls through to the last element access; update this helper to
handle empty input safely by either returning an optional or adding a clear
precondition/guard before selection. Keep the fix localized in the
`weightedRandomPick()` implementation and adjust the `chosenCollection =
(weightsOverride ?? collectionWeights).weightedRandomPick()` call site only if
the new return type requires it.

In `@Sources/NZImageApiLambda/Models/NZRecordsResult.swift`:
- Around line 25-27: Keep the `NZRecordsResult` metadata fields (`creator`,
`date`, `subject`) as optional arrays because synthesized `Codable` depends on
them being nullable when keys are missing. Do not change them to non-optional
`[String] = []` unless you also add a custom `init(from:)` that uses
`decodeIfPresent(... ) ?? []`; otherwise decoding will fail for absent DigitalNZ
metadata fields. If needed, suppress SwiftLint’s
`discouraged_optional_collection` directly on those properties in
`NZRecordsResult`.

In `@Tests/NZImageApiLambdaTests/FilteredCollectionWeightsTests.swift`:
- Line 15: The SwiftLint `required_deinit` warning is being triggered on the
stateless `FilteredCollectionWeightsTests` XCTestCase subclass, so don’t add a
boilerplate deinit here; instead adjust the lint configuration to exempt test
targets or `XCTestCase` subclasses globally. Update the relevant SwiftLint
rules/config so `FilteredCollectionWeightsTests` and similar test classes are
ignored, keeping the test file itself unchanged.

---

Outside diff comments:
In `@Sources/NZImageApiLambda/NZImageApi.swift`:
- Around line 119-133: `NoEligibleCollectionsError` is currently treated as a
generic unexpected failure in `NZImageApi.newResult` because it does not match
the `RichError` branch. Update the error handling around the
`digitalNZAPIDataSource.newResult(...)` call so this expected, user-triggerable
case is logged distinctly from real bugs; use the existing
`NoEligibleCollectionsError` symbol (and the surrounding `do/catch` in
`NZImageApi.swift`) to add a specific catch or equivalent check, and keep the
generic `"unexpected error"` path only for truly unknown errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ddecfd56-d5dd-4981-8f31-bc13793aec54

📥 Commits

Reviewing files that changed from the base of the PR and between 8b2d74a and 7a9a4eb.

📒 Files selected for processing (6)
  • Sources/NZImageApiLambda/Data Sources/DigitalNZAPIDataSource.swift
  • Sources/NZImageApiLambda/Models/NZRecordsResult.swift
  • Sources/NZImageApiLambda/NZImageApi.swift
  • Sources/NZImageApiLambda/NZImageApiLambda.swift
  • Tests/NZImageApiLambdaTests/FilteredCollectionWeightsTests.swift
  • template.yaml

Comment thread Sources/NZImageApiLambda/Data Sources/DigitalNZAPIDataSource.swift
Comment thread Sources/NZImageApiLambda/Models/NZRecordsResult.swift
Comment thread Tests/NZImageApiLambdaTests/FilteredCollectionWeightsTests.swift
CodeRabbit review on #32: falling through to the generic catch made an
expected, user-triggerable condition (an exclude list covering every
collection) read as a genuine bug in the logs.
@bradwindy

Copy link
Copy Markdown
Owner Author

Addressing the outside-diff-range comment on NZImageApi.swift (NoEligibleCollectionsError logged as an "unexpected error"): fixed in 6c5a1f2 — added a dedicated catch is NoEligibleCollectionsError branch above the generic catch-all, so the expected "exclude covers every collection" case now logs its own message instead of "An unexpected error occurred".

@bradwindy
bradwindy merged commit b82b717 into main Jul 9, 2026
4 checks passed
@bradwindy
bradwindy deleted the add-collections-endpoint branch July 9, 2026 12:17
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