Skip to content

Phase 3, Task 14: SWE Common Main Parser #27

@Sam-Bolling

Description

@Sam-Bolling

Task

Create the SWE Common Main Parser (parser.ts) — the central orchestrator that discriminates component types, detects encodings, delegates to sub-parsers (simple components, DataRecord, DataArray), and validates parsed structures against DataComponent schema definitions.

ROADMAP Reference: Phase 3, Task 14 — SWE Common Main Parser (~2-3 hours, High complexity)


Files to Create or Modify

File Action Est. Lines Purpose
src/ogc-api/csapi/formats/swecommon/parser.ts Create ~500-700 Main SWE Common 3.0 parser with type discrimination, encoding detection, schema validation
src/ogc-api/csapi/formats/swecommon/parser.spec.ts Create ~200-300 Main parser tests — type discrimination, encoding detection, schema validation, error handling

Blueprint Reference

Follow the EDR pattern in src/ogc-api/edr/model.ts (126 lines) for file structure and src/ogc-api/edr/model.spec.ts (42 lines) for test patterns.

Scope — What to Implement

Main Parser Entry Point

parseSWEComponent(json: unknown): SWEDataComponent

Parse any SWE Common 3.0 DataComponent by discriminating on the type property. This is the primary entry point for all SWE Common parsing. It reads the type discriminator from the input JSON and delegates to the appropriate sub-parser.

Type discrimination table:

type value Delegate to Source
'Quantity' parseSimpleComponent() (Issue #24) Simple scalar
'Count' parseSimpleComponent() (Issue #24) Simple scalar
'Boolean' parseSimpleComponent() (Issue #24) Simple scalar
'Text' parseSimpleComponent() (Issue #24) Simple scalar
'Time' parseSimpleComponent() (Issue #24) Simple scalar
'Category' parseSimpleComponent() (Issue #24) Simple scalar
'QuantityRange' parseSimpleComponent() (Issue #24) Range component
'CountRange' parseSimpleComponent() (Issue #24) Range component
'TimeRange' parseSimpleComponent() (Issue #24) Range component
'CategoryRange' parseSimpleComponent() (Issue #24) Range component
'DataRecord' parseDataRecord() (Issue #25) Complex aggregate
'DataArray' parseDataArray() (Issue #26) Complex aggregate
'Vector' parseVector() (this file) Complex aggregate
'Matrix' parseMatrix() (this file) Complex aggregate
'DataChoice' parseDataChoice() (this file) Complex aggregate
'Geometry' parseGeometry() (this file) Complex aggregate

Error handling: Unknown type values should produce a meaningful error naming the unrecognized type.

Complex Component Parsers (in this file)

The main parser file contains parsers for the complex components not covered by dedicated sub-parser files (Issues #24-#26):

parseVector(json: unknown): Vector

  • Parse a Vector component: positional vector with coordinate reference system
  • Properties: type: 'Vector', referenceFrame: string (required URI — the CRS), localFrame?: string, coordinates: DataField[] (named coordinate fields, same SoftNamedProperty wrapper as DataRecord fields)
  • Required per spec: type, referenceFrame, label, coordinates

parseMatrix(json: unknown): Matrix

  • Parse a Matrix component: extends DataArray with reference frame
  • Properties: inherits all DataArray properties plus referenceFrame: string (required URI), localFrame?: string
  • Required per spec: type, referenceFrame, elementType

parseDataChoice(json: unknown): DataChoice

  • Parse a DataChoice component: choice of two or more alternative data components (disjoint union)
  • Properties: type: 'DataChoice', choiceValue?: object, items: DataField[] (named alternative components, same SoftNamedProperty wrapper)
  • Required per spec: type, items

parseGeometry(json: unknown): Geometry

  • Parse a Geometry component: geometric data using GeoJSON geometry types
  • Properties: type: 'Geometry', srs?: string (spatial reference system URI), value?: GeoJSON.Geometry
  • Required per spec: type

Encoding Detection

detectEncoding(json: unknown): DataEncoding | undefined

Detect the encoding type from a DataArray or observation result structure. Reads the encoding.type discriminator:

  • 'JSONEncoding' → JSON encoding with recordsAsArrays and vectorsAsArrays options
  • 'TextEncoding' → Text/CSV encoding with separator configuration
  • 'BinaryEncoding' → Binary encoding with byte order and member specifications
  • 'XMLEncoding' → XML encoding (recognized but low priority, preserve structure)
  • No encoding present → return undefined

This delegates to parseEncoding() from Issue #26 (data-array.ts) for the actual parsing.

Schema Validation

validateAgainstSchema(value: unknown, schema: SWEDataComponent): ValidationResult

Validate a parsed observation result value against a DataStream's SWE Common schema (DataComponent definition). Returns a validation result indicating success or failure with error details.

Validation checks:

  • Structure match: Value structure matches schema structure (e.g., DataRecord with matching field names)
  • Type match: Value types match component types (number for Quantity, integer for Count, boolean for Boolean, string for Text/Category/Time)
  • Range validation: Numeric values within constraint.values AllowedValues ranges
  • Token validation: Category/Text values match constraint.values AllowedTokens list
  • Array dimensions: Array value count consistent with elementCount when specified
  • Required fields: All required schema fields present in values
  • Unit consistency: UoM codes present when schema specifies them

ValidationResult structure:

interface ValidationResult {
  valid: boolean;
  errors: ValidationError[];
}

interface ValidationError {
  path: string;       // JSON path to the problematic value (e.g., "fields[0].value")
  message: string;    // Human-readable error description
  code: string;       // Machine-readable error code (e.g., "RANGE_VIOLATION", "TYPE_MISMATCH")
}

Error Handling

  • Missing type property → error with guidance about expected discriminator values
  • Unknown type value → error naming the unrecognized type and listing valid types
  • Sub-parser delegation failures → wrap with context (which component type, which field)
  • Validation failures → return ValidationResult with structured errors (not thrown exceptions)
  • Null/undefined input → error
  • Non-object input → error

JSDoc Requirements

  • Document parseSWEComponent with @param, @returns, full type discrimination table, and usage example
  • Document each complex component parser (parseVector, parseMatrix, parseDataChoice, parseGeometry)
  • Document detectEncoding with examples of each encoding type
  • Document validateAgainstSchema with example schema and value validation
  • Add @see links to OGC SWE Common 3.0 (24-014) for each component type
  • Follow the JSDoc style in src/ogc-api/edr/model.ts

Testing Requirements

  • Create src/ogc-api/csapi/formats/swecommon/parser.spec.ts (~200-300 lines)
  • Type discrimination tests:
    • Each simple component type dispatched correctly (Quantity, Count, Boolean, Text, Time, Category)
    • Each range type dispatched correctly (QuantityRange, CountRange, TimeRange, CategoryRange)
    • DataRecord dispatched to parseDataRecord
    • DataArray dispatched to parseDataArray
    • Vector, Matrix, DataChoice, Geometry parsed correctly
    • Unknown type → meaningful error
  • Encoding detection tests:
    • JSON encoding detected and parsed
    • Text encoding detected and parsed
    • Binary encoding detected and parsed
    • XMLEncoding recognized
    • Missing encoding → undefined
  • Schema validation tests:
    • Valid DataRecord value against DataRecord schema → passes
    • Type mismatch (string where number expected) → validation error
    • Range violation (value outside AllowedValues) → validation error
    • Missing required field → validation error
    • Array dimension mismatch → validation error
  • Error handling tests:
    • Null/undefined input → error
    • Non-object input → error
    • Missing type → error
    • Nested errors include context path
  • Follow test patterns from src/ogc-api/edr/model.spec.ts

Scope — What NOT to Touch

Acceptance Criteria

  • parser.ts exists with parseSWEComponent() as the main entry point
  • Type discrimination correctly dispatches all 16 component types
  • parseVector(), parseMatrix(), parseDataChoice(), parseGeometry() implemented
  • detectEncoding() recognizes JSON, Text, Binary, and XML encoding types
  • validateAgainstSchema() returns structured ValidationResult with error details
  • Validation covers structure match, type match, range, tokens, array dimensions, required fields
  • Unknown types produce meaningful errors listing valid options
  • All new code has complete JSDoc documentation with @see spec references
  • parser.spec.ts exists with tests for type discrimination, encoding detection, schema validation, and error handling
  • Existing tests still pass (npm test)
  • No lint errors

Dependencies

Blocked by: Issue #17 — SWE Common Types (all interfaces), Issue #24 — Simple Components Parser, Issue #25 — DataRecord Parser, Issue #26 — DataArray Parser
Blocks: Issue #28 — SWE Common Index (re-exports parser functions)


Operational Constraints

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

Key constraints for this task:

  • Precedence: OGC specifications → AI Collaboration Agreement → This issue description → Existing code → Conversational context
  • No scope expansion: Do not infer unstated requirements or add unrequested features
  • No refactoring: Do not rename, restructure, or "improve" code outside this issue's scope
  • Minimal diffs: Prefer the smallest change that satisfies the acceptance criteria
  • Ask when unclear: If intent is ambiguous, stop and ask for clarification

References

Read these documents before starting implementation. They are ordered by priority.

Primary References (must read)

# Document Section/Lines What It Provides
1 docs/planning/csapi-implementation-guide.md Lines 3043-3130 (SWE Common Handler) Full specification of all data components, encodings, schema validation requirements, and advanced features
2 docs/planning/csapi-implementation-guide.md Lines 2098-2120 (File Structure) File organization showing parser.ts (~500-700 lines)
3 docs/planning/csapi-implementation-guide.md Lines 2804-2821 (SWEDataComponent union) All 16 component types in the discriminated union
4 src/ogc-api/edr/model.ts Full file (126 lines) Blueprint — file structure, JSDoc style, export conventions
5 src/ogc-api/edr/model.spec.ts Full file (42 lines) Blueprint — test structure and patterns

Upstream Type/Import References (files this task imports from)

# Document What to Import
1 src/ogc-api/csapi/formats/swecommon/types.ts SWEDataComponent, DataRecord, DataArray, Vector, Matrix, DataChoice, Geometry, DataEncoding, ValidationResult, ValidationError, and all component interfaces
2 src/ogc-api/csapi/formats/swecommon/components.ts parseSimpleComponent — for simple/range type delegation
3 src/ogc-api/csapi/formats/swecommon/data-record.ts parseDataRecord — for DataRecord delegation
4 src/ogc-api/csapi/formats/swecommon/data-array.ts parseDataArray, parseEncoding — for DataArray delegation and encoding detection

Research References (context, not required reading)

# Document What It Provides
1 docs/research/requirements/csapi-datatype-schema-requirements.md Complete analysis of SWE Common data types
2 docs/research/testing/findings/04-implementation-guide-testing-requirements.md Testing requirements and fixture definitions

Specification References (for @see links and field accuracy)

# Document Use
1 OGC SWE Common 3.0 (24-014) Normative specification for all data components, encoding methods, and validation rules
2 OGC API - Connected Systems Part 2 (23-002) DataStream schema and Observation result usage context
3 docs/research/standards/ogcapi-connectedsystems-2.bundled.oas31.yaml OpenAPI schema — AnyComponent (L7606, oneOf *ref_36), Vector (L1061, required: type, referenceFrame, label, coordinates), Matrix (L1329), DataChoice (L1351), Geometry (L1384), encoding types (L7620-7633)

Convention Quick Reference

Rule Example
Use .js extension for relative imports import { X } from './file.js'
Use import type for interfaces/types import type { Y } from './model.js'
Three-tier hierarchy: import from lower tiers only shared → ogc-api → csapi
Named exports for types and utilities export interface Z { ... }
as const arrays for enum-like values export const XTypes = [...] as const
HTTP mocking: globalThis.fetch = jest.fn() Never use nock, msw, or other libraries
Meaningful tests only Verify behavior, not that code runs without throwing

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions