Skip to content

feat(spp_api_v2): OpenAPI polymorphic bodies, OAuth2 scheme in auth middleware, bundle schemas (re-land from #76)#276

Open
gonzalesedwin1123 wants to merge 3 commits into
19.0from
reland/api-v2-core
Open

feat(spp_api_v2): OpenAPI polymorphic bodies, OAuth2 scheme in auth middleware, bundle schemas (re-land from #76)#276
gonzalesedwin1123 wants to merge 3 commits into
19.0from
reland/api-v2-core

Conversation

@gonzalesedwin1123

Copy link
Copy Markdown
Member

Re-lands the spp_api_v2 portion of reverted PR #76 (revert: #271).

Summary

  • utils/openapi_polymorphic.py: polymorphic_body() helper + OpenAPI anyOf-injection hook, installed on the app via the endpoint registry.
  • Auth middleware: OAuth2 client-credentials security scheme replacing plain HTTPBearer (advertises the token endpoint in OpenAPI), Bearer-prefix stripping.
  • BundleEntry.resource becomes a polymorphic Individual/Group body; schema import-order fix.
  • New OpenAPI contract/polymorphic/bundle tests.

Note: tests/test_search_service.py name-assertion updates from #76 already landed with the revert (#271 kept them aligned with the retained spp_registry name fix), so they are not part of this diff.

Verification

  • ./spp t spp_api_v2: 640 passed, 0 failed

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces polymorphic OpenAPI schema utilities to document BundleEntry.resource as a polymorphic Individual/Group body, replaces the plain HTTPBearer authentication scheme with an OAuth2 client-credentials scheme, and adds corresponding contract and unit tests. The review feedback highlights two main improvements: first, in openapi_polymorphic.py, the custom OpenAPI hook should call the original app.openapi method instead of calling get_openapi directly to avoid discarding app metadata; second, in auth.py, the token extraction should strip any leading or trailing whitespace after slicing the 'Bearer ' prefix to ensure robust JWT decoding.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +117 to +126
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
schema = get_openapi(
title=app.title,
version=app.version,
description=app.description,
routes=app.routes,
)
components = schema.setdefault("components", {}).setdefault("schemas", {})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Calling get_openapi directly with only a subset of parameters (title, version, description, routes) discards other important metadata configured on the FastAPI app instance, such as contact and license_info (which are explicitly defined in fastapi_endpoint_registry.py), as well as any other potential settings like servers, tags, summary, terms_of_service, etc.

Instead of calling get_openapi directly, save a reference to the original app.openapi method and call it to obtain the fully populated schema, then inject the polymorphic schemas into it.

    original_openapi = app.openapi

    def custom_openapi():
        if app.openapi_schema:
            return app.openapi_schema
        schema = original_openapi()
        components = schema.setdefault("components", {}).setdefault("schemas", {})

Comment thread spp_api_v2/middleware/auth.py Outdated
Comment on lines +62 to +63
if token.lower().startswith("bearer "):
token = token[7:]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Slicing the token with token[7:] when stripping the "bearer " prefix can leave leading or trailing whitespace if the client sent multiple spaces (e.g., "Bearer eyJ..."). This can cause JWT decoding to fail.

Applying .strip() ensures that any extra whitespace is safely removed.

Suggested change
if token.lower().startswith("bearer "):
token = token[7:]
if token.lower().startswith("bearer "):
token = token[7:].strip()

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.17647% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.62%. Comparing base (bf61488) to head (802e08d).
⚠️ Report is 1 commits behind head on 19.0.

Files with missing lines Patch % Lines
spp_api_v2/utils/openapi_polymorphic.py 89.09% 6 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #276      +/-   ##
==========================================
- Coverage   74.86%   73.62%   -1.24%     
==========================================
  Files        1093      355     -738     
  Lines       63718    19522   -44196     
==========================================
- Hits        47701    14374   -33327     
+ Misses      16017     5148   -10869     
Flag Coverage Δ
endpoint_route_handler ?
fastapi ?
spp_aggregation ?
spp_alerts ?
spp_analytics ?
spp_api_v2 79.92% <91.17%> (+0.13%) ⬆️
spp_api_v2_change_request 66.41% <ø> (ø)
spp_api_v2_cycles 70.65% <ø> (ø)
spp_api_v2_data 77.43% <ø> (ø)
spp_api_v2_entitlements 69.96% <ø> (ø)
spp_api_v2_gis 71.52% <ø> (ø)
spp_api_v2_products 65.66% <ø> (ø)
spp_api_v2_programs 92.03% <ø> (ø)
spp_api_v2_service_points 70.54% <ø> (ø)
spp_api_v2_simulation 71.12% <ø> (ø)
spp_api_v2_vocabulary 57.26% <ø> (ø)
spp_approval ?
spp_area ?
spp_area_hdx ?
spp_attachment_av_scan ?
spp_audit ?
spp_audit_programs ?
spp_banking ?
spp_base_common 90.26% <ø> (ø)
spp_base_setting ?
spp_case_base ?
spp_case_cel ?
spp_case_demo ?
spp_case_entitlements ?
spp_case_graduation ?
spp_case_programs ?
spp_case_registry ?
spp_case_session ?
spp_cel_domain ?
spp_cel_event ?
spp_cel_registry_search ?
spp_cel_vocabulary ?
spp_change_request_v2 ?
spp_claim_169 ?
spp_cr_type_assign_program ?
spp_cr_types_advanced ?
spp_cr_types_base ?
spp_dci ?
spp_dci_client ?
spp_dci_client_dr 85.60% <ø> (ø)
spp_dci_client_ibr 92.27% <ø> (ø)
spp_dci_client_sr ?
spp_dci_compliance 92.76% <ø> (ø)
spp_dci_demo 69.23% <ø> (ø)
spp_dci_indicators 95.83% <ø> (ø)
spp_dci_server ?
spp_dci_server_social ?
spp_demo ?
spp_demo_phl_luzon ?
spp_disability_registry ?
spp_drims ?
spp_drims_sl ?
spp_drims_sl_demo ?
spp_encryption ?
spp_farmer_registry ?
spp_farmer_registry_cr ?
spp_farmer_registry_demo ?
spp_farmer_registry_vocabularies ?
spp_gis ?
spp_gis_indicators ?
spp_gis_report ?
spp_graduation ?
spp_grm ?
spp_grm_case_link ?
spp_grm_demo ?
spp_hazard ?
spp_hazard_programs ?
spp_hxl_area ?
spp_import_match ?
spp_indicator ?
spp_irrigation ?
spp_land_record ?
spp_metric ?
spp_metric_service ?
spp_metrics_core ?
spp_metrics_services ?
spp_mis_demo_v2 ?
spp_oauth ?
spp_program_geofence ?
spp_programs 65.27% <ø> (ø)
spp_registrant_gis ?
spp_registry 86.83% <ø> (ø)
spp_registry_group_hierarchy ?
spp_scoring ?
spp_scoring_programs ?
spp_security 66.66% <ø> (ø)
spp_service_points ?
spp_simulation ?
spp_starter_disability_registry ?
spp_starter_farmer_registry ?
spp_starter_social_registry ?
spp_starter_sp_mis ?
spp_statistic ?
spp_storage_backend ?
spp_studio ?
spp_studio_change_requests ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_api_v2/__manifest__.py 0.00% <ø> (ø)
spp_api_v2/middleware/auth.py 76.11% <100.00%> (+0.36%) ⬆️
spp_api_v2/models/fastapi_endpoint_registry.py 94.28% <100.00%> (+0.34%) ⬆️
spp_api_v2/schemas/__init__.py 100.00% <100.00%> (ø)
spp_api_v2/schemas/bundle.py 100.00% <100.00%> (ø)
spp_api_v2/utils/openapi_polymorphic.py 89.09% <89.09%> (ø)

... and 739 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

… bundle schemas (from #76)

Re-lands the spp_api_v2 portion of PR #76, which was reverted wholesale
in d38ff9d. Restores the OpenAPI polymorphic schema utilities and app
hook, the OAuth2 client-credentials security scheme in the auth
middleware, the polymorphic BundleEntry.resource schema, and the
OpenAPI contract tests, exactly as merged in 8bf9a3a. Bumps the module
version to 19.0.2.1.0 with a matching HISTORY entry.
@gonzalesedwin1123

Copy link
Copy Markdown
Member Author

gemini-code-assist disposition:

Applied: Bearer-prefix strip now also trims whitespace (token[7:].strip()). Suite: 640/0.

Deferred: openapi_polymorphic.py using the app's original openapi() instead of a partial get_openapi call — agreed in principle (contact/license metadata), but the OpenAPI contract tests pin the current output; changing generation belongs in its own PR with contract-test updates.

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