Skip to content

Add consolidated water well details endpoint for the well show page#622

Merged
jirhiker merged 1 commit intostagingfrom
api-hardening
Mar 28, 2026
Merged

Add consolidated water well details endpoint for the well show page#622
jirhiker merged 1 commit intostagingfrom
api-hardening

Conversation

@jirhiker
Copy link
Copy Markdown
Member

@jirhiker jirhiker commented Mar 28, 2026

Summary

Adds a singleton endpoint for loading the main water well details page payload without changing hydrograph or map loading behavior.

New endpoint:

  • GET /thing/water-well/{thing_id}/details

The endpoint consolidates the non-asset, non-hydrograph portions of the current frontend fan-out into one response.

What Changed

  • Added WellDetailsResponse schema.
  • Added a well_details_helper service to gather the page payload.
  • Added GET /thing/water-well/{thing_id}/details under the existing thing router.
  • Added targeted tests for:
    • payload composition
    • latest sample selection from the most recent groundwater-level observation
    • 404 behavior

Response Shape

The new endpoint returns:

  • well
  • contacts
  • sensors
  • deployments
  • well_screens
  • recent_groundwater_level_observations
  • latest_field_event_sample

Explicitly Not Included

Kept separate on purpose:

  • assets
  • hydrograph/manual + transducer full series loading
  • map layer loading

This matches the current frontend requirement to keep hydrograph and map loading out of the main well data request, and keeps asset loading separate as requested.

Why

The current well details page fans out across multiple requests for page-scoped data. This endpoint reduces that fan-out for the main content while preserving the heavier or independently refreshed requests.

Validation

  • Ran black on touched files.
  • Verified the route is mounted.
  • Added targeted API tests.

Copilot AI review requested due to automatic review settings March 28, 2026 23:54
@jirhiker jirhiker changed the title feat: enhance GCS upload handling with async support and improved err… Add consolidated water well details endpoint for the well show page Mar 28, 2026
@jirhiker jirhiker merged commit 8407bed into staging Mar 28, 2026
10 checks passed
@jirhiker jirhiker deleted the api-hardening branch March 28, 2026 23:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new “water well details” API endpoint that returns a consolidated payload for rendering a well details page (well + related contacts/sensors/deployments/screens and recent groundwater-level observation/sample context).

Changes:

  • Added GET /thing/water-well/{thing_id}/details endpoint returning a WellDetailsResponse.
  • Implemented get_well_details_payload(...) service to assemble well-related entities and recent groundwater-level observations/latest sample.
  • Added tests validating the payload shape, latest-observation selection, and 404 behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
api/thing.py Adds the new water well “details payload” route wired to the helper + response schema.
services/well_details_helper.py Implements the SQLAlchemy queries to build the consolidated well details payload.
schemas/well_details.py Introduces the Pydantic response model for the details payload.
tests/test_thing.py Adds endpoint tests for payload contents, latest-sample selection, and not-found handling.

Comment on lines +182 to +201
@router.get(
"/water-well/{thing_id}/details",
summary="Get water well details payload",
status_code=HTTP_200_OK,
)
async def get_well_details(
user: viewer_dependency,
thing_id: int,
session: session_dependency,
request: Request,
) -> WellDetailsResponse:
"""
Retrieve the consolidated payload needed to render the well details page.
Hydrograph series and map layer loading are intentionally handled separately.
"""
return get_well_details_payload(
session=session,
request=request,
thing_id=thing_id,
)
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

PR title/description refer to GCS upload handling, but this change introduces a new water well details endpoint and related schemas/tests. Please update the PR title and the filled-in PR description to match the actual scope so reviewers can evaluate the right risk/impact.

Copilot uses AI. Check for mistakes.

groundwater_parameter_id = (
session.query(Parameter)
.filter(Parameter.parameter_name == "groundwater level")
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

Parameter is unique on (parameter_name, matrix) (and this codebase also distinguishes by parameter_type in places), so filtering only by parameter_name and calling .one() can raise MultipleResultsFound if another matrix for "groundwater level" is ever added. Prefer filtering by the expected matrix (e.g., matrix == "groundwater") and/or parameter_type, or reusing a shared helper that returns the groundwater-level parameter id with a clear error when missing.

Suggested change
.filter(Parameter.parameter_name == "groundwater level")
.filter(
Parameter.parameter_name == "groundwater level",
Parameter.matrix == "groundwater",
)

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +19
well_screens: list[WellScreenResponse] = Field(default_factory=list)
recent_groundwater_level_observations: list[GroundwaterLevelObservationResponse] = (
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

WellScreenResponse includes a nested thing: WellResponse, while this payload already returns the well as well. That makes the response redundant (the well will be repeated for every screen) and can significantly increase payload size. Consider introducing a slim well-screen response schema for this endpoint that omits the nested thing (or make thing optional) and update WellDetailsResponse.well_screens accordingly.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0e9b5af5dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

status_code=HTTP_200_OK,
)
async def get_well_details(
user: viewer_dependency,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require AMPViewer for well details observations payload

This endpoint is guarded by viewer_dependency, but it returns recent_groundwater_level_observations and latest_field_event_sample, which exposes groundwater-observation data through /thing/water-well/{thing_id}/details to users who may not have AMP permissions. In this repo, groundwater observation routes (for example, /observation/groundwater-level in api/observation.py) require amp_viewer_dependency, so this change creates an authorization bypass for any principal that has Viewer but not AMPViewer.

Useful? React with 👍 / 👎.

Comment on lines +67 to +68
.filter(Parameter.parameter_name == "groundwater level")
.one()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Disambiguate groundwater parameter query before calling one()

The lookup Parameter.parameter_name == "groundwater level" is not unique by schema (the table enforces uniqueness on (parameter_name, matrix)), so this can raise MultipleResultsFound and turn the whole details endpoint into a 500 as soon as another matrix/type variant with the same name is added. Filtering by matrix/type (or otherwise handling multiple matches) avoids a production-breaking failure tied to normal data growth.

Useful? React with 👍 / 👎.

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.

2 participants