diff --git a/.eslintrc.json b/.eslintrc.json index 7688571f..1b37c0d3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -15,14 +15,14 @@ "plugin:eslint-comments/recommended", "plugin:optimize-regex/recommended", "plugin:switch-case/recommended", - "plugin:security/recommended", + "plugin:security/recommended-legacy", "plugin:import/recommended", "plugin:import/typescript", "prettier" ], "parserOptions": { "project": "tsconfig.json", - "ecmaVersion": 2020, + "ecmaVersion": "latest", "sourceType": "module" }, "env": { @@ -110,7 +110,10 @@ "consistent-return": "off", "no-unused-vars": "off", "unused-imports/no-unused-imports": "error", - "unused-imports/no-unused-vars": "error", + "unused-imports/no-unused-vars": [ + "error", + { "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" } + ], "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-explicit-any": "off", diff --git a/jest.config.ts b/jest.config.ts index a4dfdb06..48c400f1 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -12,6 +12,7 @@ const defaultSettings = { '@ukef/config/(.*)': '/../src/config/$1', '@ukef/database/(.*)': '/../src/modules/database/$1', '@ukef/helpers/(.*)': '/../src/helpers/$1', + '@ukef/helper-modules/(.*)': '/../src/helper-modules/$1', '@ukef/modules/(.*)': '/../src/modules/$1', '@ukef/auth/(.*)': '/../src/modules/auth/$1', '@ukef/(.*)': '/../src/$1', diff --git a/src/helper-modules/ordnance-survey/dto/get-addresses-response.dto.ts b/src/helper-modules/ordnance-survey/dto/get-addresses-response.dto.ts new file mode 100644 index 00000000..755ef606 --- /dev/null +++ b/src/helper-modules/ordnance-survey/dto/get-addresses-response.dto.ts @@ -0,0 +1,56 @@ +export type GetAddressResponse = { + header: { + uri: string; + query: string; + offset: number; + totalresults: number; + format: string; + dataset: string; + lr: string; + maxresults: number; + epoch: string; + lastupdate: string; + output_srs: string; + }; + results?: GetAddressResponseItem[]; +}; + +interface GetAddressResponseItem { + DPA: GetAddressResponseAddress; +} + +interface GetAddressResponseAddress { + UPRN: string; + UDPRN: string; + ADDRESS: string; + BUILDING_NAME?: string; + BUILDING_NUMBER?: string; + ORGANISATION_NAME?: string; + DEPENDENT_LOCALITY?: string; + THOROUGHFARE_NAME: string; + POST_TOWN: string; + POSTCODE: string; + RPC: string; + X_COORDINATE: number; + Y_COORDINATE: number; + STATUS: string; + LOGICAL_STATUS_CODE: string; + CLASSIFICATION_CODE: string; + CLASSIFICATION_CODE_DESCRIPTION: string; + LOCAL_CUSTODIAN_CODE: number; + LOCAL_CUSTODIAN_CODE_DESCRIPTION: string; + COUNTRY_CODE: string; + COUNTRY_CODE_DESCRIPTION: string; + POSTAL_ADDRESS_CODE: string; + POSTAL_ADDRESS_CODE_DESCRIPTION: string; + BLPU_STATE_CODE: string; + BLPU_STATE_CODE_DESCRIPTION: string; + TOPOGRAPHY_LAYER_TOID: string; + LAST_UPDATE_DATE: string; + ENTRY_DATE: string; + BLPU_STATE_DATE: string; + LANGUAGE: string; + MATCH: number; + MATCH_DESCRIPTION: string; + DELIVERY_POINT_SUFFIX: string; +} diff --git a/src/helper_modules/ordnance-survey/dto/get-search-postcode-query.dto.ts b/src/helper-modules/ordnance-survey/dto/get-search-postcode-query.dto.ts similarity index 100% rename from src/helper_modules/ordnance-survey/dto/get-search-postcode-query.dto.ts rename to src/helper-modules/ordnance-survey/dto/get-search-postcode-query.dto.ts diff --git a/src/helper_modules/ordnance-survey/examples/example-response-for-search-places-v1-postcode-no-results.json b/src/helper-modules/ordnance-survey/examples/example-response-for-search-places-v1-postcode-no-results.json similarity index 100% rename from src/helper_modules/ordnance-survey/examples/example-response-for-search-places-v1-postcode-no-results.json rename to src/helper-modules/ordnance-survey/examples/example-response-for-search-places-v1-postcode-no-results.json diff --git a/src/helper_modules/ordnance-survey/examples/example-response-for-search-places-v1-postcode.json b/src/helper-modules/ordnance-survey/examples/example-response-for-search-places-v1-postcode.json similarity index 100% rename from src/helper_modules/ordnance-survey/examples/example-response-for-search-places-v1-postcode.json rename to src/helper-modules/ordnance-survey/examples/example-response-for-search-places-v1-postcode.json diff --git a/src/helper_modules/ordnance-survey/exception/ordnance-survey.exception.test.ts b/src/helper-modules/ordnance-survey/exception/ordnance-survey.exception.test.ts similarity index 100% rename from src/helper_modules/ordnance-survey/exception/ordnance-survey.exception.test.ts rename to src/helper-modules/ordnance-survey/exception/ordnance-survey.exception.test.ts diff --git a/src/helper_modules/ordnance-survey/exception/ordnance-survey.exception.ts b/src/helper-modules/ordnance-survey/exception/ordnance-survey.exception.ts similarity index 100% rename from src/helper_modules/ordnance-survey/exception/ordnance-survey.exception.ts rename to src/helper-modules/ordnance-survey/exception/ordnance-survey.exception.ts diff --git a/src/helper_modules/ordnance-survey/known-errors.ts b/src/helper-modules/ordnance-survey/known-errors.ts similarity index 100% rename from src/helper_modules/ordnance-survey/known-errors.ts rename to src/helper-modules/ordnance-survey/known-errors.ts diff --git a/src/helper_modules/ordnance-survey/ordnance-survey.module.ts b/src/helper-modules/ordnance-survey/ordnance-survey.module.ts similarity index 92% rename from src/helper_modules/ordnance-survey/ordnance-survey.module.ts rename to src/helper-modules/ordnance-survey/ordnance-survey.module.ts index 553f756b..2afb21df 100644 --- a/src/helper_modules/ordnance-survey/ordnance-survey.module.ts +++ b/src/helper-modules/ordnance-survey/ordnance-survey.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; -import { OrdnanceSurveyConfig, KEY as ORDNANCE_SURVEY_CONFIG_KEY } from '@ukef/config/ordnance-survey.config'; +import { KEY as ORDNANCE_SURVEY_CONFIG_KEY, OrdnanceSurveyConfig } from '@ukef/config/ordnance-survey.config'; import { HttpModule } from '@ukef/modules/http/http.module'; import { OrdnanceSurveyService } from './ordnance-survey.service'; diff --git a/src/helper_modules/ordnance-survey/ordnance-survey.service.test.ts b/src/helper-modules/ordnance-survey/ordnance-survey.service.test.ts similarity index 83% rename from src/helper_modules/ordnance-survey/ordnance-survey.service.test.ts rename to src/helper-modules/ordnance-survey/ordnance-survey.service.test.ts index e895730a..6ed18f36 100644 --- a/src/helper_modules/ordnance-survey/ordnance-survey.service.test.ts +++ b/src/helper-modules/ordnance-survey/ordnance-survey.service.test.ts @@ -1,15 +1,14 @@ 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 { when } from 'jest-when'; import { of, throwError } from 'rxjs'; +import expectedResponse = require('./examples/example-response-for-search-places-v1-postcode.json'); +import noResultsResponse = require('./examples/example-response-for-search-places-v1-postcode-no-results.json'); import { OrdnanceSurveyException } from './exception/ordnance-survey.exception'; import { OrdnanceSurveyService } from './ordnance-survey.service'; -import { ConfigService } from '@nestjs/config'; - -const expectedResponse = require('./examples/example-response-for-search-places-v1-postcode.json'); -const noResultsResponse = require('./examples/example-response-for-search-places-v1-postcode.json'); describe('OrdnanceSurveyService', () => { const valueGenerator = new RandomValueGenerator(); @@ -19,7 +18,7 @@ describe('OrdnanceSurveyService', () => { let service: OrdnanceSurveyService; const testPostcode = 'W1A 1AA'; - const testKey = valueGenerator.string({length: 10}); + const testKey = valueGenerator.string({ length: 10 }); const basePath = '/search/places/v1/postcode'; beforeEach(() => { @@ -28,16 +27,14 @@ describe('OrdnanceSurveyService', () => { httpServiceGet = jest.fn(); httpService.get = httpServiceGet; - configServiceGet = jest.fn().mockReturnValue({key: testKey}); + configServiceGet = jest.fn().mockReturnValue({ key: testKey }); configService.get = configServiceGet; service = new OrdnanceSurveyService(httpService, configService); }); - - describe('getAddressesByPostcode', () => { - const expectedPath = `${basePath}?postcode=${testPostcode}&key=${testKey}`; + const expectedPath = `${basePath}?postcode=${encodeURIComponent(testPostcode)}&key=${encodeURIComponent(testKey)}`; const expectedHttpServiceGetArgs: [string, object] = [expectedPath, { headers: { 'Content-Type': 'application/json' } }]; @@ -70,9 +67,8 @@ describe('OrdnanceSurveyService', () => { expectedUrlQueryPart: '?postcode=W1A1AA', }, ])('call Ordnance Survey API with correct and safe query parameters "$expectedUrlQueryPart"', async ({ postcode, expectedUrlQueryPart }) => { - // const expectedPath = `${basePath}${expectedUrlQueryPart}&key=${testKey}`; - - // const expectedHttpServiceGetArgs: [string, object] = [expectedPath, { headers: { 'Content-Type': 'application/json' } }]; + const expectedPath = `${basePath}${expectedUrlQueryPart}&key=${encodeURIComponent(testKey)}`; + const expectedHttpServiceGetArgs: [string, object] = [expectedPath, { headers: { 'Content-Type': 'application/json' } }]; when(httpServiceGet) .calledWith(...expectedHttpServiceGetArgs) @@ -86,13 +82,13 @@ describe('OrdnanceSurveyService', () => { }), ); - await service.getAddressesByPostcode(testPostcode); + await service.getAddressesByPostcode(postcode); expect(httpServiceGet).toHaveBeenCalledTimes(1); expect(httpServiceGet).toHaveBeenCalledWith(...expectedHttpServiceGetArgs); }); - it("no results - returns 200 without results", async () => { + it('no results - returns 200 without results', async () => { when(httpServiceGet) .calledWith(...expectedHttpServiceGetArgs) .mockReturnValueOnce( diff --git a/src/helper_modules/ordnance-survey/ordnance-survey.service.ts b/src/helper-modules/ordnance-survey/ordnance-survey.service.ts similarity index 88% rename from src/helper_modules/ordnance-survey/ordnance-survey.service.ts rename to src/helper-modules/ordnance-survey/ordnance-survey.service.ts index 7443a9a5..5464166e 100644 --- a/src/helper_modules/ordnance-survey/ordnance-survey.service.ts +++ b/src/helper-modules/ordnance-survey/ordnance-survey.service.ts @@ -1,12 +1,12 @@ import { HttpService } from '@nestjs/axios'; import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { KEY as ORDNANCE_SURVEY_CONFIG_KEY, OrdnanceSurveyConfig } from '@ukef/config/ordnance-survey.config'; import { HttpClient } from '@ukef/modules/http/http.client'; import { GetAddressResponse } from './dto/get-addresses-response.dto'; // import { getCustomersNotFoundKnownOrdnanceSurveyError } from './known-errors'; import { createWrapOrdnanceSurveyHttpGetErrorCallback } from './wrap-ordnance-survey-http-error-callback'; -import { ConfigService } from '@nestjs/config'; -import { OrdnanceSurveyConfig, KEY as ORDNANCE_SURVEY_CONFIG_KEY } from '@ukef/config/ordnance-survey.config'; @Injectable() export class OrdnanceSurveyService { @@ -20,7 +20,8 @@ export class OrdnanceSurveyService { } async getAddressesByPostcode(postcode): Promise { - const path = '/search/places/v1/postcode?postcode=' + postcode + '&key=' + this.key; + const path = `/search/places/v1/postcode?postcode=${encodeURIComponent(postcode)}&key=${encodeURIComponent(this.key)}`; + const { data } = await this.httpClient.get({ path, headers: { 'Content-Type': 'application/json' }, diff --git a/src/helper_modules/ordnance-survey/wrap-ordnance-survey-http-error-callback.ts b/src/helper-modules/ordnance-survey/wrap-ordnance-survey-http-error-callback.ts similarity index 100% rename from src/helper_modules/ordnance-survey/wrap-ordnance-survey-http-error-callback.ts rename to src/helper-modules/ordnance-survey/wrap-ordnance-survey-http-error-callback.ts diff --git a/src/helper_modules/ordnance-survey/dto/get-addresses-response.dto.ts b/src/helper_modules/ordnance-survey/dto/get-addresses-response.dto.ts deleted file mode 100644 index 5a9f6201..00000000 --- a/src/helper_modules/ordnance-survey/dto/get-addresses-response.dto.ts +++ /dev/null @@ -1,90 +0,0 @@ -export type GetAddressResponse = { - header: { - uri: string, - query: string, - offset: number, - totalresults: number, - format: string, - dataset: string, - lr: string, - maxresults: number, - epoch: string, - lastupdate: string, - output_srs: string - }, - results?: GetAddressResponseItem[], -}; - -interface GetAddressResponseItem { - DPA: { - UPRN: string, - UDPRN: string, - ADDRESS: string, - BUILDING_NAME?: string, - BUILDING_NUMBER?: string, - ORGANISATION_NAME?: string; - DEPENDENT_LOCALITY?: string; - THOROUGHFARE_NAME: string, - POST_TOWN: string, - POSTCODE: string, - RPC: string, - X_COORDINATE: number, - Y_COORDINATE: number, - STATUS: string, - LOGICAL_STATUS_CODE: string, - CLASSIFICATION_CODE: string, - CLASSIFICATION_CODE_DESCRIPTION: string, - LOCAL_CUSTODIAN_CODE: number, - LOCAL_CUSTODIAN_CODE_DESCRIPTION: string, - COUNTRY_CODE: string, - COUNTRY_CODE_DESCRIPTION: string, - POSTAL_ADDRESS_CODE: string, - POSTAL_ADDRESS_CODE_DESCRIPTION: string, - BLPU_STATE_CODE: string, - BLPU_STATE_CODE_DESCRIPTION: string, - TOPOGRAPHY_LAYER_TOID: string, - LAST_UPDATE_DATE: string, - ENTRY_DATE: string, - BLPU_STATE_DATE: string, - LANGUAGE: string, - MATCH: number, - MATCH_DESCRIPTION: string, - DELIVERY_POINT_SUFFIX: string - } -} - -// interface GetAddressResponseAddress { -// UPRN: string, -// UDPRN: string, -// ADDRESS: string, -// BUILDING_NAME?: string, -// BUILDING_NUMBER?: string, -// ORGANISATION_NAME?: string; -// DEPENDENT_LOCALITY?: string; -// THOROUGHFARE_NAME: string, -// POST_TOWN: string, -// POSTCODE: string, -// RPC: string, -// X_COORDINATE: number, -// Y_COORDINATE: number, -// STATUS: string, -// LOGICAL_STATUS_CODE: string, -// CLASSIFICATION_CODE: string, -// CLASSIFICATION_CODE_DESCRIPTION: string, -// LOCAL_CUSTODIAN_CODE: number, -// LOCAL_CUSTODIAN_CODE_DESCRIPTION: string, -// COUNTRY_CODE: string, -// COUNTRY_CODE_DESCRIPTION: string, -// POSTAL_ADDRESS_CODE: string, -// POSTAL_ADDRESS_CODE_DESCRIPTION: string, -// BLPU_STATE_CODE: string, -// BLPU_STATE_CODE_DESCRIPTION: string, -// TOPOGRAPHY_LAYER_TOID: string, -// LAST_UPDATE_DATE: string, -// ENTRY_DATE: string, -// BLPU_STATE_DATE: string, -// LANGUAGE: string, -// MATCH: number, -// MATCH_DESCRIPTION: string, -// DELIVERY_POINT_SUFFIX: string -// } diff --git a/src/modules/geospatial/dto/get-address-by-postcode-query.dto.ts b/src/modules/geospatial/dto/get-address-by-postcode-query.dto.ts index d01a159f..698dbe25 100644 --- a/src/modules/geospatial/dto/get-address-by-postcode-query.dto.ts +++ b/src/modules/geospatial/dto/get-address-by-postcode-query.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { Matches, MaxLength, MinLength } from 'class-validator'; -const UK_POSTCODE = /^[A-Za-z]{1,2}[0-9Rr][0-9A-Za-z]?\s?[0-9][ABD-HJLNP-UW-Zabd-hjlnp-uw-z]{2}$/; +const UK_POSTCODE = /^[A-Za-z]{1,2}[\dRr][\dA-Za-z]?\s?\d[ABD-HJLNP-UW-Zabd-hjlnp-uw-z]{2}$/; export class GetAddressByPostcodeQueryDto { @ApiProperty({ @@ -13,4 +13,3 @@ export class GetAddressByPostcodeQueryDto { @Matches(UK_POSTCODE) public postcode: string; } - diff --git a/src/modules/geospatial/geospatial.controller.ts b/src/modules/geospatial/geospatial.controller.ts index 74d4e8ff..4c25561a 100644 --- a/src/modules/geospatial/geospatial.controller.ts +++ b/src/modules/geospatial/geospatial.controller.ts @@ -1,10 +1,9 @@ -import { BadRequestException, Controller, Get, Query } from '@nestjs/common'; +import { Controller, Get, Query } from '@nestjs/common'; import { ApiNotFoundResponse, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; -import { GetSearchPostcodeOrdnanceSurveyQueryDto } from '@ukef/helper-modules/ordnance-survey/dto/get-search-postcode-query.dto'; -import { GeospatialService } from './geospatial.service'; import { GetAddressByPostcodeQueryDto } from './dto/get-address-by-postcode-query.dto'; import { GetSearchAddressesResponse, GetSearchAddressesResponseItem } from './dto/get-search-addresses-response.dto'; +import { GeospatialService } from './geospatial.service'; @ApiTags('geospatial') @Controller('geospatial') @@ -17,7 +16,8 @@ export class GeospatialController { }) @ApiResponse({ status: 200, - description: 'AddressBase® Premium Basic Land and Property Units (BLPUs) can reference two types of address and with the OS Places API it is possible to search for one at a time, or both. These are the Delivery Point Address (DPA) and the Land and Property Identifier (LPI).', + description: + 'AddressBase® Premium Basic Land and Property Units (BLPUs) can reference two types of address and with the OS Places API it is possible to search for one at a time, or both. These are the Delivery Point Address (DPA) and the Land and Property Identifier (LPI).', type: [GetSearchAddressesResponseItem], }) @ApiNotFoundResponse({ diff --git a/src/modules/geospatial/geospatial.service.ts b/src/modules/geospatial/geospatial.service.ts index e48de375..af45adab 100644 --- a/src/modules/geospatial/geospatial.service.ts +++ b/src/modules/geospatial/geospatial.service.ts @@ -8,14 +8,13 @@ export class GeospatialService { constructor(private readonly ordnanceSurveyService: OrdnanceSurveyService) {} async getAddressesByPostcode(postcode: string): Promise { - let addresses = []; + const addresses = []; const response = await this.ordnanceSurveyService.getAddressesByPostcode(postcode); response.results.forEach((item) => { // if (item.DPA.LANGUAGE === (req.query.language ? req.query.language : 'EN')) { + // Ordnance survey sends duplicated results with the welsh version too via 'CY' if (item.DPA.LANGUAGE === 'EN') { - // Ordnance survey sends duplicated results with the welsh version too via 'CY' - addresses.push({ organisationName: item.DPA.ORGANISATION_NAME || null, addressLine1: `${item.DPA.BUILDING_NAME || ''} ${item.DPA.BUILDING_NUMBER || ''} ${item.DPA.THOROUGHFARE_NAME || ''}`.trim(), diff --git a/src/modules/mdm.module.ts b/src/modules/mdm.module.ts index 2c16ce9b..9d3b7bfb 100644 --- a/src/modules/mdm.module.ts +++ b/src/modules/mdm.module.ts @@ -1,9 +1,11 @@ import { Module } from '@nestjs/common'; import { AuthModule } from '@ukef/auth/auth.module'; import { DatabaseModule } from '@ukef/database/database.module'; +import { OrdnanceSurveyModule } from '@ukef/helper-modules/ordnance-survey/ordnance-survey.module'; import { CurrenciesModule } from '@ukef/modules/currencies/currencies.module'; import { CustomersModule } from '@ukef/modules/customers/customers.module'; import { ExposurePeriodModule } from '@ukef/modules/exposure-period/exposure-period.module'; +import { GeospatialModule } from '@ukef/modules/geospatial/geospatial.module'; import { HealthcheckModule } from '@ukef/modules/healthcheck/healthcheck.module'; import { InterestRatesModule } from '@ukef/modules/interest-rates/interest-rates.module'; import { MarketsModule } from '@ukef/modules/markets/markets.module'; @@ -26,6 +28,8 @@ import { YieldRatesModule } from '@ukef/modules/yield-rates/yield-rates.module'; PremiumSchedulesModule, SectorIndustriesModule, YieldRatesModule, + OrdnanceSurveyModule, + GeospatialModule, ], exports: [ AuthModule, @@ -40,6 +44,8 @@ import { YieldRatesModule } from '@ukef/modules/yield-rates/yield-rates.module'; PremiumSchedulesModule, SectorIndustriesModule, YieldRatesModule, + OrdnanceSurveyModule, + GeospatialModule, ], }) export class MdmModule {} diff --git a/tsconfig.json b/tsconfig.json index 0a1bd4bf..98d8c83e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,6 +32,7 @@ "@ukef/config/*": ["src/config/*"], "@ukef/database/*": ["src/modules/database/*"], "@ukef/helpers/*": ["src/helpers/*"], + "@ukef/helper-modules/*": ["src/helper-modules/*"], "@ukef/modules/*": ["src/modules/*"], "@ukef/auth/*": ["src/modules/auth/*"], "@ukef/swagger/*": ["src/swagger"], @@ -41,7 +42,7 @@ "include": [ "src", "test", - "jest.config.ts" + "jest.config.ts", ], "exclude": [ "node_modules",