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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions