All files / scripts/imf/transport sdmx.ts

100% Statements 7/7
100% Branches 4/4
100% Functions 1/1
100% Lines 7/7

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58                                                                                  9x 9x 9x 9x     9x 4x   9x              
/**
 * @module imf/transport/sdmx
 * @description IMF SDMX 3.0 REST transport (authenticated).
 *
 * SDMX 3.0 is the full IMF catalogue (IFS / BOP / DOTS / GFS_COFOG /
 * FM / MFS_IR / PCPS / ER / full WEO 9.0.0). Every `/data/...` request
 * must carry the `Ocp-Apim-Subscription-Key` header — the IMF Azure
 * APIM gateway masks missing-key responses as **HTTP 404**, see
 * `errors/http-error.ts` for the disambiguation logic.
 *
 * The auth key is read **only** in `config/auth.ts` — this module
 * receives it via parameter, never `process.env` directly.
 *
 * @author Hack23 AB
 * @license Apache-2.0
 */
 
import { normalizeSdmxPathForBase } from './path-normaliser.js';
import { fetchWithRetry } from './fetch-with-retry.js';
 
export interface SdmxTransportOptions {
  readonly baseURL: string;
  readonly timeout: number;
  readonly maxRetries: number;
  readonly userAgent: string;
  /** Empty string when no key configured — request still goes out so probes can detect it. */
  readonly subscriptionKey: string;
}
 
/**
 * Low-level SDMX 3.0 passthrough. Returns the raw JSON envelope from
 * the IMF SDMX endpoint. Consumers are responsible for interpreting
 * the SDMX dataMessage.
 *
 * @param pathWithQuery URL path starting with `/data/...` or `/structure/...`,
 *   optionally followed by a `?...` query string.
 */
export async function sdmxFetch(
  pathWithQuery: string,
  options: SdmxTransportOptions,
): Promise<unknown> {
  const normalized = normalizeSdmxPathForBase(options.baseURL, pathWithQuery);
  const separator = normalized.startsWith('/') ? '' : '/';
  const url = `${options.baseURL}${separator}${normalized}`;
  const headers: Record<string, string> = {
    Accept: 'application/vnd.sdmx.data+json;version=2.0.0',
  };
  if (options.subscriptionKey) {
    headers['Ocp-Apim-Subscription-Key'] = options.subscriptionKey;
  }
  return fetchWithRetry(url, {
    timeout: options.timeout,
    maxRetries: options.maxRetries,
    userAgent: options.userAgent,
    extraHeaders: headers,
  });
}