Skip to content

Commit 103c376

Browse files
committed
fix(data-source): resolve detail i18n tokens via producing providerId
DataSourceDetail tokens (e.g. value.undergroundGarage) leaked as raw keys because the client guessed the producing integration via a source-coverage heuristic that ties when integrations share a generic source like "osm". Stamp the producing provider id on the detail server-side and key both token resolution and the attribution footer off it, retiring the heuristic to a backward-compat fallback.
1 parent ddc9190 commit 103c376

3 files changed

Lines changed: 27 additions & 8 deletions

File tree

apps/web/src/components/panels/place/DataSourceSections.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,14 @@ function AttributionFooter({ detail }: { detail: DataSourceDetail }) {
208208
const tc = useTranslations("common");
209209
const registry = useIntegrationRegistry();
210210

211-
// Pick the data-source integration whose dataSources cover the most of
212-
// detail.sources. Several integrations declare "osm" as a sourceId, so a
213-
// first-match lookup would attribute a parking detail (sources include
214-
// apag + nrw-mobidrom-parking + osm) to ev-charging just because that's
215-
// the first integration whose dataSources mention "osm".
216-
const meta = pickIntegrationForSources(registry.getByDomain("data-source"), detail.sources);
211+
// Resolve the producing integration. The host stamps `providerId`, which is
212+
// authoritative; fall back to the source-coverage heuristic only for details
213+
// that predate the stamp or come from outside the data-source orchestrator.
214+
// The heuristic ties when integrations share a generic sourceId like "osm"
215+
// (parking, ev-charging, etc.), so it must not override a known providerId.
216+
const meta =
217+
(detail.providerId ? registry.get(detail.providerId) : undefined) ??
218+
pickIntegrationForSources(registry.getByDomain("data-source"), detail.sources);
217219
const html = meta?.dataSources ? buildSourceAttribution(meta.dataSources, detail.sources) : "";
218220
const detailAttributions = detail.attributions ?? [];
219221

@@ -298,7 +300,12 @@ export function DataSourceSections({ detail, domain }: Props) {
298300
const t = useTranslations("dataSources");
299301
const registry = useIntegrationRegistry();
300302
const meta = pickIntegrationForSources(registry.getByDomain("data-source"), detail.sources);
301-
const resolveT = useDataSourceI18nResolver(meta?.id ?? domain);
303+
// Scope token resolution to the integration that produced the detail. The
304+
// host stamps `providerId`; fall back to `domain` (this panel's owning
305+
// integration) before `meta` — `meta` is an attribution heuristic that ties
306+
// when sources share a generic prefix (e.g. "osm") and can pick an unrelated
307+
// integration, leaking raw token keys like `value.undergroundGarage`.
308+
const resolveT = useDataSourceI18nResolver(detail.providerId ?? domain ?? meta?.id);
302309
const header = resolveSourceHeader(detail, domain);
303310
const structuredSections = detail.sections.map((section) =>
304311
translateStructuredSection(section, resolveT),

integrations/data-source/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,11 @@ export function setup(ctx: IntegrationContext): void {
8686
return;
8787
}
8888
reply.header("Cache-Control", `public, max-age=${Math.min(detailTtl, 300)}`);
89+
// Stamp the producing provider id so the client resolves this detail's
90+
// I18nTokens against the right integration catalog. Done on the way out
91+
// (not inside the cached producer) so cached entries are stamped too.
8992
reply.send({
90-
data: envelope.data,
93+
data: { ...envelope.data, providerId: provider.id },
9194
attributions: envelope.attributions,
9295
freshness: envelope.freshness,
9396
});

packages/integration-framework/src/contracts/mobility-data-source-provider.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,15 @@ export interface OsmIdentity {
213213
export interface DataSourceDetail {
214214
id: string;
215215
sources: string[];
216+
/**
217+
* Id of the data-source provider that produced this detail (e.g. "parking",
218+
* "ev-charging"). Stamped by the host's data-source route so the client can
219+
* resolve this detail's `I18nToken`s against the correct integration string
220+
* catalog. `sources` is unreliable for this — it lists upstream feeds (often
221+
* a generic "osm") that several integrations share, so picking the catalog
222+
* by source coverage ties and selects the wrong integration.
223+
*/
224+
providerId?: string;
216225
name: string;
217226
coordinates: LngLat;
218227
/** Identity used by the place resolver to gate OSM snapping. See {@link OsmIdentity}. */

0 commit comments

Comments
 (0)