diff --git a/docker-compose.yml b/docker-compose.yml index 211d385e..8218ecad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,6 +41,10 @@ services: COMPANIES_HOUSE_KEY: COMPANIES_HOUSE_MAX_REDIRECTS: COMPANIES_HOUSE_TIMEOUT: + DUN_AND_BRADSTREET_URL: + DUN_AND_BRADSTREET_KEY: + DUN_AND_BRADSTREET_MAX_REDIRECTS: + DUN_AND_BRADSTREET_TIMEOUT: API_KEY: healthcheck: test: ["CMD", "curl", "-f", "http://localhost:${PORT}"] diff --git a/src/config/dun-and-bradstreet.config.ts b/src/config/dun-and-bradstreet.config.ts new file mode 100644 index 00000000..689e98b9 --- /dev/null +++ b/src/config/dun-and-bradstreet.config.ts @@ -0,0 +1,20 @@ +import { registerAs } from '@nestjs/config'; +import { DUN_AND_BRADSTREET } from '@ukef/constants'; +import { getIntConfig } from '@ukef/helpers/get-int-config'; + +export interface DunAndBradstreetConfig { + baseUrl: string; + key: string; + maxRedirects: number; + timeout: number; +} + +export default registerAs( + DUN_AND_BRADSTREET.CONFIG.KEY, + (): DunAndBradstreetConfig => ({ + baseUrl: process.env.DUN_AND_BRADSTREET_URL, + key: process.env.DUN_AND_BRADSTREET_KEY, + maxRedirects: getIntConfig(process.env.DUN_AND_BRADSTREET_MAX_REDIRECTS, 5), + timeout: getIntConfig(process.env.DUN_AND_BRADSTREET_TIMEOUT, 30000), + }), +); diff --git a/src/config/index.ts b/src/config/index.ts index dced8537..8b63918c 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -2,7 +2,8 @@ import AppConfig from './app.config'; import CompaniesHouseConfig from './companies-house.config'; import DatabaseConfig from './database.config'; import DocConfig from './doc.config'; +import DunAndBradstreetConfig from './dun-and-bradstreet.config'; import InformaticaConfig from './informatica.config'; import OrdnanceSurveyConfig from './ordnance-survey.config'; -export default [AppConfig, CompaniesHouseConfig, DocConfig, DatabaseConfig, InformaticaConfig, OrdnanceSurveyConfig]; +export default [AppConfig, CompaniesHouseConfig, DocConfig, DatabaseConfig, DunAndBradstreetConfig, InformaticaConfig, OrdnanceSurveyConfig]; diff --git a/src/constants/dun-and-bradstreet.constant.ts b/src/constants/dun-and-bradstreet.constant.ts new file mode 100644 index 00000000..3472c638 --- /dev/null +++ b/src/constants/dun-and-bradstreet.constant.ts @@ -0,0 +1,5 @@ +export const DUN_AND_BRADSTREET = { + CONFIG: { + KEY: 'dunAndBradstreet', + }, +}; diff --git a/src/constants/index.ts b/src/constants/index.ts index b518a01d..b30f9bde 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -21,6 +21,7 @@ export * from './companies-house.constant'; export * from './customers.constant'; export * from './database-name.constant'; export * from './date.constant'; +export * from './dun-and-bradstreet.constant'; export * from './enums'; export * from './geospatial.constant'; export * from './govuk-notify.constant'; diff --git a/src/helper-modules/dun-and-bradstreet/dun-and-bradstreet.module.ts b/src/helper-modules/dun-and-bradstreet/dun-and-bradstreet.module.ts new file mode 100644 index 00000000..7dc1c4e1 --- /dev/null +++ b/src/helper-modules/dun-and-bradstreet/dun-and-bradstreet.module.ts @@ -0,0 +1,27 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { DunAndBradstreetConfig } from '@ukef/config/dun-and-bradstreet.config'; +import { DUN_AND_BRADSTREET } from '@ukef/constants'; +import { HttpModule } from '@ukef/modules/http/http.module'; + +import { DunAndBradstreetService } from './dun-and-bradstreet.service'; + +@Module({ + imports: [ + HttpModule.registerAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (configService: ConfigService) => { + const { baseUrl, maxRedirects, timeout } = configService.get(DUN_AND_BRADSTREET.CONFIG.KEY); + return { + baseURL: baseUrl, + maxRedirects, + timeout, + }; + }, + }), + ], + providers: [DunAndBradstreetService], + exports: [DunAndBradstreetService], +}) +export class DunAndBradstreetModule {} diff --git a/src/helper-modules/dun-and-bradstreet/dun-and-bradstreet.service.test.ts b/src/helper-modules/dun-and-bradstreet/dun-and-bradstreet.service.test.ts new file mode 100644 index 00000000..453d22bd --- /dev/null +++ b/src/helper-modules/dun-and-bradstreet/dun-and-bradstreet.service.test.ts @@ -0,0 +1,111 @@ +import { HttpService } from '@nestjs/axios'; +import { ConfigService } from '@nestjs/config'; +import { RandomValueGenerator } from '@ukef-test/support/generator/random-value-generator'; +import { AxiosError } from 'axios'; +import { resetAllWhenMocks, when } from 'jest-when'; +import { of, throwError } from 'rxjs'; + +import { DunAndBradstreetService } from './dun-and-bradstreet.service'; +import { DunAndBradstreetException } from './exception/dun-and-bradstreet.exception'; + +describe('DunAndBradstreetService', () => { + let httpServiceGet: jest.Mock; + let configServiceGet: jest.Mock; + let service: DunAndBradstreetService; + + const valueGenerator = new RandomValueGenerator(); + + const testRegistrationNumber = '0' + valueGenerator.stringOfNumericCharacters({ length: 7 }); + const expectedAccessToken = 'TEST_ACCESS_TOKEN'; + const getAccessTokenMethodMock = jest + .spyOn(DunAndBradstreetService.prototype as any, 'getAccessToken') + .mockImplementation(() => Promise.resolve(expectedAccessToken)); + + const dunAndBradstreetpath = `/v1/match/cleanseMatch?countryISOAlpha2Code=GB®istrationNumber=${testRegistrationNumber}`; + const expectedDunsNumber = '123456789'; + const getDunsNumberDunAndBradstreetResponse = { + matchCandidates: [ + { + organization: { + duns: expectedDunsNumber, + }, + }, + ], + }; + + const expectedHttpServiceGetArguments: [string, object] = [ + dunAndBradstreetpath, + { + headers: { + Authorization: `Bearer ${expectedAccessToken}`, + }, + }, + ]; + + const expectedHttpServiceGetResponse = of({ + data: getDunsNumberDunAndBradstreetResponse, + status: 200, + statusText: 'OK', + config: undefined, + headers: undefined, + }); + + beforeAll(() => { + const httpService = new HttpService(); + httpServiceGet = jest.fn(); + httpService.get = httpServiceGet; + + const configService = new ConfigService(); + configServiceGet = jest.fn().mockReturnValue({ key: 'TEST API_KEY' }); + configService.get = configServiceGet; + + service = new DunAndBradstreetService(httpService, configService); + }); + + beforeEach(() => { + resetAllWhenMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('getDunAndBradstreetNumberByRegistrationNumber', () => { + it('calls the Dun and Bradstreet API with the correct arguments', async () => { + when(httpServiceGet) + .calledWith(...expectedHttpServiceGetArguments) + .mockReturnValueOnce(expectedHttpServiceGetResponse); + + await service.getDunAndBradstreetNumberByRegistrationNumber(testRegistrationNumber); + + expect(getAccessTokenMethodMock).toHaveBeenCalledTimes(1); + expect(httpServiceGet).toHaveBeenCalledTimes(1); + expect(httpServiceGet).toHaveBeenCalledWith(...expectedHttpServiceGetArguments); + }); + + it('returns the results when the Dun and Bradstreet API returns a 200 response with results', async () => { + when(httpServiceGet) + .calledWith(...expectedHttpServiceGetArguments) + .mockReturnValueOnce(expectedHttpServiceGetResponse); + + const response = await service.getDunAndBradstreetNumberByRegistrationNumber(testRegistrationNumber); + + expect(getAccessTokenMethodMock).toHaveBeenCalledTimes(1); + expect(response).toBe(expectedDunsNumber); + }); + + it('throws a DunAndBradstreetException if the Dun and Bradstreet API returns an unknown error response', async () => { + const axiosError = new AxiosError(); + when(httpServiceGet) + .calledWith(...expectedHttpServiceGetArguments) + .mockReturnValueOnce(throwError(() => axiosError)); + + const getDunsNumberPromise = service.getDunAndBradstreetNumberByRegistrationNumber(testRegistrationNumber); + + expect(getAccessTokenMethodMock).toHaveBeenCalledTimes(1); + await expect(getDunsNumberPromise).rejects.toBeInstanceOf(DunAndBradstreetException); + await expect(getDunsNumberPromise).rejects.toThrow('Failed to get response from Dun and Bradstreet API'); + await expect(getDunsNumberPromise).rejects.toHaveProperty('innerError', axiosError); + }); + }); +}); diff --git a/src/helper-modules/dun-and-bradstreet/dun-and-bradstreet.service.ts b/src/helper-modules/dun-and-bradstreet/dun-and-bradstreet.service.ts new file mode 100644 index 00000000..75c9623a --- /dev/null +++ b/src/helper-modules/dun-and-bradstreet/dun-and-bradstreet.service.ts @@ -0,0 +1,56 @@ +import { HttpService } from '@nestjs/axios'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { DunAndBradstreetConfig } from '@ukef/config/dun-and-bradstreet.config'; +import { DUN_AND_BRADSTREET } from '@ukef/constants'; +import { HttpClient } from '@ukef/modules/http/http.client'; + +import { createWrapDunAndBradstreetHttpGetErrorCallback } from './wrap-dun-and-bradstreet-http-error-callback'; + +@Injectable() +export class DunAndBradstreetService { + private readonly httpClient: HttpClient; + private readonly encodedKey: string; + + constructor(httpService: HttpService, configService: ConfigService) { + this.httpClient = new HttpClient(httpService); + const { key } = configService.get(DUN_AND_BRADSTREET.CONFIG.KEY); + this.encodedKey = key; + } + + async getDunAndBradstreetNumberByRegistrationNumber(registrationNumber: string): Promise { + const path = `/v1/match/cleanseMatch?countryISOAlpha2Code=GB®istrationNumber=${registrationNumber}`; + const accessToken = await this.getAccessToken(); + + const { data } = await this.httpClient.get({ + path, + headers: { + Authorization: 'Bearer ' + accessToken, + }, + onError: createWrapDunAndBradstreetHttpGetErrorCallback({ + messageForUnknownError: 'Failed to get response from Dun and Bradstreet API', + knownErrors: [], + }), + }); + return data?.matchCandidates[0]?.organization?.duns; + } + + private async getAccessToken(): Promise { + const path = '/v3/token'; + const response = await this.httpClient.post({ + path, + body: { + grant_type: 'client_credentials', + }, + headers: { + Authorization: 'Basic ' + this.encodedKey, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + onError: createWrapDunAndBradstreetHttpGetErrorCallback({ + messageForUnknownError: 'Failed to get access token', + knownErrors: [], + }), + }); + return response.data.accessToken; + } +} diff --git a/src/helper-modules/dun-and-bradstreet/exception/dun-and-bradstreet.exception.test.ts b/src/helper-modules/dun-and-bradstreet/exception/dun-and-bradstreet.exception.test.ts new file mode 100644 index 00000000..ecb33e4d --- /dev/null +++ b/src/helper-modules/dun-and-bradstreet/exception/dun-and-bradstreet.exception.test.ts @@ -0,0 +1,28 @@ +import { RandomValueGenerator } from '@ukef-test/support/generator/random-value-generator'; + +import { DunAndBradstreetException } from './dun-and-bradstreet.exception'; + +describe('DunAndBradstreetException', () => { + const valueGenerator = new RandomValueGenerator(); + const message = valueGenerator.string(); + + it('exposes the message it was created with', () => { + const exception = new DunAndBradstreetException(message); + + expect(exception.message).toBe(message); + }); + + it('exposes the name of the exception', () => { + const exception = new DunAndBradstreetException(message); + + expect(exception.name).toBe('DunAndBradstreetException'); + }); + + it('exposes the inner error it was created with', () => { + const innerError = new Error(); + + const exception = new DunAndBradstreetException(message, innerError); + + expect(exception.innerError).toBe(innerError); + }); +}); diff --git a/src/helper-modules/dun-and-bradstreet/exception/dun-and-bradstreet.exception.ts b/src/helper-modules/dun-and-bradstreet/exception/dun-and-bradstreet.exception.ts new file mode 100644 index 00000000..a5686d9d --- /dev/null +++ b/src/helper-modules/dun-and-bradstreet/exception/dun-and-bradstreet.exception.ts @@ -0,0 +1,9 @@ +export class DunAndBradstreetException extends Error { + constructor( + message: string, + public readonly innerError?: Error, + ) { + super(message); + this.name = this.constructor.name; + } +} diff --git a/src/helper-modules/dun-and-bradstreet/known-errors.ts b/src/helper-modules/dun-and-bradstreet/known-errors.ts new file mode 100644 index 00000000..47a4438c --- /dev/null +++ b/src/helper-modules/dun-and-bradstreet/known-errors.ts @@ -0,0 +1,5 @@ +import { AxiosError } from 'axios'; + +export type KnownErrors = KnownError[]; + +type KnownError = { checkHasError: (error: Error) => boolean; throwError: (error: AxiosError) => never }; diff --git a/src/helper-modules/dun-and-bradstreet/wrap-dun-and-bradstreet-http-error-callback.ts b/src/helper-modules/dun-and-bradstreet/wrap-dun-and-bradstreet-http-error-callback.ts new file mode 100644 index 00000000..b2886b59 --- /dev/null +++ b/src/helper-modules/dun-and-bradstreet/wrap-dun-and-bradstreet-http-error-callback.ts @@ -0,0 +1,17 @@ +import { AxiosError } from 'axios'; +import { ObservableInput, throwError } from 'rxjs'; + +import { DunAndBradstreetException } from './exception/dun-and-bradstreet.exception'; +import { KnownErrors } from './known-errors'; + +type DunAndBradstreetHttpErrorCallback = (error: Error) => ObservableInput; + +export const createWrapDunAndBradstreetHttpGetErrorCallback = + ({ messageForUnknownError, knownErrors }: { messageForUnknownError: string; knownErrors: KnownErrors }): DunAndBradstreetHttpErrorCallback => + (error: Error) => { + if (error instanceof AxiosError && error?.response) { + knownErrors.forEach(({ checkHasError, throwError }) => checkHasError(error) && throwError(error)); + } + + return throwError(() => new DunAndBradstreetException(messageForUnknownError, error)); + }; diff --git a/src/logging/log-keys-to-redact.test.ts b/src/logging/log-keys-to-redact.test.ts index 155171f8..a5c0d2d1 100644 --- a/src/logging/log-keys-to-redact.test.ts +++ b/src/logging/log-keys-to-redact.test.ts @@ -14,6 +14,11 @@ describe('logKeysToRedact', () => { logKey: valueGenerator.string(), headersLogKey: valueGenerator.string(), }, + incomingResponse: { + logKey: valueGenerator.string(), + bodyLogKey: valueGenerator.string(), + accessTokenLogKey: valueGenerator.string(), + }, error: { logKey: valueGenerator.string(), sensitiveChildKeys: [valueGenerator.string(), valueGenerator.string()], diff --git a/src/logging/log-keys-to-redact.ts b/src/logging/log-keys-to-redact.ts index cf7d3c42..6a4bbda0 100644 --- a/src/logging/log-keys-to-redact.ts +++ b/src/logging/log-keys-to-redact.ts @@ -10,6 +10,12 @@ export interface LogKeysToRedactOptions { logKey: string; headersLogKey: string; }; + incomingResponse: { + logKey: string; + bodyLogKey: string; + accessTokenLogKey: string; + }; + error: { logKey: string; sensitiveChildKeys: string[]; @@ -20,13 +26,14 @@ export interface LogKeysToRedactOptions { }; } -export const logKeysToRedact = ({ redactLogs, clientRequest, outgoingRequest, error, dbError }: LogKeysToRedactOptions): string[] => { +export const logKeysToRedact = ({ redactLogs, clientRequest, outgoingRequest, incomingResponse, error, dbError }: LogKeysToRedactOptions): string[] => { if (!redactLogs) { return []; } const keys = [ ...getClientRequestLogKeysToRedact(clientRequest), ...getOutgoingRequestLogKeysToRedact(outgoingRequest), + ...getIncomingResponseLogKeysToRedact(incomingResponse), ...getErrorLogKeysToRedact(error), ...getDbErrorLogKeysToRedact(dbError), ]; @@ -39,10 +46,16 @@ const getClientRequestLogKeysToRedact = ({ logKey, headersLogKey }: LogKeysToRed buildKeyToRedact([logKey, headersLogKey]), ]; +const getIncomingResponseLogKeysToRedact = ({ logKey, bodyLogKey, accessTokenLogKey }: LogKeysToRedactOptions['incomingResponse']): string[] => [ + // We redact the client request body as they contain the Dun and Bradstreet access token + buildKeyToRedact([logKey, bodyLogKey, accessTokenLogKey]), +]; + const getOutgoingRequestLogKeysToRedact = ({ logKey, headersLogKey }: LogKeysToRedactOptions['outgoingRequest']): string[] => { return [ // We redact the outgoing request headers as they contain: // - our Basic auth details for Informatica + // - our Client auth details for Dun and Bradstreet buildKeyToRedact([logKey, headersLogKey]), ]; }; diff --git a/src/main.module.ts b/src/main.module.ts index 12c5daf8..6d063093 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -2,7 +2,14 @@ import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { APP_INTERCEPTOR } from '@nestjs/core'; import config from '@ukef/config'; -import { HEADERS_LOG_KEY, OUTGOING_REQUEST_LOG_KEY } from '@ukef/modules/http/http.constants'; +import { + ACCESS_TOKEN_LOG_KEY, + BODY_LOG_KEY, + CLIENT_REQUEST_LOG_KEY, + HEADERS_LOG_KEY, + INCOMING_RESPONSE_LOG_KEY, + OUTGOING_REQUEST_LOG_KEY, +} from '@ukef/modules/http/http.constants'; import { MdmModule } from '@ukef/modules/mdm.module'; import { LoggerModule } from 'nestjs-pino'; @@ -40,13 +47,18 @@ import { LoggingInterceptor } from './logging/logging-interceptor.helper'; redact: logKeysToRedact({ redactLogs: config.get('app.redactLogs'), clientRequest: { - logKey: 'req', - headersLogKey: 'headers', + logKey: CLIENT_REQUEST_LOG_KEY, + headersLogKey: HEADERS_LOG_KEY, }, outgoingRequest: { logKey: OUTGOING_REQUEST_LOG_KEY, headersLogKey: HEADERS_LOG_KEY, }, + incomingResponse: { + logKey: INCOMING_RESPONSE_LOG_KEY, + bodyLogKey: BODY_LOG_KEY, + accessTokenLogKey: ACCESS_TOKEN_LOG_KEY, + }, error: { logKey: 'err', sensitiveChildKeys: [ diff --git a/src/modules/companies/companies.module.ts b/src/modules/companies/companies.module.ts index 65955e6a..f86026e1 100644 --- a/src/modules/companies/companies.module.ts +++ b/src/modules/companies/companies.module.ts @@ -1,12 +1,13 @@ import { Module } from '@nestjs/common'; import { CompaniesHouseModule } from '@ukef/helper-modules/companies-house/companies-house.module'; +import { DunAndBradstreetModule } from '@ukef/helper-modules/dun-and-bradstreet/dun-and-bradstreet.module'; import { SectorIndustriesModule } from '../sector-industries/sector-industries.module'; import { CompaniesController } from './companies.controller'; import { CompaniesService } from './companies.service'; @Module({ - imports: [CompaniesHouseModule, SectorIndustriesModule], + imports: [CompaniesHouseModule, SectorIndustriesModule, DunAndBradstreetModule], controllers: [CompaniesController], providers: [CompaniesService], }) diff --git a/src/modules/http/http.client.ts b/src/modules/http/http.client.ts index 5e79b0a6..69a283ba 100644 --- a/src/modules/http/http.client.ts +++ b/src/modules/http/http.client.ts @@ -19,6 +19,23 @@ export class HttpClient { return this.responseFrom({ request: this.httpService.get(path, { headers }), onError }); } + post({ + path, + headers, + body, + onError, + }: { + path: string; + headers: RequestHeaders; + body: RequestBody; + onError: (error: Error) => ObservableInput; + }): Promise> { + return this.responseFrom({ + request: this.httpService.post(path, body, { headers }), + onError, + }); + } + private async responseFrom({ request, onError, diff --git a/src/modules/http/http.constants.ts b/src/modules/http/http.constants.ts index e0cf5df5..fb297f47 100644 --- a/src/modules/http/http.constants.ts +++ b/src/modules/http/http.constants.ts @@ -1,6 +1,8 @@ export const OUTGOING_REQUEST_LOG_KEY = 'outgoingRequest'; export const INCOMING_RESPONSE_LOG_KEY = 'incomingResponse'; +export const CLIENT_REQUEST_LOG_KEY = 'req'; export const BODY_LOG_KEY = 'data'; export const HEADERS_LOG_KEY = 'headers'; export const SENSITIVE_REQUEST_HEADER_NAMES = 'headers'; export const SENSITIVE_RESPONSE_HEADER_NAMES = 'headers'; +export const ACCESS_TOKEN_LOG_KEY = 'access_token'; diff --git a/test/docs/__snapshots__/get-docs-yaml.api-test.ts.snap b/test/docs/__snapshots__/get-docs-yaml.api-test.ts.snap index e4a7c47c..2eddbd42 100644 --- a/test/docs/__snapshots__/get-docs-yaml.api-test.ts.snap +++ b/test/docs/__snapshots__/get-docs-yaml.api-test.ts.snap @@ -29,27 +29,27 @@ paths: - name: source required: true in: query - example: GBP description: >- Source currency for exchange rate - Use ISO 3 alpha currency code standard. Only GBP and USD currencies are supported schema: + example: GBP type: string - name: target required: true in: query - example: AED description: >- Target currency for exchange rate - Use ISO 3 alpha currency code standard schema: + example: AED type: string - name: exchangeRateDate required: false in: query - example: '2021-01-26' description: Retrieve the exchange rate for a specific date schema: + example: '2021-01-26' type: string responses: '200': @@ -68,9 +68,9 @@ paths: - name: isoCode required: true in: path - example: GBP description: ISO Code schema: + example: GBP type: string responses: '200': @@ -91,33 +91,32 @@ paths: - name: companyReg required: false in: query - example: '06012345' description: >- The company registration number (in the country where the customer is registered) of the customer to search for. schema: + example: '06012345' type: string - name: partyUrn required: false in: query - example: '00302069' description: The unique UKEF id of the customer to search for. schema: pattern: ^\\d{8}$ + example: '00302069' type: string - name: name required: false in: query - example: Testing Systems Ltd description: >- The name of the customer to search for, allows partial matches, not case sensitive. schema: + example: Testing Systems Ltd type: string - name: fallbackToLegacyData required: false in: query - example: legacy-only description: >- Determines if the search results will include legacy records. If this value is 'yes' then we search for a Salesforce Account first @@ -125,6 +124,7 @@ paths: Party DB). schema: default: 'yes' + example: legacy-only enum: - 'yes' - 'no' @@ -186,23 +186,23 @@ paths: - name: startdate required: true in: query - example: '2017-07-04' description: Guarantee commencement date for a facility schema: + example: '2017-07-04' type: string - name: enddate required: true in: query - example: '2018-07-04' description: Guarantee expiry date for a facility schema: + example: '2018-07-04' type: string - name: productgroup required: true in: query - example: EW description: 'Two products are accepted: EW and BS' schema: + example: EW type: string responses: '200': @@ -349,11 +349,11 @@ paths: - name: active required: false in: query - example: 'Y' description: >- Optional filtering by field "active". If parameter is not provided result will include active and not active markets schema: + example: 'Y' enum: - 'Y' - 'N' @@ -361,11 +361,11 @@ paths: - name: search required: false in: query - example: korea description: >- Optional filtering by fields "marketName" or "isoCode". Partial matches are allowed and search is not case sensitive schema: + example: korea type: string responses: '200': @@ -401,18 +401,18 @@ paths: - name: type required: true in: query - example: 1 description: >- Id of UKEF ID type. Common types are: 1 for Deal/Facility, 2 for Party, 8 for Covenant schema: + example: 1 type: number - name: ukefId required: true in: query - example: '0030052431' description: UKEF ID to check schema: + example: '0030052431' type: string responses: '200': @@ -453,9 +453,9 @@ paths: - name: facilityId required: true in: path - example: '30000425' description: UKEF id for Facility, but without 00 at beginning schema: + example: '30000425' type: string responses: '200': @@ -476,16 +476,16 @@ paths: - name: ukefSectorId required: false in: query - example: '1010' description: Search by UKEF Sector id, returns multiple Industries schema: + example: '1010' type: string - name: ukefIndustryId required: false in: query - example: '02400' description: Search by UKEF Industry id, most likely returns 1 result schema: + example: '02400' type: string responses: '200': @@ -506,9 +506,9 @@ paths: - name: searchDate required: false in: query - example: '2023-03-01' description: Filter yield rates for specific date. Can go back to 2010-03-15 schema: + example: '2023-03-01' type: string responses: '200': @@ -532,13 +532,13 @@ paths: - name: postcode required: true in: query - example: SW1A 2AQ description: Postcode to search for schema: minLength: 5 maxLength: 8 pattern: >- ^[A-Za-z]{1,2}[\\dRr][\\dA-Za-z]?\\s?\\d[ABD-HJLNP-UW-Zabd-hjlnp-uw-z]{2}$ + example: SW1A 2AQ type: string responses: '200': @@ -562,11 +562,11 @@ paths: required: true in: query description: The Companies House registration number of the company to find. - example: '00000001' schema: minLength: 8 maxLength: 8 pattern: ^(([A-Z]{2}|[A-Z]\\d|\\d{2})(\\d{6}|\\d{5}[A-Z]))$ + example: '00000001' type: string responses: '200':