Skip to content

Part 1 extractCSAPIFeature() silently drops all @link properties during parsing #109

@Sam-Bolling

Description

@Sam-Bolling

Task

Add @link property extraction to extractCSAPIFeature() in geojson.ts — populate the systemKindLink, platformLink, deployedSystemsLink, and sampledFeatureLink interface fields (from #108) with data from raw server JSON.

Finding Reference: Demo app finding from ogc-csapi-explorer tryLinkFallback() workaround (ad06b52) and Gap Analysis Report
Severity: Moderate
Category: Design gap
Ownership: Ours


Problem Statement

The extractCSAPIFeature() function in geojson.ts — the sole parser for Part 1 GeoJSON resources — uses a property-name allowlist that silently discards all @link association properties from the raw JSON properties object. Only six hard-coded property names (featureType, uid, name, description, assetType, validTime) survive extraction; all @link inline association references defined by OGC 23-001 are lost.

Evidence:

Raw JSON properties (from server):
  ├── featureType       ✅ extracted
  ├── uid               ✅ extracted
  ├── name              ✅ extracted
  ├── description       ✅ extracted
  ├── assetType         ✅ extracted (System only)
  ├── validTime         ✅ extracted
  ├── systemKind@link   ❌ DROPPED (System)
  ├── platform@link     ❌ DROPPED (Deployment)
  ├── deployedSystems@link ❌ DROPPED (Deployment)
  └── sampledFeature@link  ❌ DROPPED (SamplingFeature)

Impact: Consumers cannot access association data through the library's typed models. The ogc-csapi-explorer had to implement a 105-line tryLinkFallback() workaround that bypasses the library entirely to read @link fields from raw JSON. When servers don't support cross-resource navigation endpoints (e.g., OSH returns 400 for /systems/{id}/procedures), @link is the only mechanism for discovering resource associations.

Findings Report

📄 Issue #109 Findings Report — Full source code review, risk assessment, precedent analysis, and implementation recommendation.

Files to Create or Modify

File Action Est. Lines Purpose
src/ogc-api/csapi/formats/geojson.ts Modify ~25 new Add CSAPIResourceRef import, 2 helper functions, @link extraction in 3 switch cases
src/ogc-api/csapi/formats/geojson.spec.ts Modify ~50 new Add test assertions for @link field extraction (present, absent, malformed)

Proposed Fix

Add tolerant conditional-spread extraction for @link properties in three of the four switch cases (Procedure has no @link fields). This follows the identical pattern already proven in the Part 2 parsers (Issue #103, part2.ts L163–164, L256–257).

Helper Functions (private, ~10 lines)

/** Type guard for @link objects — validates href is a string. */
function isCSAPIResourceRef(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && typeof (value as any).href === 'string';
}

/** Parse a raw @link object into a typed CSAPIResourceRef. */
function parseResourceRef(raw: Record<string, unknown>): CSAPIResourceRef {
  return {
    href: String(raw.href),
    ...(typeof raw.uid === 'string' ? { uid: raw.uid } : {}),
    ...(typeof raw.title === 'string' ? { title: raw.title } : {}),
    ...(typeof raw.rt === 'string' ? { rt: raw.rt } : {}),
  };
}

Extraction Pattern (per switch case)

// Scalar @link (System, SamplingFeature)
...(isCSAPIResourceRef(p['systemKind@link'])
  ? { systemKindLink: parseResourceRef(p['systemKind@link']) }
  : {}),

// Array @link (Deployment only)
...(Array.isArray(p['deployedSystems@link'])
  ? { deployedSystemsLink: (p['deployedSystems@link'] as unknown[]).filter(isCSAPIResourceRef).map(parseResourceRef) }
  : {}),

Design Notes

  • Depends on Part 1 (GeoJSON) TypeScript interfaces omit all @link association properties #108 (RESOLVED ✅) — interface fields systemKindLink, platformLink, deployedSystemsLink, sampledFeatureLink + CSAPIResourceRef type already exist
  • Same pattern as Part 2part2.ts L163–164 uses ...(typeof obj['system@id'] === 'string' ? { systemId: ... } : {})
  • Backward-compatible — all new fields are optional (?); existing tests pass unchanged
  • Tolerant — malformed @link data (missing href, non-object) is silently skipped

Scope — What NOT to Touch

Acceptance Criteria

  • geojson.ts imports CSAPIResourceRef from ../model.js
  • Private isCSAPIResourceRef() type guard and parseResourceRef() helper exist in geojson.ts
  • System case extracts systemKind@linksystemKindLink
  • Deployment case extracts platform@linkplatformLink and deployedSystems@linkdeployedSystemsLink
  • SamplingFeature case extracts sampledFeature@linksampledFeatureLink
  • Procedure case is unchanged (no @link fields)
  • Tests verify @link fields survive extraction when present
  • Tests verify @link fields are undefined when absent (tolerant extraction)
  • Tests verify malformed @link data is silently skipped
  • Existing tests still pass (npm test)
  • No lint errors

Dependencies

Blocked by: #108 — Part 1 interfaces omit @link fields (RESOLVED ✅ commit f8026ea)
Blocks: #110 — No @link resolution utilities (depends on parser extracting the data)


Operational Constraints

⚠️ MANDATORY: Before starting work on this issue, review docs/governance/AI_OPERATIONAL_CONSTRAINTS.md.

Key constraints:

  • Precedence: OGC specifications → AI Collaboration Agreement → This issue description → Existing code → Conversational context
  • No scope expansion: Fix the finding, nothing more
  • No server-specific branches: Fixes must be general-purpose
  • Minimal diffs: Prefer the smallest change that satisfies the acceptance criteria
  • Tolerant extraction (Postel's Law): Never gate on missing fields — undefined if absent, never throw

References

# Document What It Provides
1 Issue #109 Findings Report Full source code review, risk assessment, and recommendation
2 src/ogc-api/csapi/formats/geojson.ts Parser to modify — extractCSAPIFeature() L393–472
3 src/ogc-api/csapi/formats/part2.ts L163–164, L256–257 Blueprint — Part 2 @id extraction pattern (proven precedent)
4 src/ogc-api/csapi/model.ts L117–127, L345–475 Target interfaces with @link fields from #108
5 Gap Analysis Report Original discovery of the gap
6 OGC 23-001 §8.3, §8.5, §8.9, §16 Spec definitions for @link properties on Part 1 resources

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions