feat(geoloc): expose per-entrance coordinates in GET /geoloc/networks#1712
Conversation
- Rewrite NETWORKS_IN_BOUNDS SQL with CTE for stable centroid and entrance list - Network qualification: caves with >1 non-deleted entrances (sensitive count) - Response includes entrances array with id, name, latitude, longitude per entrance - Centroid computed from all non-sensitive, non-deleted entrances (stable across pans) - Add property-based tests for formatNetworks grouping and centroid math - Update integration tests for new response shape - Update Swagger documentation for /geoloc/networks Closes #1697
Paul-AUB
left a comment
There was a problem hiding this comment.
Clean implementation of a well-thought-out feature. The CTE + window function approach is the right tool here, the formatNetworks rewrite is clear, and the test coverage (integration + property-based) is exemplary for a service-layer change. A few items worth looking at before merge:
Suggestions (Should Consider)
-
[GeoLocService.js:112] The
LEFT JOIN t_name AS nc ON nc.id_cave = c.id AND nc.is_main = true AND nc.is_deleted = falsehas no SQL-level uniqueness guarantee. If a cave ever carries two activeis_main = truename entries (e.g. a multilingual app storing one per language), each entrance row in the output would be silently duplicated. The old query'sGROUP BY c.id, COALESCE(nc.name, ne.name)happened to collapse this as a side effect. The rest of the codebase follows the same JOIN pattern, so the data model likely enforces uniqueness today, but since the original GROUP BY was removed here, consider adding a defensiveDISTINCT ON (c.id, en.id)or replacing the cave-name join with aLIMIT 1lateral subquery (mirroring thenc_first_entpattern already used for the fallback). -
[GeoLocNetworks.property.test.js:8]
formatNetworksis inlined verbatim in the property test file rather than imported from the service. If the real function is refactored, these property tests will keep passing against the stale copy and won't catch the regression. Consider importing directly:// eslint-disable-next-line import/no-unresolved const { formatNetworks } = require('../../../api/services/GeoLocService');
(Export it from the service, or extract it to a dedicated util if it needs to remain private.)
-
[swaggerV1.yaml:5112] The network-level
namefield is documented astype: stringwithoutnullable: true, butCOALESCE(nc.name, nc_first_ent.name)can returnnullwhen a cave has no name row int_nameand every one of its non-sensitive entrances also has no name. The entrance-levelnameon line 5132 correctly marksnullable: true— the same treatment should apply to the top-levelnamefor consistency and to avoid client-side surprises.
Nitpicks (Optional)
-
[GeoLocService.js:124]
NETWORKS_IN_BOUNDSends with a trailing semicolon (ORDER BY c.id, en.id;) that no other SQL constant in the file has.node-postgresaccepts it, but it's inconsistent with the surrounding style. -
[GeoLocNetworks.property.test.js:45] The entrance-ID generator (
fc.integer({ min: 1, max: 100000 })) does not enforce uniqueness across entries within the same network. The real SQL would never return duplicate entrance IDs (primary key), so the fixture slightly oversteps the production domain. Usingfc.uniqueArrayor sequential IDs would make the fixture a more faithful replica of what the service actually returns.
- Replace cave-name LEFT JOIN with LATERAL LIMIT 1 to prevent row duplication - Export formatNetworks from GeoLocService and import in property tests - Add nullable: true to network-level name in swagger spec - Remove trailing semicolon from NETWORKS_IN_BOUNDS for style consistency - Use fc.uniqueArray with selector for entrance ID uniqueness in property tests
|
Thanks for the thorough review @Paul-AUB! All items addressed in a939732:
All 3106 tests pass, lint clean, and manual curl validation confirmed happy path + edge cases. |
🤔 What
GET /api/v1/geoloc/networksto include per-entrance coordinates in its responseentrancesarray withid,name,latitude,longitudefor each non-sensitive entrancelongitude/latitude) is now computed from ALL non-sensitive entrances of the network, making it stable across map pans🤷♂️ Why
The main map uses this endpoint to display network markers. Currently it returns a single centroid per network computed only from entrances visible in the viewport — the centroid shifts as the user pans. The frontend needs individual entrance coordinates to render markers at each entrance location, and the centroid should remain stable.
🔍 How
NETWORKS_IN_BOUNDSSQL with a CTE-based query:qualifying_networksCTE identifies caves with >1 non-deleted entrances (sensitive ones count toward qualification) that have at least one non-sensitive entrance in the bboxavg() OVER (PARTITION BY)formatNetworksto group flat rows by cave ID into network objects withentrancesarraysCOALESCE(cave_name, first_entrance_name)with aLATERALsubquery to avoid row multiplicationid,name,longitude,latitudefields preserved🧪 Testing
GeoLocService.test.jsto verify the new response shape (cave 1 with entrances 1 & 2)Geoloc.test.jsto verify HTTP endpoint returnsentrancesarrayGeoLocNetworks.property.test.js) forformatNetworksgrouping logic and centroid correctnessGeoLocNameFilter.test.jsassertions for the new shapeRun:
npm run test -- --grep "GeoLocService|GeoLocNetworks|find networks"📸 Previews
N/A (API-only change)