Skip to content

samplingFeature@link on Observations silently dropped: POST returns 201 but field not persisted #339

@Sam-Bolling

Description

@Sam-Bolling

Summary

OSH accepts samplingFeature@link on Observation POST (HTTP 201) but silently discards the field — it is absent from subsequent GET responses. This follows the same "silent-accept / silent-discard" pattern reported in #337 for deployedSystems@link and deployment@link.

SamplingFeature CRUD itself works correctly — the feature can be created, read, updated, and deleted. Only the association from Observation → SamplingFeature via samplingFeature@link is dropped.

Mechanism Spec Reference Behavior
samplingFeature@link on Observation OGC 23-002 §7.2.2 POST accepted (201) → field silently dropped on GET

Environment

  • Server: OSH deployed on Oracle Cloud (latest master branch as of 2026-03)
  • Date tested: 2026-03-05
  • Encoding tested: JSON (application/json)
  • Client tooling: curl (raw HTTP)

Advertised Conformance Classes

GET /conformance returns (observation-relevant subset):

http://www.opengis.net/spec/ogcapi-connectedsystems-2/1.0/conf/observation
http://www.opengis.net/spec/ogcapi-connectedsystems-2/1.0/conf/create-replace-delete
http://www.opengis.net/spec/ogcapi-connectedsystems-1/1.0/conf/sampling-feature

The server claims both conf/observation and conf/sampling-feature.


Reproduction: Step 1 — Create SamplingFeature (works)

Request

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

{
  "type": "Feature",
  "geometry": { "type": "Point", "coordinates": [-110.35, 31.63] },
  "properties": {
    "featureType": "sosa:Sample",
    "uid": "urn:os4csapi:foi:uas-track-001",
    "name": "UAS-Track-001",
    "description": "Track for UAS target observed by LOB intersection",
    "validTime": ["2026-03-05T00:00:00Z", ".."]
  }
}

Response

HTTP 201 Created
Location: .../samplingFeatures/040g

Read-back

GET /sensorhub/api/samplingFeatures/040g HTTP/1.1
Accept: application/geo+json

Returns the full feature with all fields intact. SamplingFeature CRUD works correctly.


Reproduction: Step 2 — POST Observation with samplingFeature@link (accepted but dropped)

Request

POST /sensorhub/api/datastreams/{dsId}/observations HTTP/1.1
Content-Type: application/json

{
  "phenomenonTime": "2026-03-05T20:00:00Z",
  "resultTime": "2026-03-05T20:00:00Z",
  "samplingFeature@link": {
    "href": "/sensorhub/api/samplingFeatures/040g",
    "uid": "urn:os4csapi:foi:uas-track-001",
    "type": "application/geo+json"
  },
  "result": {
    "bearing": 45.0,
    "signal_strength": -60
  }
}

Response

HTTP 201 Created
Location: .../observations/{obsId}

Read-back

GET /sensorhub/api/datastreams/{dsId}/observations?resultTime=latest&limit=1 HTTP/1.1
Accept: application/json
{
  "phenomenonTime": "2026-03-05T20:00:00Z",
  "resultTime": "2026-03-05T20:00:00Z",
  "result": {
    "bearing": 45.0,
    "signal_strength": -60
  }
}

samplingFeature@link is completely absent from the response. The association was silently discarded.


Observed pattern (cumulative with #337)

OSH persists @link fields that follow its internal parent→child hierarchy but drops cross-cutting associations:

Field Direction Persists?
platform@link on Deployment Deployment → System (bridge) ✅ Yes
system@link on DataStream DataStream → System (parent) ✅ Yes (read-only)
deployedSystems@link on Deployment Deployment → Systems (cross-cutting) ❌ Dropped (#337)
deployment@link on DataStream DataStream → Deployment (cross-cutting) ❌ Dropped (#337)
samplingFeature@link on Observation Observation → SamplingFeature (cross-cutting) ❌ Dropped (this issue)

This is the third instance of the silent-accept / silent-discard pattern for cross-cutting @link fields.


Impact

samplingFeature@link enables grouping observations by the real-world feature they describe (e.g., a UAS track, a weather station, a water body). Without it:

  • Clients cannot query observations by sampling feature via the API
  • /samplingFeatures/{id}/observations scoped queries are not possible
  • Track-level or target-level data grouping must be done entirely client-side
  • SamplingFeatures can be created but never meaningfully associated with data

Questions

  1. Is samplingFeature@link persistence on the roadmap? Is this a known gap?
  2. As with Deployment associations silently dropped: deployedSystems@link, deployment@link not persisted; deployment-scoped endpoints return 400 #337, could unsupported @link fields be rejected with a 4xx rather than silently accepted? The silent-drop pattern makes the gap invisible to clients.

Suggested resolution

Option A — Implement the association:
Persist samplingFeature@link on Observation resources so the Observation → SamplingFeature relationship round-trips correctly.

Option B — Accurately reflect current capabilities:
Reject samplingFeature@link with HTTP 422 instead of silently accepting it, so clients discover the limitation at write time rather than on read-back.

Either option resolves the immediate pain point (silent data loss on write).


Detailed probe report

Cross-reference: OS4CSAPI/osh-core#2 (same issue on our fork), #337 (deployment association gaps — same pattern)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions