Skip to content

Provider adapter system

Provider-specific behavior implements one interface outside route handlers:

class ProviderAdapter(ABC):
    async def fetch_location(self, query: LocationQuery) -> ProviderPayload: ...
    def normalize_location(self, query, payload) -> LocationResponse: ...
    def raw_location(self, query, payload) -> RawProviderResponse: ...

An adapter owns provider input validation, request construction, timeout/retry behavior, raw parsing, canonical mapping, citations, warnings, licensing fields, and operational behavior.

Laboratory collections use a sibling PedonProviderAdapter contract with bounded search, one-pedon detail, normalization, and raw-detail methods. This separation prevents a sampled pedon from being forced into mapped-point semantics.

USDA SDA implementation

The first adapter resolves a WGS84 point through an official SDA spatial table function, joins to map unit, legend, major component, and overlapping horizon tables, and selects only allowlisted columns. It limits provider rows to 500 and rejects unsupported properties before the request.

Routine tests use httpx.MockTransport; they do not call USDA. An integration test is skipped unless OPENSOIL_RUN_LIVE_TESTS=1.

NCSS Lab Data Mart implementation

The experimental adapter queries allowlisted SDA laboratory tables. A bounded bbox search returns GeoJSON point features; detail requests join one provider pedon to overlapping laboratory layers and selected physical/chemical fields. Preparation selection prefers the provider's S preparation, and queries are limited by spatial extent, row count, pedon key, property count, and depth.

The adapter preserves method codes and reported strings. Censored values keep their qualifier and reporting limit while the canonical numeric value remains null. Companion morphological data are not part of the selected SDA interface and are never silently joined.

Adapter safety rules

  • Never expose arbitrary SQL, URLs, table names, or provider credentials.
  • Bound result size and accepted spatial extent.
  • Use explicit timeouts and only limited retries for transient failures.
  • Preserve the original response when terms allow.
  • Never relabel modeled or mapped data as observed.
  • Record transformations; reject unknown conversions.
  • Treat a null provider field as unknown, not zero.
  • Keep provider failures structured and free of stack traces.
  • Add fixtures for success, empty data, malformed data, timeout, and failure.