Skip to content

samplingFeature@link silently dropped on Observation POST despite HTTP 201 acceptance #2

@Sam-Bolling

Description

@Sam-Bolling

Summary

OSH accepts samplingFeature@link on Observation POST requests (HTTP 201) but silently discards the field — it is not returned on subsequent GET. This prevents CSAPI-native linkage between Observations and their Feature of Interest (FOI).

This is the same "silent acceptance + silent discard" pattern documented in Issue #1 for deployedSystems@link and deployment@link.

Operation Result
POST /samplingFeatures (create FOI) 201 Created — persists and round-trips correctly
POST /datastreams/{id}/observations with samplingFeature@link 201 Created — accepted without error
GET /observations/{id} (read back) samplingFeature@link absent — silently dropped

Environment

  • Server: OSH (OpenSensorHub) CSAPI instance on Oracle Cloud
  • API root: https://os4csapi-osh.duckdns.org/sensorhub/api
  • Date tested: 2026-03-03
  • Encoding tested: application/om+json

Context

The LOB Localizer system fuses Lines of Bearing from three acoustic sensor nodes to produce location-estimate Observations (rendered as gold ⊕ dots in the webapp). These observations need a featureOfInterest link to identify what they are about — in this case, a tracked UAS target.

Per SOSA/SSN, every sosa:Observation should reference a sosa:FeatureOfInterest. CSAPI Part 2 provides samplingFeature@link as the mechanism for this (OGC 23-002 §8.3.5).


Reproduction

1. Create SamplingFeature ✅

POST /sensorhub/api/samplingFeatures HTTP/1.1
Content-Type: application/json

{
  "type": "Feature",
  "properties": {
    "uid": "urn:os4csapi:foi:uas-track-001",
    "name": "UAS-Track-001",
    "description": "Tracked UAS target identity for the AZ String Alpha sensor network.",
    "featureType": "http://www.opengis.net/def/samplingFeatureType/OGC-OM/2.0/SF_SamplingPoint"
  }
}
HTTP 201 Created → Location: /samplingFeatures/040g

Read-back returns the full resource correctly. SamplingFeature CRUD works.

2. POST Observation with samplingFeature@link ✅ (accepted)

POST /sensorhub/api/datastreams/04f0/observations HTTP/1.1
Content-Type: application/om+json

{
  "phenomenonTime": "2026-03-04T03:13:18.886Z",
  "resultTime": "2026-03-04T03:13:18.886Z",
  "samplingFeature@link": {
    "href": "/samplingFeatures/040g",
    "uid": "urn:os4csapi:foi:uas-track-001",
    "title": "UAS-Track-001"
  },
  "result": {
    "timestamp": 1772593998.953,
    "trackId": 1,
    "estimatedLat": 31.658,
    "estimatedLon": -110.270,
    "cep50_m": 50.0,
    "classification": "UAS",
    "numContributingLobs": 3,
    "contributingSensors": "AZ-MA-1,AZ-MA-2,AZ-MA-3",
    "residual_m": 30.0
  }
}
HTTP 201 Created → Location: /observations/041sthkupk339jq9g0

3. Read back ❌ (field dropped)

GET /sensorhub/api/observations/041sthkupk339jq9g0 HTTP/1.1
Accept: application/om+json
{
  "id": "041sthkupk339jq9g0",
  "datastream@id": "04f0",
  "phenomenonTime": "2026-03-04T03:13:18.886Z",
  "resultTime": "2026-03-04T03:13:18.886Z",
  "result": { "..." }
}

samplingFeature@link is completely absent.


Cumulative Pattern

Third instance of the silent-accept/silent-discard behavior:

@link Field Resource Type Spec Reference Persists?
deployedSystems@link Deployment OGC 23-001 §8.5 ❌ Dropped (#1)
deployment@link DataStream OGC 23-002 §7.3.2 ❌ Dropped (#1)
samplingFeature@link Observation OGC 23-002 §8.3.5 ❌ Dropped (this issue)
platform@link Deployment OGC 23-001 §8.5 ✅ Works
system@link DataStream OGC 23-002 §7.3.2 ✅ Works (read-only)

Impact

Without persistence, clients cannot:

  • Query observations by FOI (GET /observations?samplingFeature={id})
  • Group observations by subject identity through the API
  • Build track-grouped displays without embedding track identity in the result blob as a workaround

Suggested Resolution

Option A — Persist the field: Store samplingFeature@link on Observation write, return it on read, support filtering by samplingFeature query parameter.

Option B — Reject unsupported fields: Return HTTP 422 when samplingFeature@link is provided but not supported, making the gap visible at write time.


Evidence

Full probe report with HTTP transcripts:

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