diff --git a/src/adapter/basic.ts b/src/adapter/basic.ts index a8f9ff97..3bd39923 100644 --- a/src/adapter/basic.ts +++ b/src/adapter/basic.ts @@ -25,7 +25,7 @@ import { makeLogger, } from '../util' import { Requester } from '../util/requester' -import { AdapterTimeoutError } from '../validation/error' +import { AdapterError, AdapterTimeoutError } from '../validation/error' import { EmptyInputParameters } from '../validation/input-params' import { AdapterEndpoint } from './endpoint' import { @@ -409,6 +409,14 @@ export class Adapter, output: Readonly): AdapterError | undefined { + const endpoint = this.endpointsMap[req.requestContext.endpointName] + if (endpoint.customOutputValidation) { + return endpoint.customOutputValidation(output) + } + return undefined + } + /** * Function to serve as middleware to pass along the AdapterRequest to the appropriate Transport (acc. to the endpoint in the req.) * diff --git a/src/adapter/endpoint.ts b/src/adapter/endpoint.ts index af880b5b..bfc00968 100644 --- a/src/adapter/endpoint.ts +++ b/src/adapter/endpoint.ts @@ -10,6 +10,7 @@ import { AdapterEndpointInterface, AdapterEndpointParams, CustomInputValidator, + CustomOutputValidator, EndpointGenerics, EndpointRateLimitingConfig, RequestTransform, @@ -30,6 +31,7 @@ export class AdapterEndpoint implements AdapterEndpo rateLimiting?: EndpointRateLimitingConfig | undefined cacheKeyGenerator?: (data: TypeFromDefinition) => string customInputValidation?: CustomInputValidator + customOutputValidation?: CustomOutputValidator | undefined requestTransforms: RequestTransform[] overrides?: Record | undefined customRouter?: ( diff --git a/src/adapter/lwba.ts b/src/adapter/lwba.ts index a9a07483..1eb129c3 100644 --- a/src/adapter/lwba.ts +++ b/src/adapter/lwba.ts @@ -1,4 +1,5 @@ import { TransportGenerics } from '../transports' +import { AdapterError } from '../validation/error' import { AdapterEndpoint } from './endpoint' import { AdapterEndpointParams, PriceEndpointInputParametersDefinition } from './index' @@ -59,6 +60,25 @@ export class LwbaEndpoint extends AdapterEndpoin } } + // All LWBA requests must have a mid, bid, and ask + // Response validation ensures that we meet the invariant: bid <= mid <= ask + params.customOutputValidation = (output) => { + const data = output.data as LwbaResponseDataFields['Data'] + if (!data.mid || !data.bid || !data.ask) { + throw new AdapterError({ + statusCode: 500, + message: `Invariant voilation. LWBA response must contain mid, bid and ask prices.`, + }) + } + if (data.mid < data.bid || data.mid > data.ask) { + throw new AdapterError({ + statusCode: 500, + message: `Invariant voilation. Mid price must be between bid and ask prices.`, + }) + } + + return undefined + } super(params) } } diff --git a/src/adapter/types.ts b/src/adapter/types.ts index a47e5df7..0c58f705 100644 --- a/src/adapter/types.ts +++ b/src/adapter/types.ts @@ -4,7 +4,7 @@ import { Cache } from '../cache' import { AdapterConfig, BaseAdapterSettings, SettingsDefinitionMap } from '../config' import { AdapterRateLimitTier, RateLimiter } from '../rate-limiting' import { Transport, TransportGenerics, TransportRoutes } from '../transports' -import { AdapterRequest, LoggerFactory, SubscriptionSetFactory } from '../util' +import { AdapterRequest, AdapterResponse, LoggerFactory, ResponseGenerics, SubscriptionSetFactory } from '../util' import { Requester } from '../util/requester' import { InputParameters } from '../validation' import { AdapterError } from '../validation/error' @@ -127,6 +127,10 @@ export type CustomInputValidator = ( adapterSettings: T['Settings'], ) => AdapterError | undefined +export type CustomOutputValidator = ( + output: Readonly, +) => AdapterError | undefined + /** * Structure to describe a specific endpoint in an [[Adapter]] */ @@ -149,6 +153,8 @@ export interface BaseAdapterEndpointParams { /** Custom input validation. Void function that should throw AdapterInputError on validation errors */ customInputValidation?: CustomInputValidator + customOutputValidation?: CustomOutputValidator + /** Transforms that will apply to the request before submitting it through the adapter request flow */ requestTransforms?: RequestTransform[] diff --git a/src/index.ts b/src/index.ts index 0a467dba..361a2611 100644 --- a/src/index.ts +++ b/src/index.ts @@ -234,6 +234,12 @@ async function buildRestApi(adapter: Adapter) { req as AdapterRequest, reply as unknown as Promise, ) + + adapter.validateOutput( + req as AdapterRequest, + response + ) + return reply.code(response.statusCode || 200).send(response) }, })