Skip to content

Commit f4f16ab

Browse files
committed
fix: resolve CI pre-commit and auth audit failures
1 parent 317b544 commit f4f16ab

9 files changed

Lines changed: 289 additions & 135 deletions

File tree

scripts/audit-api-auth.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@
8080
("spp_encryption_rest_api", "well_known.py", "*"),
8181
# FastAPI demo router (development only)
8282
("fastapi", "demo_router.py", "*"),
83+
# OGC OPTIONS endpoint - CORS preflight, public by design
84+
("spp_api_v2_gis", "ogc_features.py", "options_collection_items"),
8385
}
8486

8587

spp_api_v2_gis/README.rst

Lines changed: 110 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
.. image:: https://odoo-community.org/readme-banner-image
2-
:target: https://odoo-community.org/get-involved?utm_source=readme
3-
:alt: Odoo Community Association
4-
51
===============
62
OpenSPP GIS API
73
===============
84

9-
..
5+
..
106
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
117
!! This file is generated by oca-gen-addon-readme !!
128
!! changes will be overwritten. !!
@@ -20,67 +16,137 @@ OpenSPP GIS API
2016
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
2117
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
2218
:alt: License: LGPL-3
23-
.. |badge3| image:: https://img.shields.io/badge/github-OpenSPP%2Fopenspp--modules-lightgray.png?logo=github
24-
:target: https://github.com/OpenSPP/openspp-modules/tree/19.0/spp_api_v2_gis
25-
:alt: OpenSPP/openspp-modules
19+
.. |badge3| image:: https://img.shields.io/badge/github-OpenSPP%2FOpenSPP2-lightgray.png?logo=github
20+
:target: https://github.com/OpenSPP/OpenSPP2/tree/19.0/spp_api_v2_gis
21+
:alt: OpenSPP/OpenSPP2
2622

2723
|badge1| |badge2| |badge3|
2824

29-
REST API for QGIS plugin integration, providing GeoJSON endpoints,
30-
spatial queries, and geofence management.
25+
REST API for QGIS plugin integration, providing OGC API - Features
26+
endpoints, spatial queries, and geofence management.
3127

3228
Key Features
3329
------------
3430

35-
- **Layer Catalog**: List available GIS layers and reports
36-
- **GeoJSON Export**: Get pre-aggregated layer data for QGIS
37-
- **QML Styling**: Fetch QGIS style files for consistent visualization
38-
- **Spatial Queries**: Query registrant statistics within arbitrary
39-
polygons using PostGIS
40-
- **Geofence Management**: Save and manage areas of interest
31+
- **OGC API - Features**: Standards-compliant feature collections
32+
(GovStack GIS BB)
33+
- **GeoJSON Export**: Get pre-aggregated layer data for QGIS
34+
- **QML Styling**: Fetch QGIS style files for consistent visualization
35+
- **Spatial Queries**: Query registrant statistics within arbitrary
36+
polygons using PostGIS
37+
- **Geofence Management**: Save and manage areas of interest
4138

4239
Architecture
4340
------------
4441

4542
Follows thin client architecture where QGIS displays data and OpenSPP
4643
performs all computation:
4744

48-
- All spatial queries executed in PostGIS for performance
49-
- Pre-aggregated data returned to minimize data transfer
50-
- Configuration-driven styling using QML templates
51-
- OAuth 2.0 authentication with scope-based access control
45+
- All spatial queries executed in PostGIS for performance (including
46+
bbox via ST_Intersects)
47+
- Pre-aggregated data returned to minimize data transfer
48+
- Configuration-driven styling using QML templates
49+
- JWT authentication with scope-based access control
5250

5351
API Endpoints
5452
-------------
5553

56-
- ``GET /gis/catalog`` - List available layers and reports
57-
- ``GET /gis/layers/{id}`` - Get layer as GeoJSON FeatureCollection
58-
- ``GET /gis/layers/{id}/qml`` - Get QGIS style file
59-
- ``POST /gis/query/statistics`` - Query statistics for polygon
60-
- ``POST /gis/geofences`` - Create geofence
61-
- ``GET /gis/geofences`` - List geofences
62-
- ``GET /gis/geofences/{id}`` - Get single geofence as GeoJSON
63-
- ``DELETE /gis/geofences/{id}`` - Archive geofence
64-
- ``GET /gis/export/geopackage`` - Export layers for offline use
65-
66-
Required Scopes
67-
---------------
68-
69-
- ``gis:read`` - View layers and statistics
70-
- ``gis:geofence`` - Create and manage geofences
54+
**OGC API - Features (primary interface)**
55+
56+
+-------------------------------------------+--------+------------------------------+
57+
| Endpoint | Method | Description |
58+
+===========================================+========+==============================+
59+
| ``/gis/ogc/`` | GET | OGC API landing page |
60+
+-------------------------------------------+--------+------------------------------+
61+
| ``/gis/ogc/conformance`` | GET | OGC conformance classes |
62+
+-------------------------------------------+--------+------------------------------+
63+
| ``/gis/ogc/collections`` | GET | List feature collections |
64+
+-------------------------------------------+--------+------------------------------+
65+
| ``/gis/ogc/collections/{id}`` | GET | Collection metadata |
66+
+-------------------------------------------+--------+------------------------------+
67+
| ``/gis/ogc/collections/{id}/items`` | GET | Feature items (GeoJSON) |
68+
+-------------------------------------------+--------+------------------------------+
69+
| ``/gis/ogc/collections/{id}/items/{fid}`` | GET | Single feature |
70+
+-------------------------------------------+--------+------------------------------+
71+
| ``/gis/ogc/collections/{id}/qml`` | GET | QGIS style file (extension) |
72+
+-------------------------------------------+--------+------------------------------+
73+
74+
**Additional endpoints**
75+
76+
========================== ========== =======================
77+
Endpoint Method Description
78+
========================== ========== =======================
79+
``/gis/query/statistics`` POST Query stats for polygon
80+
``/gis/geofences`` POST/GET Geofence management
81+
``/gis/geofences/{id}`` GET/DELETE Single geofence
82+
``/gis/export/geopackage`` GET Export for offline use
83+
========================== ========== =======================
84+
85+
Scopes and Data Privacy
86+
-----------------------
87+
88+
**OAuth Scopes**
89+
90+
+------------------+--------------+------------------------------------+
91+
| Scope | Access | Description |
92+
+==================+==============+====================================+
93+
| ``gis:read`` | Read-only | View collections, layers, |
94+
| | | statistics, export data |
95+
+------------------+--------------+------------------------------------+
96+
| ``gis:geofence`` | Read + Write | Create and archive geofences (also |
97+
| | | requires ``gis:read`` for listing) |
98+
+------------------+--------------+------------------------------------+
99+
100+
**What data is exposed**
101+
102+
**Aggregated statistics only.** No endpoint in this module returns
103+
individual registrant records.
104+
105+
- **OGC collections/items**: Return GeoJSON features organized by
106+
administrative area, with pre-computed aggregate values (counts,
107+
percentages). Each feature represents an *area*, not a person.
108+
- **Spatial query statistics** (``POST /gis/query/statistics``): Accepts
109+
a GeoJSON polygon and returns configured aggregate statistics computed
110+
by ``spp.aggregation.service``. Individual registrant IDs are computed
111+
internally for aggregation but are **explicitly stripped** from the
112+
response before it is sent (see ``spatial_query.py``).
113+
- **Exports** (GeoPackage/GeoJSON): Contain the same area-level
114+
aggregated layer data, not registrant-level records.
115+
- **Geofences**: Store only geometry and metadata — no registrant data.
116+
117+
**Privacy controls**
118+
119+
- **K-anonymity suppression**: Statistics backed by CEL variables can
120+
apply k-anonymity thresholds. When a cell count falls below the
121+
configured minimum, the value is replaced with a suppression marker
122+
and flagged as ``"suppressed": true`` in the response. This prevents
123+
re-identification in small populations.
124+
- **CEL variable configuration**: Administrators control which
125+
statistics are published and their suppression thresholds via
126+
``spp.statistic`` records.
127+
- **Scope separation**: ``gis:read`` and ``gis:geofence`` are separate
128+
scopes, allowing clients to be granted read-only access without write
129+
capability.
130+
131+
**Design rationale**
132+
133+
This module follows a **thin client** architecture: QGIS (or any
134+
OGC-compatible client) displays pre-aggregated data, while OpenSPP
135+
retains all individual-level data server-side. This ensures that GIS API
136+
clients — including the QGIS plugin — never need access to personally
137+
identifiable information.
71138

72139
Dependencies
73140
------------
74141

75-
- ``spp_api_v2`` - FastAPI infrastructure
76-
- ``spp_gis`` - PostGIS integration
77-
- ``spp_gis_report`` - Report configuration
78-
- ``spp_area`` - Administrative area data
142+
- ``spp_api_v2`` - FastAPI infrastructure
143+
- ``spp_gis`` - PostGIS integration
144+
- ``spp_gis_report`` - Report configuration
145+
- ``spp_area`` - Administrative area data
79146

80147
.. IMPORTANT::
81148
This is an alpha version, the data model and design can change at any time without warning.
82149
Only for development or testing purpose, do not use in production.
83-
`More details on development status <https://odoo-community.org/page/development-status>`_
84150

85151
**Table of contents**
86152

@@ -90,10 +156,10 @@ Dependencies
90156
Bug Tracker
91157
===========
92158

93-
Bugs are tracked on `GitHub Issues <https://github.com/OpenSPP/openspp-modules/issues>`_.
159+
Bugs are tracked on `GitHub Issues <https://github.com/OpenSPP/OpenSPP2/issues>`_.
94160
In case of trouble, please check there if your issue has already been reported.
95161
If you spotted it first, help us to smash it by providing a detailed and welcomed
96-
`feedback <https://github.com/OpenSPP/openspp-modules/issues/new?body=module:%20spp_api_v2_gis%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
162+
`feedback <https://github.com/OpenSPP/OpenSPP2/issues/new?body=module:%20spp_api_v2_gis%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
97163

98164
Do not contact contributors directly about support or help with technical issues.
99165

@@ -122,6 +188,6 @@ Current maintainers:
122188

123189
|maintainer-jeremi| |maintainer-gonzalesedwin1123| |maintainer-reichie020212|
124190

125-
This module is part of the `OpenSPP/openspp-modules <https://github.com/OpenSPP/openspp-modules/tree/19.0/spp_api_v2_gis>`_ project on GitHub.
191+
This module is part of the `OpenSPP/OpenSPP2 <https://github.com/OpenSPP/OpenSPP2/tree/19.0/spp_api_v2_gis>`_ project on GitHub.
126192

127-
You are welcome to contribute.
193+
You are welcome to contribute.

spp_api_v2_gis/__manifest__.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,4 @@
3131
"summary": """
3232
OGC API - Features compliant GIS endpoints for QGIS and GovStack GIS BB.
3333
""",
34-
"description": """
35-
OpenSPP GIS API
36-
===============
37-
38-
Extends OpenSPP API V2 with OGC API - Features compliant endpoints,
39-
enabling GovStack GIS Building Block compliance and interoperability
40-
with any OGC client (QGIS, ArcGIS, Leaflet, ogr2ogr, etc.).
41-
42-
OGC API - Features Endpoints
43-
-----------------------------
44-
- ``GET /gis/ogc/`` - Landing page
45-
- ``GET /gis/ogc/conformance`` - Conformance declaration
46-
- ``GET /gis/ogc/collections`` - List feature collections
47-
- ``GET /gis/ogc/collections/{id}`` - Collection metadata
48-
- ``GET /gis/ogc/collections/{id}/items`` - Feature items (GeoJSON)
49-
- ``GET /gis/ogc/collections/{id}/items/{fid}`` - Single feature
50-
- ``GET /gis/ogc/collections/{id}/qml`` - QGIS style file (extension)
51-
52-
Proprietary Endpoints
53-
---------------------
54-
- ``POST /gis/query/statistics`` - Spatial statistics query
55-
- ``CRUD /gis/geofences`` - Manage saved areas of interest
56-
- ``GET /gis/export/geopackage`` - Export layers for offline use
57-
58-
Design Principles
59-
-----------------
60-
- OGC API - Features Core + GeoJSON conformance
61-
- Thin client architecture (QGIS displays, OpenSPP computes)
62-
- Pre-aggregated data for performance
63-
- PostGIS spatial queries
64-
- Requires authentication via OAuth 2.0
65-
""",
6634
}

spp_api_v2_gis/models/geofence.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def _compute_area_sqkm(self):
136136
result = self.env.cr.fetchone()
137137
rec.area_sqkm = result[0] if result else 0.0
138138
except Exception as e:
139-
_logger.warning("Failed to compute area for geofence %s: %s", rec.name, str(e))
139+
_logger.warning("Failed to compute area for geofence %s: %s", rec.id, str(e))
140140
rec.area_sqkm = 0.0
141141

142142
@api.constrains("name", "active")
@@ -186,7 +186,7 @@ def to_geojson(self):
186186
try:
187187
geometry_dict = mapping(self.geometry)
188188
except Exception as e:
189-
_logger.warning("Failed to convert geometry to GeoJSON for geofence %s: %s", self.name, str(e))
189+
_logger.warning("Failed to convert geometry to GeoJSON for geofence %s: %s", self.id, str(e))
190190
geometry_dict = None
191191

192192
return {

spp_api_v2_gis/readme/DESCRIPTION.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Follows thin client architecture where QGIS displays data and OpenSPP performs a
1919

2020
## API Endpoints
2121

22-
### OGC API - Features (primary interface)
22+
**OGC API - Features (primary interface)**
2323

2424
| Endpoint | Method | Description |
2525
|----------|--------|-------------|
@@ -31,7 +31,7 @@ Follows thin client architecture where QGIS displays data and OpenSPP performs a
3131
| `/gis/ogc/collections/{id}/items/{fid}` | GET | Single feature |
3232
| `/gis/ogc/collections/{id}/qml` | GET | QGIS style file (extension) |
3333

34-
### Additional endpoints
34+
**Additional endpoints**
3535

3636
| Endpoint | Method | Description |
3737
|----------|--------|-------------|
@@ -42,14 +42,14 @@ Follows thin client architecture where QGIS displays data and OpenSPP performs a
4242

4343
## Scopes and Data Privacy
4444

45-
### OAuth Scopes
45+
**OAuth Scopes**
4646

4747
| Scope | Access | Description |
4848
|-------|--------|-------------|
4949
| `gis:read` | Read-only | View collections, layers, statistics, export data |
5050
| `gis:geofence` | Read + Write | Create and archive geofences (also requires `gis:read` for listing) |
5151

52-
### What data is exposed
52+
**What data is exposed**
5353

5454
**Aggregated statistics only.** No endpoint in this module returns individual registrant records.
5555

@@ -58,13 +58,13 @@ Follows thin client architecture where QGIS displays data and OpenSPP performs a
5858
- **Exports** (GeoPackage/GeoJSON): Contain the same area-level aggregated layer data, not registrant-level records.
5959
- **Geofences**: Store only geometry and metadata — no registrant data.
6060

61-
### Privacy controls
61+
**Privacy controls**
6262

6363
- **K-anonymity suppression**: Statistics backed by CEL variables can apply k-anonymity thresholds. When a cell count falls below the configured minimum, the value is replaced with a suppression marker and flagged as `"suppressed": true` in the response. This prevents re-identification in small populations.
6464
- **CEL variable configuration**: Administrators control which statistics are published and their suppression thresholds via `spp.statistic` records.
6565
- **Scope separation**: `gis:read` and `gis:geofence` are separate scopes, allowing clients to be granted read-only access without write capability.
6666

67-
### Design rationale
67+
**Design rationale**
6868

6969
This module follows a **thin client** architecture: QGIS (or any OGC-compatible client) displays pre-aggregated data, while OpenSPP retains all individual-level data server-side. This ensures that GIS API clients — including the QGIS plugin — never need access to personally identifiable information.
7070

spp_api_v2_gis/services/layers_service.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def _get_data_layer_geojson(self, layer_id, include_geometry=True, limit=None, o
300300
"styling": styling,
301301
}
302302

303-
_logger.info("Generated GeoJSON for layer: %s with %d features", layer.name, len(features))
303+
_logger.info("Generated GeoJSON for layer: %s with %d features", layer.id, len(features))
304304

305305
return geojson
306306

@@ -318,7 +318,7 @@ def _fetch_layer_features(self, layer, include_geometry, limit=None, offset=0, b
318318
list: List of GeoJSON features
319319
"""
320320
if not layer.model_name or not layer.geo_field_id:
321-
_logger.warning("Layer %s has no model or geo field configured", layer.name)
321+
_logger.warning("Layer %s has no model or geo field configured", layer.id)
322322
return []
323323

324324
Model = self.env[layer.model_name].sudo()
@@ -331,7 +331,7 @@ def _fetch_layer_features(self, layer, include_geometry, limit=None, offset=0, b
331331

332332
domain = literal_eval(layer.domain)
333333
except (ValueError, SyntaxError) as e:
334-
_logger.warning("Invalid domain on layer %s: %s", layer.name, e)
334+
_logger.warning("Invalid domain on layer %s: %s", layer.id, e)
335335

336336
# Apply bbox spatial filter via PostGIS ST_Intersects
337337
if bbox:

spp_api_v2_gis/services/qml_template_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def _generate_graduated_polygon(
122122
# Get thresholds
123123
thresholds = report.threshold_ids.sorted("sequence")
124124
if not thresholds:
125-
_logger.warning("No thresholds defined for report: %s", report.name)
125+
_logger.warning("No thresholds defined for report: %s", report.id)
126126
# Generate default single-class QML
127127
return self._generate_default_polygon(template, field_name, opacity)
128128

0 commit comments

Comments
 (0)