Skip to content

Consolidate the 7 MapViewPage.vue resultTime=latest fallback call sites into a single helper #47

@Sam-Bolling

Description

@Sam-Bolling

Summary

demo/src/pages/MapViewPage.vue (in this repo) currently contains seven copy-pasted instances of the same 5–7 line "request latest observation; if empty, fall back to plain limit=1" pattern. Consolidate those into a single per-page (or per-module) helper so the pattern is implemented and tested in one place. This is purely a consumer-side ergonomic cleanup; no library change is required.

Context — why this lives here, not in the library

The fallback pattern was introduced because the OS4CSAPI/connected-systems-go server silently ignores the resultTime=latest query parameter (and, as documented in the upstream client's references catalog, the broader temporal-parameter family). Server-side root cause is tracked at connected-systems-go#11.

The TypeScript client (OS4CSAPI/ogc-client-CSAPI_2) considered shipping a built-in compatibility shim for this pattern in ogc-client-CSAPI_2#168, then closed that issue wontfix after Phase 8 triage. Reasons for the rejection (summary; full text in that issue's status banner):

  • Library is already spec-correct — fault lies entirely with the Go server.
  • A shim targeting only latest would be selective; the server ignores the entire temporal-parameter family, not just one keyword.
  • The shim is not transparent — consumers must still know which server class they target (the fallback URL is unsafe against SensorHub due to ascending default sort).
  • Adding a method whose deprecation is already scheduled at filing time is an anti-pattern.
  • N=1 consumer ergonomic problems belong in the consumer's repo.

This issue is the consequence of that last point: the ergonomic cleanup is worth doing, just here rather than in the library.

Affected sites in MapViewPage.vue

Per ogc-client-CSAPI_2#168, the seven sites are:

  1. buildSystemLocationCache() Phase C — observation-derived system locations
  2. refreshLocalizerOverlay() — localizer position fetch
  3. loadObservationTracks() — orbit/track latest-time anchor
  4. refreshLivePositions() — live position update cycle
  5. MapViewPage.vue — observation fetch path A (map rendering pipeline)
  6. MapViewPage.vue — observation fetch path B (map rendering pipeline)
  7. Bearing-line refresh cycle

(Line numbers will need to be verified by whoever picks this up.)

Each site currently looks roughly like:

const url = builder.getDataStreamObservations(dsId, { resultTime: 'latest', limit: 1 });
let obsRes = await apiFetch(url, { headers: { Accept: 'application/om+json' } });
if (obsRes.ok && obsRes.data && !(obsRes.data.items?.[0] || obsRes.data[0])) {
  const fallbackUrl = builder.getDataStreamObservations(dsId, { limit: 1 });
  obsRes = await apiFetch(fallbackUrl, { headers: { Accept: 'application/om+json' } });
}

Recommended direction

Implementation is the maintainer's call. The shape below is one option; the goal is one place where this lives, not a specific API.

Add a small page-local helper, e.g. in MapViewPage.vue's <script setup> block or a sibling useLatestObservation.ts composable:

async function fetchLatestObservation(
  builder: CSAPIQueryBuilder,
  datastreamId: string,
  apiFetch: ApiFetch,
): Promise<Observation | null> {
  const headers = { Accept: 'application/om+json' };
  const primary = builder.getDataStreamObservations(datastreamId, {
    resultTime: 'latest',
    limit: 1,
  });
  let res = await apiFetch(primary, { headers });
  const items = res.data?.items ?? res.data ?? [];
  if (res.ok && items.length === 0) {
    // Server may silently ignore resultTime=latest (e.g. connected-systems-go);
    // fall back to plain limit=1 — relies on server's default sort being
    // descending (true for Go; NOT TRUE for SensorHub — see notes below).
    const fallback = builder.getDataStreamObservations(datastreamId, { limit: 1 });
    res = await apiFetch(fallback, { headers });
  }
  const first = (res.data?.items ?? res.data ?? [])[0] ?? null;
  return first;
}

Then each of the seven sites becomes a single call. Caveats to capture in the JSDoc on this helper:

  • The fallback path only works against servers whose default sort is descending by resultTime. Currently true for connected-systems-go; not true for OSH/SensorHub (ascending default). If the Explorer ever needs to support targeting SensorHub through the same code path, the helper needs a sort hint (or the fallback needs to be disabled when the server class is known to sort ascending).
  • This helper is server-quirk compensation, not idiomatic CSAPI usage. When connected-systems-go#11 ships, the helper can be simplified to just the primary path (and the fallback removed).

Out of scope

  • ❌ Modifying the upstream library (ogc-client-CSAPI_2) — see #168 for the closure rationale.
  • ❌ Re-implementing the workaround for other temporal parameters (phenomenonTime, issueTime, etc.) — only resultTime=latest is currently used by MapViewPage.vue. If/when other temporal parameters are needed, generalize then; don't pre-build.
  • ❌ Adding a "server class" detection mechanism — the helper documents the constraint in JSDoc; runtime detection is over-engineering for the Explorer's current single-target deployment.

Acceptance criteria

  • One helper function (or composable) exists, with JSDoc documenting the server-quirk rationale and the SensorHub-incompatibility caveat.
  • All seven sites in MapViewPage.vue call the helper.
  • Behavior is identical to the current copy-pasted blocks (no regression).
  • When connected-systems-go#11 ships and the Go server starts honoring resultTime=latest, the helper's fallback branch should become a no-op (i.e. the primary URL returns the data and the helper returns from the first apiFetch). This should be verified by re-running the Explorer against the fixed server when available.

References

# Source Role
1 OS4CSAPI/ogc-client-CSAPI_2#168 Library-side decision to close wontfix and push this work into the consumer repo
2 OS4CSAPI/connected-systems-go#11 Server-side root cause; deprecation trigger for the helper
3 docs/research/references.md — Known Server Conformance Gaps Catalog of empirically observed Go server gaps; "Gap 1" entry covers this exact behavior
4 OGC 23-002r0 §13.3.2 /req/advanced-filtering/obs-by-resulttime statement D Spec the server should honor and currently doesn't

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions