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
    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