Skip to content

fix: spurious 'Invalid layer identifier' for dynamically registered OpenSPP collection layers#1

Open
gonzalesedwin1123 wants to merge 1 commit into
jeremi:claude/prism-openspp-integration-ZEioQfrom
gonzalesedwin1123:fix/dynamic-layer-url-validation
Open

fix: spurious 'Invalid layer identifier' for dynamically registered OpenSPP collection layers#1
gonzalesedwin1123 wants to merge 1 commit into
jeremi:claude/prism-openspp-integration-ZEioQfrom
gonzalesedwin1123:fix/dynamic-layer-url-validation

Conversation

@gonzalesedwin1123

Copy link
Copy Markdown

Problem

Selecting a collection from the OpenSPP Reports > Collections browser shows an error notification — Invalid layer identifier(s): openspp_MIS_DEMO_BENEFICIARY_DIST_adm0 — even though the choropleth renders correctly.

Root cause

layers-utils.tsx validated URL layer ids against a key list snapshotted once at mount:

const layerDefinitionIds = useMemo(() => Object.keys(LayerDefinitions), []);

OpenSPP collections are fetched asynchronously and injected into LayerDefinitions later via registerDynamicLayer() (RootAccordionItems/index.tsx), so their openspp_<collectionId> ids are never in the frozen snapshot. The rendering pipeline reads the live map (works); only the validator used the stale copy (spurious error).

Fix

Validate against the live LayerDefinitions map at validation time, via a getInvalidLayerIds helper placed next to registerDynamicLayer in config/utils.ts. Since registerDynamicLayer mutates a module-level object (invisible to React reactivity), memoizing its keys can never be correct — the check must read the object when it runs. The invalidLayersIds memo recomputes on every urlLayerIds change (i.e., whenever a layer is toggled), which happens strictly after registration in the click flow.

Known limitation (pre-existing, out of scope)

Opening a shared URL that references a dynamic collection layer still races the async collections fetch: validation runs before registerDynamicLayer, and nothing re-triggers it after registration. Fixing that needs registration to become reactive (e.g., a redux-tracked registry version) — a larger change than this notification bug warrants.

Testing

  • TDD: frontend/src/config/dynamic-layers.test.ts (4 cases — unknown id invalid, static id valid, runtime-registered id valid, mixed list) written red first, green after the fix.
  • yarn tsc --noEmit and eslint on touched files: identical to base-branch baseline (3 pre-existing TS errors in untouched files, 1 pre-existing prettier warning).
  • Verified live against a local OpenSPP misluzon instance: dev server compiles clean, app serves.

Found during the QGIS/PRISM ↔ OpenSPP integration test session (baseline for the OpenSPP2#281 OGC migration).

The invalid-layer check in layers-utils snapshotted LayerDefinitions keys
once at mount, before OpenSPP collections are fetched and registered via
registerDynamicLayer. Selecting a collection from the dynamic Collections
browser then raised a spurious 'Invalid layer identifier(s)' notification
even though the layer itself rendered correctly.

Validate against the live LayerDefinitions map at validation time instead,
via a getInvalidLayerIds helper next to registerDynamicLayer.

@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 the getInvalidLayerIds helper function to validate layer IDs against the live LayerDefinitions map, ensuring that dynamically registered layers are correctly recognized. The useLayers hook is updated to use this new helper, and comprehensive unit tests are added. Feedback suggests replacing the in operator with Object.prototype.hasOwnProperty.call to prevent potential issues with built-in prototype properties when processing untrusted URL inputs.

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.

* registerDynamicLayer are recognized as valid.
*/
export function getInvalidLayerIds(layerIds: string[]): string[] {
return layerIds.filter(layerId => !(layerId in LayerDefinitions));

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

Using the in operator to check for keys in LayerDefinitions can be problematic if the layerId matches a built-in property on Object.prototype (e.g., toString, constructor, hasOwnProperty). Since layerIds are parsed from URL parameters, which are untrusted user inputs, it is safer to use Object.prototype.hasOwnProperty.call to ensure we only check for direct properties of the LayerDefinitions object.

Suggested change
return layerIds.filter(layerId => !(layerId in LayerDefinitions));
return layerIds.filter(layerId => !Object.prototype.hasOwnProperty.call(LayerDefinitions, layerId));

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