@ministryofjustice/hmpps-connect-dps-shared-items 2.3.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/addressAutosuggest/osPlacesAddressService.d.ts +15 -3
- package/dist/addressAutosuggest/osPlacesApiClient.d.ts +3 -3
- package/dist/addressAutosuggest/testMocks/osPlacesAddressQueryResponse.d.ts +1 -0
- package/dist/assets/dps/components/address-autosuggest/autosuggest-address.js +1 -1
- package/dist/assets/dps/components/address-autosuggest/autosuggest-ui.js +8 -3
- package/dist/index.cjs +114 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +88 -76
- package/dist/index.esm.js +114 -34
- package/dist/index.esm.js.map +1 -1
- package/dist/utils/utils.d.ts +1 -0
- package/dist/utils/utils.test.d.ts +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# hmpps-connect-dps-shared-items
|
|
2
2
|
|
|
3
|
+
[](https://operations-engineering-reports.cloud-platform.service.justice.gov.uk/public-report/hmpps-connect-dps-shared-items-package "Link to report")
|
|
4
|
+
[](https://github.com/ministryofjustice/hmpps-connect-dps-shared-items-package/actions/workflows/pipeline.yml)
|
|
5
|
+
|
|
3
6
|
A library for sharing DPS Components across applications.
|
|
4
7
|
|
|
5
8
|
## Contents
|
|
@@ -38,7 +41,7 @@ Add the `hmpps-connect-dps-shared-items` assets directory to nunjucks configurat
|
|
|
38
41
|
```
|
|
39
42
|
|
|
40
43
|
then import the component into your nunjucks file:
|
|
41
|
-
```
|
|
44
|
+
```nunjucks
|
|
42
45
|
{% from "dps/components/alert-flag/macro.njk" import alertFlag %}
|
|
43
46
|
```
|
|
44
47
|
|
|
@@ -68,4 +71,4 @@ connectDps.initAll()
|
|
|
68
71
|
|
|
69
72
|
## For Library Developers
|
|
70
73
|
|
|
71
|
-
1. [Publishing to NPM](readme/publishing.md)
|
|
74
|
+
1. [Publishing to NPM](readme/publishing.md)
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
+
import { IFuseOptions } from 'fuse.js';
|
|
1
2
|
import { OsAddress } from '../types/public/addressAutosuggest/osAddress';
|
|
2
3
|
import OsPlacesApiClient from './osPlacesApiClient';
|
|
3
4
|
import { ConnectDpsComponentLogger } from '../types/public/ConnectDpsComponentLogger';
|
|
5
|
+
export interface AddressesMatchingQueryConfig {
|
|
6
|
+
osPlacesQueryParamOverrides: Record<string, unknown>;
|
|
7
|
+
fuzzyMatchOptionOverrides: IFuseOptions<OsAddress>;
|
|
8
|
+
}
|
|
9
|
+
export interface AddressesUprnQueryConfig {
|
|
10
|
+
osPlacesQueryParamOverrides: Record<string, unknown>;
|
|
11
|
+
}
|
|
4
12
|
export default class OsPlacesAddressService {
|
|
5
13
|
private readonly logger;
|
|
6
14
|
private readonly osPlacesApiClient;
|
|
7
15
|
constructor(logger: ConnectDpsComponentLogger, osPlacesApiClient: OsPlacesApiClient);
|
|
8
|
-
getAddressesMatchingQuery(
|
|
9
|
-
getAddressByUprn(uprn: string): Promise<OsAddress | null>;
|
|
16
|
+
getAddressesMatchingQuery(rawSearchQuery: string, config?: AddressesMatchingQueryConfig): Promise<OsAddress[]>;
|
|
17
|
+
getAddressByUprn(uprn: string, config?: AddressesUprnQueryConfig): Promise<OsAddress | null>;
|
|
10
18
|
sanitiseUkPostcode(stringContainingPostcode: string): string;
|
|
19
|
+
private getOSPlacesResultsForQuery;
|
|
20
|
+
private getOptimisedAddressesMatchingQuery;
|
|
11
21
|
private handleResponse;
|
|
12
|
-
private
|
|
22
|
+
private dpaToOsAddress;
|
|
23
|
+
private lpiToOsAddress;
|
|
13
24
|
private formatAddressString;
|
|
25
|
+
private sanitiseString;
|
|
14
26
|
}
|
|
@@ -7,7 +7,7 @@ export default class OsPlacesApiClient {
|
|
|
7
7
|
constructor(logger: ConnectDpsComponentLogger, config: ApiConfig & {
|
|
8
8
|
apiKey: string;
|
|
9
9
|
});
|
|
10
|
-
getAddressesByFreeTextQuery(freeTextQuery: string): Promise<OsPlacesQueryResponse>;
|
|
11
|
-
getAddressesByUprn(uprn: string): Promise<OsPlacesQueryResponse>;
|
|
12
|
-
get
|
|
10
|
+
getAddressesByFreeTextQuery(freeTextQuery: string, queryParamOverrides?: Record<string, unknown>): Promise<OsPlacesQueryResponse>;
|
|
11
|
+
getAddressesByUprn(uprn: string, queryParamOverrides?: Record<string, unknown>): Promise<OsPlacesQueryResponse>;
|
|
12
|
+
private get;
|
|
13
13
|
}
|
|
@@ -2,6 +2,7 @@ import { OsPlacesQueryResponse } from '../../types/public/addressAutosuggest/osP
|
|
|
2
2
|
export declare const mockOsPlacesAddressQueryResponse: OsPlacesQueryResponse;
|
|
3
3
|
export declare const mockOsPlacesAddressQueryEmptyResponse: OsPlacesQueryResponse;
|
|
4
4
|
export declare const mockOsPlacesAddressQuerySingleResponse: OsPlacesQueryResponse;
|
|
5
|
+
export declare const mockOsPlacesAddressQueryNonPostalResponse: OsPlacesQueryResponse;
|
|
5
6
|
export declare const mockOsPlacesInvalidApiKey: {
|
|
6
7
|
fault: {
|
|
7
8
|
faultstring: string;
|
|
@@ -52,7 +52,7 @@ export class AutosuggestAddress {
|
|
|
52
52
|
// Reset any previously selected result:
|
|
53
53
|
this.uprn.value = ''
|
|
54
54
|
|
|
55
|
-
return await this.findAddress(query?.replace(/[^A-Z0-9 ]/gi, ''))
|
|
55
|
+
return await this.findAddress(query?.replace(/[^A-Z0-9-.' ]/gi, ''))
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
async findAddress(query) {
|
|
@@ -355,9 +355,14 @@ export class AutosuggestUi {
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
emboldenMatch(string, query) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
358
|
+
return this.escapeRegExp(query)
|
|
359
|
+
.trim()
|
|
360
|
+
.replace(/\s\s+/g, ' ')
|
|
361
|
+
.split(' ')
|
|
362
|
+
.reduce((accumulator, currentValue) => {
|
|
363
|
+
let reg = new RegExp(`(?<!<)${currentValue}(?![\\w\\s]*>)`, 'gi')
|
|
364
|
+
return accumulator.replace(reg, '<strong>$&</strong>')
|
|
365
|
+
}, string)
|
|
361
366
|
}
|
|
362
367
|
|
|
363
368
|
escapeRegExp(string) {
|
package/dist/index.cjs
CHANGED
|
@@ -12,10 +12,19 @@ const properCase = (word) => word.length >= 1 ? word[0].toUpperCase() + word.toL
|
|
|
12
12
|
* @returns name converted to proper case.
|
|
13
13
|
*/
|
|
14
14
|
const properCaseName = (name) => (isBlank(name) ? '' : name.split('-').map(properCase).join('-'));
|
|
15
|
-
const convertToTitleCase = (sentence) => isBlank(sentence)
|
|
15
|
+
const convertToTitleCase = (sentence) => isBlank(sentence)
|
|
16
|
+
? ''
|
|
17
|
+
: sentence
|
|
18
|
+
.split('(') // Handle parentheses
|
|
19
|
+
.map(subsentence => subsentence.split(' ').map(properCaseName).join(' '))
|
|
20
|
+
.join('(');
|
|
16
21
|
|
|
17
22
|
const simplePostCodeRegex = /^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$/i;
|
|
18
23
|
const stringContainingPostCodeRegex = /^(.*?)([A-Z]{1,2}\d[A-Z\d]? ?)(\d[A-Z]{2})(.*)$/i;
|
|
24
|
+
// Where we know OS Places API struggles with certain search terms, we will perform an additional replacement search:
|
|
25
|
+
const knownIssueSearchTermReplacements = {
|
|
26
|
+
amazon: 'amazon.co.uk',
|
|
27
|
+
};
|
|
19
28
|
class OsPlacesAddressService {
|
|
20
29
|
logger;
|
|
21
30
|
osPlacesApiClient;
|
|
@@ -23,31 +32,25 @@ class OsPlacesAddressService {
|
|
|
23
32
|
this.logger = logger;
|
|
24
33
|
this.osPlacesApiClient = osPlacesApiClient;
|
|
25
34
|
}
|
|
26
|
-
async getAddressesMatchingQuery(
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
const queryIsAPostCode = simplePostCodeRegex.test(searchQuery.trim());
|
|
38
|
-
const buildingNumberSort = (a, b) => a?.addressString && b?.addressString && queryIsAPostCode
|
|
39
|
-
? a.addressString.localeCompare(b.addressString, undefined, { numeric: true, sensitivity: 'base' })
|
|
40
|
-
: 1;
|
|
41
|
-
// maximum number of properties in a postcode is 100
|
|
42
|
-
return (bestMatchResults.length ? bestMatchResults : rawResults).sort(buildingNumberSort).slice(0, 100);
|
|
35
|
+
async getAddressesMatchingQuery(rawSearchQuery, config = { osPlacesQueryParamOverrides: {}, fuzzyMatchOptionOverrides: {} }) {
|
|
36
|
+
const sanitisedSearchQuery = this.sanitiseString(rawSearchQuery) || '';
|
|
37
|
+
const additionalSearchQuery = Object.keys(knownIssueSearchTermReplacements).reduce((accumulator, currentValue) => {
|
|
38
|
+
const reg = new RegExp(currentValue, 'gi');
|
|
39
|
+
return accumulator.replace(reg, knownIssueSearchTermReplacements[currentValue]);
|
|
40
|
+
}, sanitisedSearchQuery);
|
|
41
|
+
const rawResults = await this.getOSPlacesResultsForQuery(sanitisedSearchQuery, config);
|
|
42
|
+
const additionalResultsForKnownIssueSearchTerms = sanitisedSearchQuery === additionalSearchQuery
|
|
43
|
+
? []
|
|
44
|
+
: await this.getOSPlacesResultsForQuery(additionalSearchQuery, config);
|
|
45
|
+
return this.getOptimisedAddressesMatchingQuery(sanitisedSearchQuery, [...rawResults, ...additionalResultsForKnownIssueSearchTerms], config);
|
|
43
46
|
}
|
|
44
|
-
async getAddressByUprn(uprn) {
|
|
45
|
-
const response = await this.osPlacesApiClient.getAddressesByUprn(uprn);
|
|
47
|
+
async getAddressByUprn(uprn, config = { osPlacesQueryParamOverrides: {} }) {
|
|
48
|
+
const response = await this.osPlacesApiClient.getAddressesByUprn(uprn, config.osPlacesQueryParamOverrides);
|
|
46
49
|
const result = this.handleResponse(response);
|
|
47
50
|
if (result.length === 0)
|
|
48
51
|
return null;
|
|
49
52
|
if (result.length > 1)
|
|
50
|
-
this.logger.info(
|
|
53
|
+
this.logger.info('Multiple results returned for UPRN');
|
|
51
54
|
return result[result.length - 1];
|
|
52
55
|
}
|
|
53
56
|
sanitiseUkPostcode(stringContainingPostcode) {
|
|
@@ -56,18 +59,60 @@ class OsPlacesAddressService {
|
|
|
56
59
|
return stringContainingPostcode;
|
|
57
60
|
return `${postCodeQuery[1]}${postCodeQuery[2].toUpperCase().trim()} ${postCodeQuery[3].toUpperCase().trim()}${postCodeQuery[4]}`;
|
|
58
61
|
}
|
|
62
|
+
async getOSPlacesResultsForQuery(searchQuery, config) {
|
|
63
|
+
const response = await this.osPlacesApiClient.getAddressesByFreeTextQuery(this.sanitiseUkPostcode(searchQuery), // the API responds better when postcodes are properly formatted
|
|
64
|
+
config.osPlacesQueryParamOverrides);
|
|
65
|
+
return this.handleResponse(response);
|
|
66
|
+
}
|
|
67
|
+
getOptimisedAddressesMatchingQuery(searchQuery, rawResults, config) {
|
|
68
|
+
const bestMatchResults = new Fuse(rawResults, {
|
|
69
|
+
shouldSort: true,
|
|
70
|
+
threshold: 0.2,
|
|
71
|
+
useExtendedSearch: true, // to allow search terms to be separated
|
|
72
|
+
ignoreLocation: true, // to allow search terms to be separated
|
|
73
|
+
keys: [{ name: 'addressString' }],
|
|
74
|
+
...config.fuzzyMatchOptionOverrides,
|
|
75
|
+
})
|
|
76
|
+
.search(searchQuery)
|
|
77
|
+
.map(result => result.item);
|
|
78
|
+
const queryIsAPostCode = simplePostCodeRegex.test(searchQuery);
|
|
79
|
+
const isExactMatchToQuery = (addressString) => searchQuery && this.sanitiseString(addressString)?.includes(searchQuery);
|
|
80
|
+
// By using `useExtendedSearch` and allowing search terms to be separated, we need to reprioritise exact match results:
|
|
81
|
+
const preferExactMatchSort = (a, b) => isExactMatchToQuery(a?.addressString) && !isExactMatchToQuery(b?.addressString) ? -1 : 1;
|
|
82
|
+
// This ensures house numbers are ordered numerically, not alphabetically when doing a search just on postcode:
|
|
83
|
+
const buildingNumberSort = (a, b) => a?.addressString && b?.addressString && queryIsAPostCode
|
|
84
|
+
? a.addressString.localeCompare(b.addressString, undefined, { numeric: true, sensitivity: 'base' })
|
|
85
|
+
: 1;
|
|
86
|
+
// maximum number of properties in a postcode is 100
|
|
87
|
+
return (bestMatchResults.length ? bestMatchResults : rawResults)
|
|
88
|
+
.sort(queryIsAPostCode ? buildingNumberSort : preferExactMatchSort)
|
|
89
|
+
.slice(0, 100);
|
|
90
|
+
}
|
|
59
91
|
handleResponse(response) {
|
|
60
92
|
if (response.header && response.header.totalresults === 0)
|
|
61
93
|
return [];
|
|
62
|
-
return response.results
|
|
94
|
+
return response.results
|
|
95
|
+
.map(result => (result.DPA && this.dpaToOsAddress(result.DPA)) || (result.LPI && this.lpiToOsAddress(result.LPI)))
|
|
96
|
+
.filter(result => !!result?.uprn)
|
|
97
|
+
// remove duplicate results with the same UPRN:
|
|
98
|
+
.filter((val, index, self) => self.findIndex(address => address?.uprn === val?.uprn) === index);
|
|
63
99
|
}
|
|
64
|
-
|
|
65
|
-
const { UPRN, DEPENDENT_LOCALITY, SUB_BUILDING_NAME, BUILDING_NAME, BUILDING_NUMBER, THOROUGHFARE_NAME, POST_TOWN, POSTCODE, COUNTRY_CODE, LOCAL_CUSTODIAN_CODE_DESCRIPTION, } = addressResult;
|
|
100
|
+
dpaToOsAddress(addressResult) {
|
|
101
|
+
const { UPRN, ADDRESS, DEPENDENT_LOCALITY, ORGANISATION_NAME, SUB_BUILDING_NAME, BUILDING_NAME, BUILDING_NUMBER, THOROUGHFARE_NAME, POST_TOWN, POSTCODE, COUNTRY_CODE, LOCAL_CUSTODIAN_CODE_DESCRIPTION, POSTAL_ADDRESS_CODE, } = addressResult;
|
|
102
|
+
// Do not return address data that does not have a valid postal address:
|
|
103
|
+
if (POSTAL_ADDRESS_CODE === 'N')
|
|
104
|
+
return undefined;
|
|
105
|
+
const useOrganisationNameAsBuildingName = !isBlank(ORGANISATION_NAME) && isBlank(BUILDING_NAME) && isBlank(SUB_BUILDING_NAME);
|
|
106
|
+
const buildingName = convertToTitleCase(useOrganisationNameAsBuildingName ? ORGANISATION_NAME : BUILDING_NAME);
|
|
107
|
+
const subBuildingName = [useOrganisationNameAsBuildingName ? null : ORGANISATION_NAME, SUB_BUILDING_NAME]
|
|
108
|
+
.filter(name => !isBlank(name))
|
|
109
|
+
.map(name => convertToTitleCase(name))
|
|
110
|
+
.join(', ');
|
|
66
111
|
return {
|
|
67
|
-
addressString: this.formatAddressString(
|
|
112
|
+
addressString: this.formatAddressString(ADDRESS, BUILDING_NUMBER, THOROUGHFARE_NAME, POSTCODE),
|
|
68
113
|
buildingNumber: BUILDING_NUMBER,
|
|
69
|
-
buildingName
|
|
70
|
-
subBuildingName
|
|
114
|
+
buildingName,
|
|
115
|
+
subBuildingName,
|
|
71
116
|
thoroughfareName: convertToTitleCase(THOROUGHFARE_NAME),
|
|
72
117
|
dependentLocality: convertToTitleCase(DEPENDENT_LOCALITY),
|
|
73
118
|
postTown: convertToTitleCase(POST_TOWN),
|
|
@@ -77,10 +122,40 @@ class OsPlacesAddressService {
|
|
|
77
122
|
uprn: UPRN,
|
|
78
123
|
};
|
|
79
124
|
}
|
|
80
|
-
|
|
81
|
-
const { ADDRESS,
|
|
82
|
-
|
|
83
|
-
|
|
125
|
+
lpiToOsAddress(addressResult) {
|
|
126
|
+
const { UPRN, ADDRESS, ORGANISATION, SAO_TEXT, PAO_START_NUMBER, PAO_TEXT, STREET_DESCRIPTION, TOWN_NAME, POSTCODE_LOCATOR, COUNTRY_CODE, POSTAL_ADDRESS_CODE, } = addressResult;
|
|
127
|
+
// Do not return address data that does not have a valid postal address:
|
|
128
|
+
if (POSTAL_ADDRESS_CODE === 'N')
|
|
129
|
+
return undefined;
|
|
130
|
+
const useOrganisationAsBuildingName = !isBlank(ORGANISATION) && isBlank(PAO_TEXT) && isBlank(SAO_TEXT);
|
|
131
|
+
const buildingName = convertToTitleCase(useOrganisationAsBuildingName ? ORGANISATION : PAO_TEXT);
|
|
132
|
+
const subBuildingName = [useOrganisationAsBuildingName ? null : ORGANISATION, SAO_TEXT]
|
|
133
|
+
.filter(name => !isBlank(name))
|
|
134
|
+
.map(name => convertToTitleCase(name))
|
|
135
|
+
.join(', ');
|
|
136
|
+
return {
|
|
137
|
+
addressString: this.formatAddressString(ADDRESS, PAO_START_NUMBER, STREET_DESCRIPTION, POSTCODE_LOCATOR),
|
|
138
|
+
buildingNumber: PAO_START_NUMBER,
|
|
139
|
+
buildingName,
|
|
140
|
+
subBuildingName,
|
|
141
|
+
thoroughfareName: convertToTitleCase(STREET_DESCRIPTION),
|
|
142
|
+
postTown: convertToTitleCase(TOWN_NAME),
|
|
143
|
+
postcode: POSTCODE_LOCATOR,
|
|
144
|
+
country: COUNTRY_CODE,
|
|
145
|
+
uprn: UPRN,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
formatAddressString(addressString, buildingNumber, thoroughfareName, postcode) {
|
|
149
|
+
const withoutPostcode = addressString
|
|
150
|
+
.replace(`, ${postcode}`, '')
|
|
151
|
+
.replace(`${buildingNumber}, ${thoroughfareName}`, `${buildingNumber} ${thoroughfareName}`);
|
|
152
|
+
return `${convertToTitleCase(withoutPostcode)}, ${postcode}`;
|
|
153
|
+
}
|
|
154
|
+
sanitiseString(input) {
|
|
155
|
+
return input
|
|
156
|
+
?.trim()
|
|
157
|
+
?.toLowerCase()
|
|
158
|
+
?.replace(/[^A-Z0-9-.' ]/gi, '');
|
|
84
159
|
}
|
|
85
160
|
}
|
|
86
161
|
|
|
@@ -91,18 +166,23 @@ class OsPlacesApiClient {
|
|
|
91
166
|
this.logger = logger;
|
|
92
167
|
this.config = config;
|
|
93
168
|
}
|
|
94
|
-
async getAddressesByFreeTextQuery(freeTextQuery) {
|
|
169
|
+
async getAddressesByFreeTextQuery(freeTextQuery, queryParamOverrides = {}) {
|
|
95
170
|
const queryParams = {
|
|
96
171
|
query: freeTextQuery,
|
|
97
|
-
lr: 'EN',
|
|
98
172
|
key: this.config.apiKey,
|
|
173
|
+
lr: 'EN',
|
|
174
|
+
fq: ['LOGICAL_STATUS_CODE:1', 'LPI_LOGICAL_STATUS_CODE:1'], // only want active addresses
|
|
175
|
+
dataset: 'LPI', // LPI chosen as default since it appears to perform better, especially for business addresses
|
|
176
|
+
...queryParamOverrides,
|
|
99
177
|
};
|
|
100
178
|
return this.get('/find', queryParams);
|
|
101
179
|
}
|
|
102
|
-
async getAddressesByUprn(uprn) {
|
|
180
|
+
async getAddressesByUprn(uprn, queryParamOverrides = {}) {
|
|
103
181
|
const queryParams = {
|
|
104
182
|
uprn,
|
|
105
183
|
key: this.config.apiKey,
|
|
184
|
+
dataset: 'DPA,LPI', // Both datasets available for the lookup by UPRN, preference for DPA because of the preferred data structure
|
|
185
|
+
...queryParamOverrides,
|
|
106
186
|
};
|
|
107
187
|
return this.get('/uprn', queryParams);
|
|
108
188
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/utils/utils.ts","../src/addressAutosuggest/osPlacesAddressService.ts","../src/addressAutosuggest/osPlacesApiClient.ts","../src/alertFlags/getAlertFlagLabelsForAlerts.ts"],"sourcesContent":["const isBlank = (str: string): boolean => !str || /^\\s*$/.test(str)\n\nconst properCase = (word: string): string =>\n word.length >= 1 ? word[0].toUpperCase() + word.toLowerCase().slice(1) : word\n\n/**\n * Converts a name (first name, last name, middle name, etc.) to proper case equivalent, handling double-barreled names\n * correctly (i.e. each part in a double-barreled is converted to proper case).\n * @param name name to be converted.\n * @returns name converted to proper case.\n */\nexport const properCaseName = (name: string): string => (isBlank(name) ? '' : name.split('-').map(properCase).join('-'))\n\nexport const convertToTitleCase = (sentence: string): string =>\n isBlank(sentence) ? '' : sentence.split(' ').map(properCaseName).join(' ')\n","import Fuse from 'fuse.js'\nimport { OsAddress } from '../types/public/addressAutosuggest/osAddress'\nimport { convertToTitleCase } from '../utils/utils'\nimport OsPlacesApiClient from './osPlacesApiClient'\nimport { ConnectDpsComponentLogger } from '../types/public/ConnectDpsComponentLogger'\nimport { OsPlacesDeliveryPointAddress, OsPlacesQueryResponse } from '../types/public/addressAutosuggest/osPlaces'\n\nconst simplePostCodeRegex = /^[A-Z]{1,2}\\d[A-Z\\d]? ?\\d[A-Z]{2}$/i\nconst stringContainingPostCodeRegex = /^(.*?)([A-Z]{1,2}\\d[A-Z\\d]? ?)(\\d[A-Z]{2})(.*)$/i\n\nexport default class OsPlacesAddressService {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly osPlacesApiClient: OsPlacesApiClient,\n ) {}\n\n public async getAddressesMatchingQuery(searchQuery: string, sanitisePostcode: boolean = true): Promise<OsAddress[]> {\n const response = await this.osPlacesApiClient.getAddressesByFreeTextQuery(\n sanitisePostcode ? this.sanitiseUkPostcode(searchQuery) : searchQuery,\n )\n\n const rawResults = this.handleResponse(response)\n\n const bestMatchResults = new Fuse(rawResults, {\n shouldSort: true,\n threshold: 0.2,\n ignoreLocation: true,\n keys: [{ name: 'addressString' }],\n })\n .search(searchQuery)\n .map(result => result.item)\n\n const queryIsAPostCode = simplePostCodeRegex.test(searchQuery.trim())\n const buildingNumberSort = (a: OsAddress, b: OsAddress) =>\n a?.addressString && b?.addressString && queryIsAPostCode\n ? a.addressString.localeCompare(b.addressString, undefined, { numeric: true, sensitivity: 'base' })\n : 1\n\n // maximum number of properties in a postcode is 100\n return (bestMatchResults.length ? bestMatchResults : rawResults).sort(buildingNumberSort).slice(0, 100)\n }\n\n public async getAddressByUprn(uprn: string): Promise<OsAddress | null> {\n const response = await this.osPlacesApiClient.getAddressesByUprn(uprn)\n const result = this.handleResponse(response)\n\n if (result.length === 0) return null\n if (result.length > 1) this.logger.info(`Multiple results returned for UPRN`)\n\n return result[result.length - 1]\n }\n\n public sanitiseUkPostcode(stringContainingPostcode: string) {\n const postCodeQuery = stringContainingPostCodeRegex.exec(stringContainingPostcode?.replace(/[^A-Z0-9 ]/gi, ''))\n if (!postCodeQuery) return stringContainingPostcode\n\n return `${postCodeQuery[1]}${postCodeQuery[2].toUpperCase().trim()} ${postCodeQuery[3].toUpperCase().trim()}${postCodeQuery[4]}`\n }\n\n private handleResponse(response: OsPlacesQueryResponse): OsAddress[] {\n if (response.header && response.header.totalresults === 0) return []\n\n return response.results.filter(result => result.DPA).map(result => this.toOsAddress(result.DPA!))\n }\n\n private toOsAddress(addressResult: OsPlacesDeliveryPointAddress): OsAddress {\n const {\n UPRN,\n DEPENDENT_LOCALITY,\n SUB_BUILDING_NAME,\n BUILDING_NAME,\n BUILDING_NUMBER,\n THOROUGHFARE_NAME,\n POST_TOWN,\n POSTCODE,\n COUNTRY_CODE,\n LOCAL_CUSTODIAN_CODE_DESCRIPTION,\n } = addressResult\n\n return {\n addressString: this.formatAddressString(addressResult),\n buildingNumber: BUILDING_NUMBER,\n buildingName: convertToTitleCase(BUILDING_NAME),\n subBuildingName: convertToTitleCase(SUB_BUILDING_NAME),\n thoroughfareName: convertToTitleCase(THOROUGHFARE_NAME),\n dependentLocality: convertToTitleCase(DEPENDENT_LOCALITY),\n postTown: convertToTitleCase(POST_TOWN),\n county: convertToTitleCase(LOCAL_CUSTODIAN_CODE_DESCRIPTION),\n postcode: POSTCODE,\n country: COUNTRY_CODE,\n uprn: UPRN,\n }\n }\n\n private formatAddressString(addressResult: OsPlacesDeliveryPointAddress) {\n const { ADDRESS, BUILDING_NUMBER, THOROUGHFARE_NAME, POSTCODE } = addressResult\n const withoutPostcode = ADDRESS.replace(`, ${POSTCODE}`, '').replace(\n `${BUILDING_NUMBER}, ${THOROUGHFARE_NAME}`,\n `${BUILDING_NUMBER} ${THOROUGHFARE_NAME}`,\n )\n return `${convertToTitleCase(withoutPostcode)}, ${POSTCODE}`\n }\n}\n","import superagent from 'superagent'\nimport { ApiConfig } from '@ministryofjustice/hmpps-rest-client'\nimport { OsPlacesQueryResponse } from '../types/public/addressAutosuggest/osPlaces'\nimport { ConnectDpsComponentLogger } from '../types/public/ConnectDpsComponentLogger'\n\nexport default class OsPlacesApiClient {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly config: ApiConfig & { apiKey: string },\n ) {}\n\n async getAddressesByFreeTextQuery(freeTextQuery: string): Promise<OsPlacesQueryResponse> {\n const queryParams: Record<string, string> = {\n query: freeTextQuery,\n lr: 'EN',\n key: this.config.apiKey,\n }\n\n return this.get<Promise<OsPlacesQueryResponse>>('/find', queryParams)\n }\n\n async getAddressesByUprn(uprn: string): Promise<OsPlacesQueryResponse> {\n const queryParams: Record<string, string> = {\n uprn,\n key: this.config.apiKey,\n }\n\n return this.get<Promise<OsPlacesQueryResponse>>('/uprn', queryParams)\n }\n\n async get<T>(path: string, query: Record<string, string>): Promise<T> {\n const endpoint = `${this.config.url}${path}`\n try {\n const result = await superagent\n .get(endpoint)\n .retry(2, (err, _res) => {\n if (err) this.logger.info(`Retry handler found API error with ${err.code} ${err.message}`)\n return undefined // retry handler only for logging retries, not to influence retry logic\n })\n .query(query)\n .timeout(this.config.timeout)\n\n return result.body\n } catch (error) {\n const errorMessage = error && typeof error === 'object' && 'message' in error ? error.message : error\n throw Error(`Error calling OS Places API: ${errorMessage}`)\n }\n }\n}\n","import { Alert, AlertsServiceAlert } from '../types/public/alertFlags/Alert'\nimport { AlertFlagLabel } from '../types/public/alertFlags/AlertFlagLabel'\n\nexport const alertFlagLabels: AlertFlagLabel[] = [\n { alertCodes: ['HA'], classes: 'dps-alert-status dps-alert-status--self-harm', label: 'ACCT open' },\n {\n alertCodes: ['HA1'],\n classes: 'dps-alert-status dps-alert-status--self-harm',\n label: 'ACCT post closure',\n },\n {\n alertCodes: ['XSA', 'SA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Staff assaulter',\n },\n {\n alertCodes: ['XA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Arsonist',\n },\n {\n alertCodes: ['PEEP'],\n classes: 'dps-alert-status dps-alert-status--medical',\n label: 'PEEP',\n },\n {\n alertCodes: ['HID'],\n classes: 'dps-alert-status dps-alert-status--medical',\n label: 'Hidden disability',\n },\n { alertCodes: ['XEL'], classes: 'dps-alert-status dps-alert-status--security', label: 'E-list' },\n { alertCodes: ['XELH'], classes: 'dps-alert-status dps-alert-status--security', label: 'E-list heightened' },\n { alertCodes: ['XER'], classes: 'dps-alert-status dps-alert-status--security', label: 'Escape risk' },\n {\n alertCodes: ['XRF'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Risk to females',\n },\n { alertCodes: ['XTACT'], classes: 'dps-alert-status dps-alert-status--security', label: 'TACT' },\n {\n alertCodes: ['XCO'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Corruptor',\n },\n {\n alertCodes: ['XCA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Chemical attacker',\n },\n {\n alertCodes: ['XCI'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Concerted indiscipline',\n },\n { alertCodes: ['XR'], classes: 'dps-alert-status dps-alert-status--security', label: 'Racist' },\n {\n alertCodes: ['RTP', 'RLG'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'Risk to LGBT',\n },\n {\n alertCodes: ['XHT'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Hostage taker',\n },\n {\n alertCodes: ['XCU'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Controlled unlock',\n },\n {\n alertCodes: ['XGANG'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Gang member',\n },\n { alertCodes: ['CSIP'], classes: 'dps-alert-status dps-alert-status--other', label: 'CSIP' },\n { alertCodes: ['F1'], classes: 'dps-alert-status dps-alert-status--ex-armed-forces', label: 'Veteran' },\n {\n alertCodes: ['LCE'],\n classes: 'dps-alert-status dps-alert-status--care-leaver',\n label: 'Care experienced',\n },\n {\n alertCodes: ['RNO121'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'No one-to-one',\n },\n { alertCodes: ['RCON'], classes: 'dps-alert-status dps-alert-status--risk', label: 'Conflict' },\n {\n alertCodes: ['RCDR'],\n classes: 'dps-alert-status dps-alert-status--quarantined',\n label: 'Quarantined',\n },\n {\n alertCodes: ['URCU'],\n classes: 'dps-alert-status dps-alert-status--reverse-cohorting-unit',\n label: 'Reverse Cohorting Unit',\n },\n {\n alertCodes: ['UPIU'],\n classes: 'dps-alert-status dps-alert-status--protective-isolation-unit',\n label: 'Protective Isolation Unit',\n },\n { alertCodes: ['USU'], classes: 'dps-alert-status dps-alert-status--shielding-unit', label: 'Shielding Unit' },\n {\n alertCodes: ['URS'],\n classes: 'dps-alert-status dps-alert-status--refusing-to-shield',\n label: 'Refusing to shield',\n },\n {\n alertCodes: ['RKS'],\n classes: 'dps-alert-status dps-alert-status--risk-to-known-adults',\n label: 'Risk to known adults',\n },\n { alertCodes: ['VIP'], classes: 'dps-alert-status dps-alert-status--isolated-prisoner', label: 'Isolated' },\n {\n alertCodes: ['PVN'],\n classes: 'dps-alert-status dps-alert-status--multicase dps-alert-status--visor',\n label: 'ViSOR',\n },\n {\n alertCodes: ['XCDO'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Involved in 2024 civil disorder',\n },\n {\n alertCodes: ['XVL'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Violent',\n },\n {\n alertCodes: ['DOCGM'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'OCG nominal (do not share)',\n },\n {\n alertCodes: ['RSS'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'Risk to staff',\n },\n {\n alertCodes: ['XCOP'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Potential corruptor',\n },\n].sort((a, b) => a.label.localeCompare(b.label))\n\nfunction isAlertsServiceAlert(alert: Alert): alert is AlertsServiceAlert {\n return typeof alert.alertCode !== 'string'\n}\n\nexport function getAlertFlagLabelsForAlerts(prisonerAlerts: Alert[]): AlertFlagLabel[] {\n return alertFlagLabels.reduce(\n (acc: AlertFlagLabel[], flag: { alertCodes: string[]; classes: string; label: string }) => {\n const alertIds = prisonerAlerts\n .filter(alert => {\n const alertsServiceAlert = isAlertsServiceAlert(alert)\n const alertIsActive = alertsServiceAlert ? alert.isActive : alert.active && !alert.expired\n const prisonerAlertCode = alertsServiceAlert ? alert.alertCode.code : alert.alertCode\n\n return alertIsActive && flag.alertCodes.includes(prisonerAlertCode)\n })\n .map(alert => (isAlertsServiceAlert(alert) ? (alert.alertUuid as string) : alert.alertCode))\n\n return alertIds.length ? [...acc, { ...flag, alertIds }] : acc\n },\n [] as AlertFlagLabel[],\n )\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/utils/utils.ts","../src/addressAutosuggest/osPlacesAddressService.ts","../src/addressAutosuggest/osPlacesApiClient.ts","../src/alertFlags/getAlertFlagLabelsForAlerts.ts"],"sourcesContent":["export const isBlank = (str: string | null | undefined): boolean => !str || /^\\s*$/.test(str)\n\nconst properCase = (word: string): string =>\n word.length >= 1 ? word[0].toUpperCase() + word.toLowerCase().slice(1) : word\n\n/**\n * Converts a name (first name, last name, middle name, etc.) to proper case equivalent, handling double-barreled names\n * correctly (i.e. each part in a double-barreled is converted to proper case).\n * @param name name to be converted.\n * @returns name converted to proper case.\n */\nexport const properCaseName = (name: string): string => (isBlank(name) ? '' : name.split('-').map(properCase).join('-'))\n\nexport const convertToTitleCase = (sentence: string): string =>\n isBlank(sentence)\n ? ''\n : sentence\n .split('(') // Handle parentheses\n .map(subsentence => subsentence.split(' ').map(properCaseName).join(' '))\n .join('(')\n","import Fuse, { IFuseOptions } from 'fuse.js'\nimport { OsAddress } from '../types/public/addressAutosuggest/osAddress'\nimport { convertToTitleCase, isBlank } from '../utils/utils'\nimport OsPlacesApiClient from './osPlacesApiClient'\nimport { ConnectDpsComponentLogger } from '../types/public/ConnectDpsComponentLogger'\nimport {\n OsPlacesDeliveryPointAddress,\n OsPlacesLandPropertyIdentifier,\n OsPlacesQueryResponse,\n} from '../types/public/addressAutosuggest/osPlaces'\n\nconst simplePostCodeRegex = /^[A-Z]{1,2}\\d[A-Z\\d]? ?\\d[A-Z]{2}$/i\nconst stringContainingPostCodeRegex = /^(.*?)([A-Z]{1,2}\\d[A-Z\\d]? ?)(\\d[A-Z]{2})(.*)$/i\n\n// Where we know OS Places API struggles with certain search terms, we will perform an additional replacement search:\nconst knownIssueSearchTermReplacements: Record<string, string> = {\n amazon: 'amazon.co.uk',\n}\n\nexport interface AddressesMatchingQueryConfig {\n osPlacesQueryParamOverrides: Record<string, unknown>\n fuzzyMatchOptionOverrides: IFuseOptions<OsAddress>\n}\n\nexport interface AddressesUprnQueryConfig {\n osPlacesQueryParamOverrides: Record<string, unknown>\n}\n\nexport default class OsPlacesAddressService {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly osPlacesApiClient: OsPlacesApiClient,\n ) {}\n\n public async getAddressesMatchingQuery(\n rawSearchQuery: string,\n config: AddressesMatchingQueryConfig = { osPlacesQueryParamOverrides: {}, fuzzyMatchOptionOverrides: {} },\n ): Promise<OsAddress[]> {\n const sanitisedSearchQuery = this.sanitiseString(rawSearchQuery) || ''\n const additionalSearchQuery = Object.keys(knownIssueSearchTermReplacements).reduce((accumulator, currentValue) => {\n const reg = new RegExp(currentValue, 'gi')\n return accumulator.replace(reg, knownIssueSearchTermReplacements[currentValue])\n }, sanitisedSearchQuery)\n\n const rawResults = await this.getOSPlacesResultsForQuery(sanitisedSearchQuery, config)\n const additionalResultsForKnownIssueSearchTerms =\n sanitisedSearchQuery === additionalSearchQuery\n ? []\n : await this.getOSPlacesResultsForQuery(additionalSearchQuery, config)\n\n return this.getOptimisedAddressesMatchingQuery(\n sanitisedSearchQuery,\n [...rawResults, ...additionalResultsForKnownIssueSearchTerms],\n config,\n )\n }\n\n public async getAddressByUprn(\n uprn: string,\n config: AddressesUprnQueryConfig = { osPlacesQueryParamOverrides: {} },\n ): Promise<OsAddress | null> {\n const response = await this.osPlacesApiClient.getAddressesByUprn(uprn, config.osPlacesQueryParamOverrides)\n const result = this.handleResponse(response)\n\n if (result.length === 0) return null\n if (result.length > 1) this.logger.info('Multiple results returned for UPRN')\n\n return result[result.length - 1]\n }\n\n public sanitiseUkPostcode(stringContainingPostcode: string) {\n const postCodeQuery = stringContainingPostCodeRegex.exec(stringContainingPostcode?.replace(/[^A-Z0-9 ]/gi, ''))\n if (!postCodeQuery) return stringContainingPostcode\n\n return `${postCodeQuery[1]}${postCodeQuery[2].toUpperCase().trim()} ${postCodeQuery[3].toUpperCase().trim()}${postCodeQuery[4]}`\n }\n\n private async getOSPlacesResultsForQuery(\n searchQuery: string,\n config: AddressesMatchingQueryConfig,\n ): Promise<OsAddress[]> {\n const response = await this.osPlacesApiClient.getAddressesByFreeTextQuery(\n this.sanitiseUkPostcode(searchQuery), // the API responds better when postcodes are properly formatted\n config.osPlacesQueryParamOverrides,\n )\n\n return this.handleResponse(response)\n }\n\n private getOptimisedAddressesMatchingQuery(\n searchQuery: string,\n rawResults: OsAddress[],\n config: AddressesMatchingQueryConfig,\n ): OsAddress[] {\n const bestMatchResults = new Fuse(rawResults, {\n shouldSort: true,\n threshold: 0.2,\n useExtendedSearch: true, // to allow search terms to be separated\n ignoreLocation: true, // to allow search terms to be separated\n keys: [{ name: 'addressString' }],\n ...config.fuzzyMatchOptionOverrides,\n })\n .search(searchQuery)\n .map(result => result.item)\n\n const queryIsAPostCode = simplePostCodeRegex.test(searchQuery)\n const isExactMatchToQuery = (addressString: string | undefined) =>\n searchQuery && this.sanitiseString(addressString)?.includes(searchQuery!)\n\n // By using `useExtendedSearch` and allowing search terms to be separated, we need to reprioritise exact match results:\n const preferExactMatchSort = (a: OsAddress, b: OsAddress) =>\n isExactMatchToQuery(a?.addressString) && !isExactMatchToQuery(b?.addressString) ? -1 : 1\n\n // This ensures house numbers are ordered numerically, not alphabetically when doing a search just on postcode:\n const buildingNumberSort = (a: OsAddress, b: OsAddress) =>\n a?.addressString && b?.addressString && queryIsAPostCode\n ? a.addressString.localeCompare(b.addressString, undefined, { numeric: true, sensitivity: 'base' })\n : 1\n\n // maximum number of properties in a postcode is 100\n return (bestMatchResults.length ? bestMatchResults : rawResults)\n .sort(queryIsAPostCode ? buildingNumberSort : preferExactMatchSort)\n .slice(0, 100)\n }\n\n private handleResponse(response: OsPlacesQueryResponse): OsAddress[] {\n if (response.header && response.header.totalresults === 0) return []\n\n return (\n response.results\n .map(\n result => (result.DPA && this.dpaToOsAddress(result.DPA)) || (result.LPI && this.lpiToOsAddress(result.LPI)),\n )\n .filter(result => !!result?.uprn)\n // remove duplicate results with the same UPRN:\n .filter((val, index, self) => self.findIndex(address => address?.uprn === val?.uprn) === index) as OsAddress[]\n )\n }\n\n private dpaToOsAddress(addressResult: OsPlacesDeliveryPointAddress): OsAddress | undefined {\n const {\n UPRN,\n ADDRESS,\n DEPENDENT_LOCALITY,\n ORGANISATION_NAME,\n SUB_BUILDING_NAME,\n BUILDING_NAME,\n BUILDING_NUMBER,\n THOROUGHFARE_NAME,\n POST_TOWN,\n POSTCODE,\n COUNTRY_CODE,\n LOCAL_CUSTODIAN_CODE_DESCRIPTION,\n POSTAL_ADDRESS_CODE,\n } = addressResult\n\n // Do not return address data that does not have a valid postal address:\n if (POSTAL_ADDRESS_CODE === 'N') return undefined\n\n const useOrganisationNameAsBuildingName =\n !isBlank(ORGANISATION_NAME) && isBlank(BUILDING_NAME) && isBlank(SUB_BUILDING_NAME)\n\n const buildingName = convertToTitleCase(useOrganisationNameAsBuildingName ? ORGANISATION_NAME : BUILDING_NAME)\n const subBuildingName = [useOrganisationNameAsBuildingName ? null : ORGANISATION_NAME, SUB_BUILDING_NAME]\n .filter(name => !isBlank(name))\n .map(name => convertToTitleCase(name!))\n .join(', ')\n\n return {\n addressString: this.formatAddressString(ADDRESS, BUILDING_NUMBER, THOROUGHFARE_NAME, POSTCODE),\n buildingNumber: BUILDING_NUMBER,\n buildingName,\n subBuildingName,\n thoroughfareName: convertToTitleCase(THOROUGHFARE_NAME),\n dependentLocality: convertToTitleCase(DEPENDENT_LOCALITY),\n postTown: convertToTitleCase(POST_TOWN),\n county: convertToTitleCase(LOCAL_CUSTODIAN_CODE_DESCRIPTION),\n postcode: POSTCODE,\n country: COUNTRY_CODE,\n uprn: UPRN,\n }\n }\n\n private lpiToOsAddress(addressResult: OsPlacesLandPropertyIdentifier): OsAddress | undefined {\n const {\n UPRN,\n ADDRESS,\n ORGANISATION,\n SAO_TEXT,\n PAO_START_NUMBER,\n PAO_TEXT,\n STREET_DESCRIPTION,\n TOWN_NAME,\n POSTCODE_LOCATOR,\n COUNTRY_CODE,\n POSTAL_ADDRESS_CODE,\n } = addressResult\n\n // Do not return address data that does not have a valid postal address:\n if (POSTAL_ADDRESS_CODE === 'N') return undefined\n\n const useOrganisationAsBuildingName = !isBlank(ORGANISATION) && isBlank(PAO_TEXT) && isBlank(SAO_TEXT)\n\n const buildingName = convertToTitleCase(useOrganisationAsBuildingName ? ORGANISATION : PAO_TEXT)\n const subBuildingName = [useOrganisationAsBuildingName ? null : ORGANISATION, SAO_TEXT]\n .filter(name => !isBlank(name))\n .map(name => convertToTitleCase(name!))\n .join(', ')\n\n return {\n addressString: this.formatAddressString(ADDRESS, PAO_START_NUMBER, STREET_DESCRIPTION, POSTCODE_LOCATOR),\n buildingNumber: PAO_START_NUMBER,\n buildingName,\n subBuildingName,\n thoroughfareName: convertToTitleCase(STREET_DESCRIPTION),\n postTown: convertToTitleCase(TOWN_NAME),\n postcode: POSTCODE_LOCATOR,\n country: COUNTRY_CODE,\n uprn: UPRN,\n }\n }\n\n private formatAddressString(\n addressString: string,\n buildingNumber: number,\n thoroughfareName: string,\n postcode: string,\n ) {\n const withoutPostcode = addressString\n .replace(`, ${postcode}`, '')\n .replace(`${buildingNumber}, ${thoroughfareName}`, `${buildingNumber} ${thoroughfareName}`)\n return `${convertToTitleCase(withoutPostcode)}, ${postcode}`\n }\n\n private sanitiseString(input: string | undefined) {\n return input\n ?.trim()\n ?.toLowerCase()\n ?.replace(/[^A-Z0-9-.' ]/gi, '')\n }\n}\n","import superagent from 'superagent'\nimport { ApiConfig } from '@ministryofjustice/hmpps-rest-client'\nimport { OsPlacesQueryResponse } from '../types/public/addressAutosuggest/osPlaces'\nimport { ConnectDpsComponentLogger } from '../types/public/ConnectDpsComponentLogger'\n\nexport default class OsPlacesApiClient {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly config: ApiConfig & { apiKey: string },\n ) {}\n\n async getAddressesByFreeTextQuery(\n freeTextQuery: string,\n queryParamOverrides: Record<string, unknown> = {},\n ): Promise<OsPlacesQueryResponse> {\n const queryParams: Record<string, unknown> = {\n query: freeTextQuery,\n key: this.config.apiKey,\n lr: 'EN',\n fq: ['LOGICAL_STATUS_CODE:1', 'LPI_LOGICAL_STATUS_CODE:1'], // only want active addresses\n dataset: 'LPI', // LPI chosen as default since it appears to perform better, especially for business addresses\n ...queryParamOverrides,\n }\n\n return this.get<Promise<OsPlacesQueryResponse>>('/find', queryParams)\n }\n\n async getAddressesByUprn(\n uprn: string,\n queryParamOverrides: Record<string, unknown> = {},\n ): Promise<OsPlacesQueryResponse> {\n const queryParams: Record<string, string> = {\n uprn,\n key: this.config.apiKey,\n dataset: 'DPA,LPI', // Both datasets available for the lookup by UPRN, preference for DPA because of the preferred data structure\n ...queryParamOverrides,\n }\n\n return this.get<Promise<OsPlacesQueryResponse>>('/uprn', queryParams)\n }\n\n private async get<T>(path: string, query: Record<string, unknown>): Promise<T> {\n const endpoint = `${this.config.url}${path}`\n try {\n const result = await superagent\n .get(endpoint)\n .retry(2, (err, _res) => {\n if (err) this.logger.info(`Retry handler found API error with ${err.code} ${err.message}`)\n return undefined // retry handler only for logging retries, not to influence retry logic\n })\n .query(query)\n .timeout(this.config.timeout)\n\n return result.body\n } catch (error) {\n const errorMessage = error && typeof error === 'object' && 'message' in error ? error.message : error\n throw Error(`Error calling OS Places API: ${errorMessage}`)\n }\n }\n}\n","import { Alert, AlertsServiceAlert } from '../types/public/alertFlags/Alert'\nimport { AlertFlagLabel } from '../types/public/alertFlags/AlertFlagLabel'\n\nexport const alertFlagLabels: AlertFlagLabel[] = [\n { alertCodes: ['HA'], classes: 'dps-alert-status dps-alert-status--self-harm', label: 'ACCT open' },\n {\n alertCodes: ['HA1'],\n classes: 'dps-alert-status dps-alert-status--self-harm',\n label: 'ACCT post closure',\n },\n {\n alertCodes: ['XSA', 'SA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Staff assaulter',\n },\n {\n alertCodes: ['XA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Arsonist',\n },\n {\n alertCodes: ['PEEP'],\n classes: 'dps-alert-status dps-alert-status--medical',\n label: 'PEEP',\n },\n {\n alertCodes: ['HID'],\n classes: 'dps-alert-status dps-alert-status--medical',\n label: 'Hidden disability',\n },\n { alertCodes: ['XEL'], classes: 'dps-alert-status dps-alert-status--security', label: 'E-list' },\n { alertCodes: ['XELH'], classes: 'dps-alert-status dps-alert-status--security', label: 'E-list heightened' },\n { alertCodes: ['XER'], classes: 'dps-alert-status dps-alert-status--security', label: 'Escape risk' },\n {\n alertCodes: ['XRF'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Risk to females',\n },\n { alertCodes: ['XTACT'], classes: 'dps-alert-status dps-alert-status--security', label: 'TACT' },\n {\n alertCodes: ['XCO'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Corruptor',\n },\n {\n alertCodes: ['XCA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Chemical attacker',\n },\n {\n alertCodes: ['XCI'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Concerted indiscipline',\n },\n { alertCodes: ['XR'], classes: 'dps-alert-status dps-alert-status--security', label: 'Racist' },\n {\n alertCodes: ['RTP', 'RLG'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'Risk to LGBT',\n },\n {\n alertCodes: ['XHT'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Hostage taker',\n },\n {\n alertCodes: ['XCU'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Controlled unlock',\n },\n {\n alertCodes: ['XGANG'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Gang member',\n },\n { alertCodes: ['CSIP'], classes: 'dps-alert-status dps-alert-status--other', label: 'CSIP' },\n { alertCodes: ['F1'], classes: 'dps-alert-status dps-alert-status--ex-armed-forces', label: 'Veteran' },\n {\n alertCodes: ['LCE'],\n classes: 'dps-alert-status dps-alert-status--care-leaver',\n label: 'Care experienced',\n },\n {\n alertCodes: ['RNO121'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'No one-to-one',\n },\n { alertCodes: ['RCON'], classes: 'dps-alert-status dps-alert-status--risk', label: 'Conflict' },\n {\n alertCodes: ['RCDR'],\n classes: 'dps-alert-status dps-alert-status--quarantined',\n label: 'Quarantined',\n },\n {\n alertCodes: ['URCU'],\n classes: 'dps-alert-status dps-alert-status--reverse-cohorting-unit',\n label: 'Reverse Cohorting Unit',\n },\n {\n alertCodes: ['UPIU'],\n classes: 'dps-alert-status dps-alert-status--protective-isolation-unit',\n label: 'Protective Isolation Unit',\n },\n { alertCodes: ['USU'], classes: 'dps-alert-status dps-alert-status--shielding-unit', label: 'Shielding Unit' },\n {\n alertCodes: ['URS'],\n classes: 'dps-alert-status dps-alert-status--refusing-to-shield',\n label: 'Refusing to shield',\n },\n {\n alertCodes: ['RKS'],\n classes: 'dps-alert-status dps-alert-status--risk-to-known-adults',\n label: 'Risk to known adults',\n },\n { alertCodes: ['VIP'], classes: 'dps-alert-status dps-alert-status--isolated-prisoner', label: 'Isolated' },\n {\n alertCodes: ['PVN'],\n classes: 'dps-alert-status dps-alert-status--multicase dps-alert-status--visor',\n label: 'ViSOR',\n },\n {\n alertCodes: ['XCDO'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Involved in 2024 civil disorder',\n },\n {\n alertCodes: ['XVL'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Violent',\n },\n {\n alertCodes: ['DOCGM'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'OCG nominal (do not share)',\n },\n {\n alertCodes: ['RSS'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'Risk to staff',\n },\n {\n alertCodes: ['XCOP'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Potential corruptor',\n },\n].sort((a, b) => a.label.localeCompare(b.label))\n\nfunction isAlertsServiceAlert(alert: Alert): alert is AlertsServiceAlert {\n return typeof alert.alertCode !== 'string'\n}\n\nexport function getAlertFlagLabelsForAlerts(prisonerAlerts: Alert[]): AlertFlagLabel[] {\n return alertFlagLabels.reduce(\n (acc: AlertFlagLabel[], flag: { alertCodes: string[]; classes: string; label: string }) => {\n const alertIds = prisonerAlerts\n .filter(alert => {\n const alertsServiceAlert = isAlertsServiceAlert(alert)\n const alertIsActive = alertsServiceAlert ? alert.isActive : alert.active && !alert.expired\n const prisonerAlertCode = alertsServiceAlert ? alert.alertCode.code : alert.alertCode\n\n return alertIsActive && flag.alertCodes.includes(prisonerAlertCode)\n })\n .map(alert => (isAlertsServiceAlert(alert) ? (alert.alertUuid as string) : alert.alertCode))\n\n return alertIds.length ? [...acc, { ...flag, alertIds }] : acc\n },\n [] as AlertFlagLabel[],\n )\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,78 +1,9 @@
|
|
|
1
1
|
import Logger from 'bunyan';
|
|
2
|
+
import { IFuseOptions } from 'fuse.js';
|
|
2
3
|
import { ApiConfig } from '@ministryofjustice/hmpps-rest-client';
|
|
3
4
|
|
|
4
5
|
type ConnectDpsComponentLogger = Logger | typeof console;
|
|
5
6
|
|
|
6
|
-
interface AlertFlagLabel {
|
|
7
|
-
alertCodes: string[];
|
|
8
|
-
classes: string;
|
|
9
|
-
label: string;
|
|
10
|
-
alertIds?: string[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type Alert = BasicAlert | PrisonApiAlert | AlertsServiceAlert;
|
|
14
|
-
interface BasicAlert {
|
|
15
|
-
alertType: string;
|
|
16
|
-
alertCode: string;
|
|
17
|
-
active: boolean;
|
|
18
|
-
expired: boolean;
|
|
19
|
-
}
|
|
20
|
-
interface PrisonApiAlert {
|
|
21
|
-
alertId: string;
|
|
22
|
-
alertType: string;
|
|
23
|
-
alertTypeDescription: string;
|
|
24
|
-
alertCode: string;
|
|
25
|
-
alertCodeDescription: string;
|
|
26
|
-
comment?: string;
|
|
27
|
-
dateCreated: string;
|
|
28
|
-
modifiedDateTime?: string;
|
|
29
|
-
dateExpires?: string;
|
|
30
|
-
expired: boolean;
|
|
31
|
-
active: boolean;
|
|
32
|
-
addedByFirstName?: string;
|
|
33
|
-
addedByLastName?: string;
|
|
34
|
-
addedByFullName?: string;
|
|
35
|
-
expiredByFirstName?: string;
|
|
36
|
-
expiredByLastName?: string;
|
|
37
|
-
expiredByFullName?: string;
|
|
38
|
-
}
|
|
39
|
-
interface AlertsServiceAlert {
|
|
40
|
-
alertUuid?: string;
|
|
41
|
-
prisonNumber?: string;
|
|
42
|
-
alertCode: {
|
|
43
|
-
alertTypeCode: string;
|
|
44
|
-
alertTypeDescription: string;
|
|
45
|
-
code: string;
|
|
46
|
-
description: string;
|
|
47
|
-
};
|
|
48
|
-
description: string;
|
|
49
|
-
authorisedBy?: string;
|
|
50
|
-
activeFrom: string;
|
|
51
|
-
activeTo?: string;
|
|
52
|
-
isActive: boolean;
|
|
53
|
-
comments?: [
|
|
54
|
-
{
|
|
55
|
-
commentUuid: string;
|
|
56
|
-
comment: string;
|
|
57
|
-
createdAt: string;
|
|
58
|
-
createdBy: string;
|
|
59
|
-
createdByDisplayName: string;
|
|
60
|
-
}
|
|
61
|
-
];
|
|
62
|
-
createdAt?: string;
|
|
63
|
-
createdBy?: string;
|
|
64
|
-
createdByDisplayName: string;
|
|
65
|
-
lastModifiedAt?: string;
|
|
66
|
-
lastModifiedBy?: string;
|
|
67
|
-
lastModifiedByDisplayName?: string;
|
|
68
|
-
activeToLastSetAt?: string;
|
|
69
|
-
activeToLastSetBy?: string;
|
|
70
|
-
activeToLastSetByDisplayName?: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
declare const alertFlagLabels: AlertFlagLabel[];
|
|
74
|
-
declare function getAlertFlagLabelsForAlerts(prisonerAlerts: Alert[]): AlertFlagLabel[];
|
|
75
|
-
|
|
76
7
|
interface OsAddress {
|
|
77
8
|
addressString?: string;
|
|
78
9
|
buildingNumber?: number;
|
|
@@ -218,22 +149,103 @@ declare class OsPlacesApiClient {
|
|
|
218
149
|
constructor(logger: ConnectDpsComponentLogger, config: ApiConfig & {
|
|
219
150
|
apiKey: string;
|
|
220
151
|
});
|
|
221
|
-
getAddressesByFreeTextQuery(freeTextQuery: string): Promise<OsPlacesQueryResponse>;
|
|
222
|
-
getAddressesByUprn(uprn: string): Promise<OsPlacesQueryResponse>;
|
|
223
|
-
get
|
|
152
|
+
getAddressesByFreeTextQuery(freeTextQuery: string, queryParamOverrides?: Record<string, unknown>): Promise<OsPlacesQueryResponse>;
|
|
153
|
+
getAddressesByUprn(uprn: string, queryParamOverrides?: Record<string, unknown>): Promise<OsPlacesQueryResponse>;
|
|
154
|
+
private get;
|
|
224
155
|
}
|
|
225
156
|
|
|
157
|
+
interface AddressesMatchingQueryConfig {
|
|
158
|
+
osPlacesQueryParamOverrides: Record<string, unknown>;
|
|
159
|
+
fuzzyMatchOptionOverrides: IFuseOptions<OsAddress>;
|
|
160
|
+
}
|
|
161
|
+
interface AddressesUprnQueryConfig {
|
|
162
|
+
osPlacesQueryParamOverrides: Record<string, unknown>;
|
|
163
|
+
}
|
|
226
164
|
declare class OsPlacesAddressService {
|
|
227
165
|
private readonly logger;
|
|
228
166
|
private readonly osPlacesApiClient;
|
|
229
167
|
constructor(logger: ConnectDpsComponentLogger, osPlacesApiClient: OsPlacesApiClient);
|
|
230
|
-
getAddressesMatchingQuery(
|
|
231
|
-
getAddressByUprn(uprn: string): Promise<OsAddress | null>;
|
|
168
|
+
getAddressesMatchingQuery(rawSearchQuery: string, config?: AddressesMatchingQueryConfig): Promise<OsAddress[]>;
|
|
169
|
+
getAddressByUprn(uprn: string, config?: AddressesUprnQueryConfig): Promise<OsAddress | null>;
|
|
232
170
|
sanitiseUkPostcode(stringContainingPostcode: string): string;
|
|
171
|
+
private getOSPlacesResultsForQuery;
|
|
172
|
+
private getOptimisedAddressesMatchingQuery;
|
|
233
173
|
private handleResponse;
|
|
234
|
-
private
|
|
174
|
+
private dpaToOsAddress;
|
|
175
|
+
private lpiToOsAddress;
|
|
235
176
|
private formatAddressString;
|
|
177
|
+
private sanitiseString;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
interface AlertFlagLabel {
|
|
181
|
+
alertCodes: string[];
|
|
182
|
+
classes: string;
|
|
183
|
+
label: string;
|
|
184
|
+
alertIds?: string[];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
type Alert = BasicAlert | PrisonApiAlert | AlertsServiceAlert;
|
|
188
|
+
interface BasicAlert {
|
|
189
|
+
alertType: string;
|
|
190
|
+
alertCode: string;
|
|
191
|
+
active: boolean;
|
|
192
|
+
expired: boolean;
|
|
193
|
+
}
|
|
194
|
+
interface PrisonApiAlert {
|
|
195
|
+
alertId: string;
|
|
196
|
+
alertType: string;
|
|
197
|
+
alertTypeDescription: string;
|
|
198
|
+
alertCode: string;
|
|
199
|
+
alertCodeDescription: string;
|
|
200
|
+
comment?: string;
|
|
201
|
+
dateCreated: string;
|
|
202
|
+
modifiedDateTime?: string;
|
|
203
|
+
dateExpires?: string;
|
|
204
|
+
expired: boolean;
|
|
205
|
+
active: boolean;
|
|
206
|
+
addedByFirstName?: string;
|
|
207
|
+
addedByLastName?: string;
|
|
208
|
+
addedByFullName?: string;
|
|
209
|
+
expiredByFirstName?: string;
|
|
210
|
+
expiredByLastName?: string;
|
|
211
|
+
expiredByFullName?: string;
|
|
236
212
|
}
|
|
213
|
+
interface AlertsServiceAlert {
|
|
214
|
+
alertUuid?: string;
|
|
215
|
+
prisonNumber?: string;
|
|
216
|
+
alertCode: {
|
|
217
|
+
alertTypeCode: string;
|
|
218
|
+
alertTypeDescription: string;
|
|
219
|
+
code: string;
|
|
220
|
+
description: string;
|
|
221
|
+
};
|
|
222
|
+
description: string;
|
|
223
|
+
authorisedBy?: string;
|
|
224
|
+
activeFrom: string;
|
|
225
|
+
activeTo?: string;
|
|
226
|
+
isActive: boolean;
|
|
227
|
+
comments?: [
|
|
228
|
+
{
|
|
229
|
+
commentUuid: string;
|
|
230
|
+
comment: string;
|
|
231
|
+
createdAt: string;
|
|
232
|
+
createdBy: string;
|
|
233
|
+
createdByDisplayName: string;
|
|
234
|
+
}
|
|
235
|
+
];
|
|
236
|
+
createdAt?: string;
|
|
237
|
+
createdBy?: string;
|
|
238
|
+
createdByDisplayName: string;
|
|
239
|
+
lastModifiedAt?: string;
|
|
240
|
+
lastModifiedBy?: string;
|
|
241
|
+
lastModifiedByDisplayName?: string;
|
|
242
|
+
activeToLastSetAt?: string;
|
|
243
|
+
activeToLastSetBy?: string;
|
|
244
|
+
activeToLastSetByDisplayName?: string;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
declare const alertFlagLabels: AlertFlagLabel[];
|
|
248
|
+
declare function getAlertFlagLabelsForAlerts(prisonerAlerts: Alert[]): AlertFlagLabel[];
|
|
237
249
|
|
|
238
250
|
export { OsPlacesAddressService, OsPlacesApiClient, alertFlagLabels, getAlertFlagLabelsForAlerts };
|
|
239
251
|
export type { Alert, AlertFlagLabel, AlertsServiceAlert, BasicAlert, ConnectDpsComponentLogger, OsAddress, OsPlacesDeliveryPointAddress, OsPlacesLandPropertyIdentifier, OsPlacesQueryResponse, OsPlacesQueryResult, OsPlacesResponseHeader, PrisonApiAlert };
|
package/dist/index.esm.js
CHANGED
|
@@ -10,10 +10,19 @@ const properCase = (word) => word.length >= 1 ? word[0].toUpperCase() + word.toL
|
|
|
10
10
|
* @returns name converted to proper case.
|
|
11
11
|
*/
|
|
12
12
|
const properCaseName = (name) => (isBlank(name) ? '' : name.split('-').map(properCase).join('-'));
|
|
13
|
-
const convertToTitleCase = (sentence) => isBlank(sentence)
|
|
13
|
+
const convertToTitleCase = (sentence) => isBlank(sentence)
|
|
14
|
+
? ''
|
|
15
|
+
: sentence
|
|
16
|
+
.split('(') // Handle parentheses
|
|
17
|
+
.map(subsentence => subsentence.split(' ').map(properCaseName).join(' '))
|
|
18
|
+
.join('(');
|
|
14
19
|
|
|
15
20
|
const simplePostCodeRegex = /^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$/i;
|
|
16
21
|
const stringContainingPostCodeRegex = /^(.*?)([A-Z]{1,2}\d[A-Z\d]? ?)(\d[A-Z]{2})(.*)$/i;
|
|
22
|
+
// Where we know OS Places API struggles with certain search terms, we will perform an additional replacement search:
|
|
23
|
+
const knownIssueSearchTermReplacements = {
|
|
24
|
+
amazon: 'amazon.co.uk',
|
|
25
|
+
};
|
|
17
26
|
class OsPlacesAddressService {
|
|
18
27
|
logger;
|
|
19
28
|
osPlacesApiClient;
|
|
@@ -21,31 +30,25 @@ class OsPlacesAddressService {
|
|
|
21
30
|
this.logger = logger;
|
|
22
31
|
this.osPlacesApiClient = osPlacesApiClient;
|
|
23
32
|
}
|
|
24
|
-
async getAddressesMatchingQuery(
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
const queryIsAPostCode = simplePostCodeRegex.test(searchQuery.trim());
|
|
36
|
-
const buildingNumberSort = (a, b) => a?.addressString && b?.addressString && queryIsAPostCode
|
|
37
|
-
? a.addressString.localeCompare(b.addressString, undefined, { numeric: true, sensitivity: 'base' })
|
|
38
|
-
: 1;
|
|
39
|
-
// maximum number of properties in a postcode is 100
|
|
40
|
-
return (bestMatchResults.length ? bestMatchResults : rawResults).sort(buildingNumberSort).slice(0, 100);
|
|
33
|
+
async getAddressesMatchingQuery(rawSearchQuery, config = { osPlacesQueryParamOverrides: {}, fuzzyMatchOptionOverrides: {} }) {
|
|
34
|
+
const sanitisedSearchQuery = this.sanitiseString(rawSearchQuery) || '';
|
|
35
|
+
const additionalSearchQuery = Object.keys(knownIssueSearchTermReplacements).reduce((accumulator, currentValue) => {
|
|
36
|
+
const reg = new RegExp(currentValue, 'gi');
|
|
37
|
+
return accumulator.replace(reg, knownIssueSearchTermReplacements[currentValue]);
|
|
38
|
+
}, sanitisedSearchQuery);
|
|
39
|
+
const rawResults = await this.getOSPlacesResultsForQuery(sanitisedSearchQuery, config);
|
|
40
|
+
const additionalResultsForKnownIssueSearchTerms = sanitisedSearchQuery === additionalSearchQuery
|
|
41
|
+
? []
|
|
42
|
+
: await this.getOSPlacesResultsForQuery(additionalSearchQuery, config);
|
|
43
|
+
return this.getOptimisedAddressesMatchingQuery(sanitisedSearchQuery, [...rawResults, ...additionalResultsForKnownIssueSearchTerms], config);
|
|
41
44
|
}
|
|
42
|
-
async getAddressByUprn(uprn) {
|
|
43
|
-
const response = await this.osPlacesApiClient.getAddressesByUprn(uprn);
|
|
45
|
+
async getAddressByUprn(uprn, config = { osPlacesQueryParamOverrides: {} }) {
|
|
46
|
+
const response = await this.osPlacesApiClient.getAddressesByUprn(uprn, config.osPlacesQueryParamOverrides);
|
|
44
47
|
const result = this.handleResponse(response);
|
|
45
48
|
if (result.length === 0)
|
|
46
49
|
return null;
|
|
47
50
|
if (result.length > 1)
|
|
48
|
-
this.logger.info(
|
|
51
|
+
this.logger.info('Multiple results returned for UPRN');
|
|
49
52
|
return result[result.length - 1];
|
|
50
53
|
}
|
|
51
54
|
sanitiseUkPostcode(stringContainingPostcode) {
|
|
@@ -54,18 +57,60 @@ class OsPlacesAddressService {
|
|
|
54
57
|
return stringContainingPostcode;
|
|
55
58
|
return `${postCodeQuery[1]}${postCodeQuery[2].toUpperCase().trim()} ${postCodeQuery[3].toUpperCase().trim()}${postCodeQuery[4]}`;
|
|
56
59
|
}
|
|
60
|
+
async getOSPlacesResultsForQuery(searchQuery, config) {
|
|
61
|
+
const response = await this.osPlacesApiClient.getAddressesByFreeTextQuery(this.sanitiseUkPostcode(searchQuery), // the API responds better when postcodes are properly formatted
|
|
62
|
+
config.osPlacesQueryParamOverrides);
|
|
63
|
+
return this.handleResponse(response);
|
|
64
|
+
}
|
|
65
|
+
getOptimisedAddressesMatchingQuery(searchQuery, rawResults, config) {
|
|
66
|
+
const bestMatchResults = new Fuse(rawResults, {
|
|
67
|
+
shouldSort: true,
|
|
68
|
+
threshold: 0.2,
|
|
69
|
+
useExtendedSearch: true, // to allow search terms to be separated
|
|
70
|
+
ignoreLocation: true, // to allow search terms to be separated
|
|
71
|
+
keys: [{ name: 'addressString' }],
|
|
72
|
+
...config.fuzzyMatchOptionOverrides,
|
|
73
|
+
})
|
|
74
|
+
.search(searchQuery)
|
|
75
|
+
.map(result => result.item);
|
|
76
|
+
const queryIsAPostCode = simplePostCodeRegex.test(searchQuery);
|
|
77
|
+
const isExactMatchToQuery = (addressString) => searchQuery && this.sanitiseString(addressString)?.includes(searchQuery);
|
|
78
|
+
// By using `useExtendedSearch` and allowing search terms to be separated, we need to reprioritise exact match results:
|
|
79
|
+
const preferExactMatchSort = (a, b) => isExactMatchToQuery(a?.addressString) && !isExactMatchToQuery(b?.addressString) ? -1 : 1;
|
|
80
|
+
// This ensures house numbers are ordered numerically, not alphabetically when doing a search just on postcode:
|
|
81
|
+
const buildingNumberSort = (a, b) => a?.addressString && b?.addressString && queryIsAPostCode
|
|
82
|
+
? a.addressString.localeCompare(b.addressString, undefined, { numeric: true, sensitivity: 'base' })
|
|
83
|
+
: 1;
|
|
84
|
+
// maximum number of properties in a postcode is 100
|
|
85
|
+
return (bestMatchResults.length ? bestMatchResults : rawResults)
|
|
86
|
+
.sort(queryIsAPostCode ? buildingNumberSort : preferExactMatchSort)
|
|
87
|
+
.slice(0, 100);
|
|
88
|
+
}
|
|
57
89
|
handleResponse(response) {
|
|
58
90
|
if (response.header && response.header.totalresults === 0)
|
|
59
91
|
return [];
|
|
60
|
-
return response.results
|
|
92
|
+
return response.results
|
|
93
|
+
.map(result => (result.DPA && this.dpaToOsAddress(result.DPA)) || (result.LPI && this.lpiToOsAddress(result.LPI)))
|
|
94
|
+
.filter(result => !!result?.uprn)
|
|
95
|
+
// remove duplicate results with the same UPRN:
|
|
96
|
+
.filter((val, index, self) => self.findIndex(address => address?.uprn === val?.uprn) === index);
|
|
61
97
|
}
|
|
62
|
-
|
|
63
|
-
const { UPRN, DEPENDENT_LOCALITY, SUB_BUILDING_NAME, BUILDING_NAME, BUILDING_NUMBER, THOROUGHFARE_NAME, POST_TOWN, POSTCODE, COUNTRY_CODE, LOCAL_CUSTODIAN_CODE_DESCRIPTION, } = addressResult;
|
|
98
|
+
dpaToOsAddress(addressResult) {
|
|
99
|
+
const { UPRN, ADDRESS, DEPENDENT_LOCALITY, ORGANISATION_NAME, SUB_BUILDING_NAME, BUILDING_NAME, BUILDING_NUMBER, THOROUGHFARE_NAME, POST_TOWN, POSTCODE, COUNTRY_CODE, LOCAL_CUSTODIAN_CODE_DESCRIPTION, POSTAL_ADDRESS_CODE, } = addressResult;
|
|
100
|
+
// Do not return address data that does not have a valid postal address:
|
|
101
|
+
if (POSTAL_ADDRESS_CODE === 'N')
|
|
102
|
+
return undefined;
|
|
103
|
+
const useOrganisationNameAsBuildingName = !isBlank(ORGANISATION_NAME) && isBlank(BUILDING_NAME) && isBlank(SUB_BUILDING_NAME);
|
|
104
|
+
const buildingName = convertToTitleCase(useOrganisationNameAsBuildingName ? ORGANISATION_NAME : BUILDING_NAME);
|
|
105
|
+
const subBuildingName = [useOrganisationNameAsBuildingName ? null : ORGANISATION_NAME, SUB_BUILDING_NAME]
|
|
106
|
+
.filter(name => !isBlank(name))
|
|
107
|
+
.map(name => convertToTitleCase(name))
|
|
108
|
+
.join(', ');
|
|
64
109
|
return {
|
|
65
|
-
addressString: this.formatAddressString(
|
|
110
|
+
addressString: this.formatAddressString(ADDRESS, BUILDING_NUMBER, THOROUGHFARE_NAME, POSTCODE),
|
|
66
111
|
buildingNumber: BUILDING_NUMBER,
|
|
67
|
-
buildingName
|
|
68
|
-
subBuildingName
|
|
112
|
+
buildingName,
|
|
113
|
+
subBuildingName,
|
|
69
114
|
thoroughfareName: convertToTitleCase(THOROUGHFARE_NAME),
|
|
70
115
|
dependentLocality: convertToTitleCase(DEPENDENT_LOCALITY),
|
|
71
116
|
postTown: convertToTitleCase(POST_TOWN),
|
|
@@ -75,10 +120,40 @@ class OsPlacesAddressService {
|
|
|
75
120
|
uprn: UPRN,
|
|
76
121
|
};
|
|
77
122
|
}
|
|
78
|
-
|
|
79
|
-
const { ADDRESS,
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
lpiToOsAddress(addressResult) {
|
|
124
|
+
const { UPRN, ADDRESS, ORGANISATION, SAO_TEXT, PAO_START_NUMBER, PAO_TEXT, STREET_DESCRIPTION, TOWN_NAME, POSTCODE_LOCATOR, COUNTRY_CODE, POSTAL_ADDRESS_CODE, } = addressResult;
|
|
125
|
+
// Do not return address data that does not have a valid postal address:
|
|
126
|
+
if (POSTAL_ADDRESS_CODE === 'N')
|
|
127
|
+
return undefined;
|
|
128
|
+
const useOrganisationAsBuildingName = !isBlank(ORGANISATION) && isBlank(PAO_TEXT) && isBlank(SAO_TEXT);
|
|
129
|
+
const buildingName = convertToTitleCase(useOrganisationAsBuildingName ? ORGANISATION : PAO_TEXT);
|
|
130
|
+
const subBuildingName = [useOrganisationAsBuildingName ? null : ORGANISATION, SAO_TEXT]
|
|
131
|
+
.filter(name => !isBlank(name))
|
|
132
|
+
.map(name => convertToTitleCase(name))
|
|
133
|
+
.join(', ');
|
|
134
|
+
return {
|
|
135
|
+
addressString: this.formatAddressString(ADDRESS, PAO_START_NUMBER, STREET_DESCRIPTION, POSTCODE_LOCATOR),
|
|
136
|
+
buildingNumber: PAO_START_NUMBER,
|
|
137
|
+
buildingName,
|
|
138
|
+
subBuildingName,
|
|
139
|
+
thoroughfareName: convertToTitleCase(STREET_DESCRIPTION),
|
|
140
|
+
postTown: convertToTitleCase(TOWN_NAME),
|
|
141
|
+
postcode: POSTCODE_LOCATOR,
|
|
142
|
+
country: COUNTRY_CODE,
|
|
143
|
+
uprn: UPRN,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
formatAddressString(addressString, buildingNumber, thoroughfareName, postcode) {
|
|
147
|
+
const withoutPostcode = addressString
|
|
148
|
+
.replace(`, ${postcode}`, '')
|
|
149
|
+
.replace(`${buildingNumber}, ${thoroughfareName}`, `${buildingNumber} ${thoroughfareName}`);
|
|
150
|
+
return `${convertToTitleCase(withoutPostcode)}, ${postcode}`;
|
|
151
|
+
}
|
|
152
|
+
sanitiseString(input) {
|
|
153
|
+
return input
|
|
154
|
+
?.trim()
|
|
155
|
+
?.toLowerCase()
|
|
156
|
+
?.replace(/[^A-Z0-9-.' ]/gi, '');
|
|
82
157
|
}
|
|
83
158
|
}
|
|
84
159
|
|
|
@@ -89,18 +164,23 @@ class OsPlacesApiClient {
|
|
|
89
164
|
this.logger = logger;
|
|
90
165
|
this.config = config;
|
|
91
166
|
}
|
|
92
|
-
async getAddressesByFreeTextQuery(freeTextQuery) {
|
|
167
|
+
async getAddressesByFreeTextQuery(freeTextQuery, queryParamOverrides = {}) {
|
|
93
168
|
const queryParams = {
|
|
94
169
|
query: freeTextQuery,
|
|
95
|
-
lr: 'EN',
|
|
96
170
|
key: this.config.apiKey,
|
|
171
|
+
lr: 'EN',
|
|
172
|
+
fq: ['LOGICAL_STATUS_CODE:1', 'LPI_LOGICAL_STATUS_CODE:1'], // only want active addresses
|
|
173
|
+
dataset: 'LPI', // LPI chosen as default since it appears to perform better, especially for business addresses
|
|
174
|
+
...queryParamOverrides,
|
|
97
175
|
};
|
|
98
176
|
return this.get('/find', queryParams);
|
|
99
177
|
}
|
|
100
|
-
async getAddressesByUprn(uprn) {
|
|
178
|
+
async getAddressesByUprn(uprn, queryParamOverrides = {}) {
|
|
101
179
|
const queryParams = {
|
|
102
180
|
uprn,
|
|
103
181
|
key: this.config.apiKey,
|
|
182
|
+
dataset: 'DPA,LPI', // Both datasets available for the lookup by UPRN, preference for DPA because of the preferred data structure
|
|
183
|
+
...queryParamOverrides,
|
|
104
184
|
};
|
|
105
185
|
return this.get('/uprn', queryParams);
|
|
106
186
|
}
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/utils/utils.ts","../src/addressAutosuggest/osPlacesAddressService.ts","../src/addressAutosuggest/osPlacesApiClient.ts","../src/alertFlags/getAlertFlagLabelsForAlerts.ts"],"sourcesContent":["const isBlank = (str: string): boolean => !str || /^\\s*$/.test(str)\n\nconst properCase = (word: string): string =>\n word.length >= 1 ? word[0].toUpperCase() + word.toLowerCase().slice(1) : word\n\n/**\n * Converts a name (first name, last name, middle name, etc.) to proper case equivalent, handling double-barreled names\n * correctly (i.e. each part in a double-barreled is converted to proper case).\n * @param name name to be converted.\n * @returns name converted to proper case.\n */\nexport const properCaseName = (name: string): string => (isBlank(name) ? '' : name.split('-').map(properCase).join('-'))\n\nexport const convertToTitleCase = (sentence: string): string =>\n isBlank(sentence) ? '' : sentence.split(' ').map(properCaseName).join(' ')\n","import Fuse from 'fuse.js'\nimport { OsAddress } from '../types/public/addressAutosuggest/osAddress'\nimport { convertToTitleCase } from '../utils/utils'\nimport OsPlacesApiClient from './osPlacesApiClient'\nimport { ConnectDpsComponentLogger } from '../types/public/ConnectDpsComponentLogger'\nimport { OsPlacesDeliveryPointAddress, OsPlacesQueryResponse } from '../types/public/addressAutosuggest/osPlaces'\n\nconst simplePostCodeRegex = /^[A-Z]{1,2}\\d[A-Z\\d]? ?\\d[A-Z]{2}$/i\nconst stringContainingPostCodeRegex = /^(.*?)([A-Z]{1,2}\\d[A-Z\\d]? ?)(\\d[A-Z]{2})(.*)$/i\n\nexport default class OsPlacesAddressService {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly osPlacesApiClient: OsPlacesApiClient,\n ) {}\n\n public async getAddressesMatchingQuery(searchQuery: string, sanitisePostcode: boolean = true): Promise<OsAddress[]> {\n const response = await this.osPlacesApiClient.getAddressesByFreeTextQuery(\n sanitisePostcode ? this.sanitiseUkPostcode(searchQuery) : searchQuery,\n )\n\n const rawResults = this.handleResponse(response)\n\n const bestMatchResults = new Fuse(rawResults, {\n shouldSort: true,\n threshold: 0.2,\n ignoreLocation: true,\n keys: [{ name: 'addressString' }],\n })\n .search(searchQuery)\n .map(result => result.item)\n\n const queryIsAPostCode = simplePostCodeRegex.test(searchQuery.trim())\n const buildingNumberSort = (a: OsAddress, b: OsAddress) =>\n a?.addressString && b?.addressString && queryIsAPostCode\n ? a.addressString.localeCompare(b.addressString, undefined, { numeric: true, sensitivity: 'base' })\n : 1\n\n // maximum number of properties in a postcode is 100\n return (bestMatchResults.length ? bestMatchResults : rawResults).sort(buildingNumberSort).slice(0, 100)\n }\n\n public async getAddressByUprn(uprn: string): Promise<OsAddress | null> {\n const response = await this.osPlacesApiClient.getAddressesByUprn(uprn)\n const result = this.handleResponse(response)\n\n if (result.length === 0) return null\n if (result.length > 1) this.logger.info(`Multiple results returned for UPRN`)\n\n return result[result.length - 1]\n }\n\n public sanitiseUkPostcode(stringContainingPostcode: string) {\n const postCodeQuery = stringContainingPostCodeRegex.exec(stringContainingPostcode?.replace(/[^A-Z0-9 ]/gi, ''))\n if (!postCodeQuery) return stringContainingPostcode\n\n return `${postCodeQuery[1]}${postCodeQuery[2].toUpperCase().trim()} ${postCodeQuery[3].toUpperCase().trim()}${postCodeQuery[4]}`\n }\n\n private handleResponse(response: OsPlacesQueryResponse): OsAddress[] {\n if (response.header && response.header.totalresults === 0) return []\n\n return response.results.filter(result => result.DPA).map(result => this.toOsAddress(result.DPA!))\n }\n\n private toOsAddress(addressResult: OsPlacesDeliveryPointAddress): OsAddress {\n const {\n UPRN,\n DEPENDENT_LOCALITY,\n SUB_BUILDING_NAME,\n BUILDING_NAME,\n BUILDING_NUMBER,\n THOROUGHFARE_NAME,\n POST_TOWN,\n POSTCODE,\n COUNTRY_CODE,\n LOCAL_CUSTODIAN_CODE_DESCRIPTION,\n } = addressResult\n\n return {\n addressString: this.formatAddressString(addressResult),\n buildingNumber: BUILDING_NUMBER,\n buildingName: convertToTitleCase(BUILDING_NAME),\n subBuildingName: convertToTitleCase(SUB_BUILDING_NAME),\n thoroughfareName: convertToTitleCase(THOROUGHFARE_NAME),\n dependentLocality: convertToTitleCase(DEPENDENT_LOCALITY),\n postTown: convertToTitleCase(POST_TOWN),\n county: convertToTitleCase(LOCAL_CUSTODIAN_CODE_DESCRIPTION),\n postcode: POSTCODE,\n country: COUNTRY_CODE,\n uprn: UPRN,\n }\n }\n\n private formatAddressString(addressResult: OsPlacesDeliveryPointAddress) {\n const { ADDRESS, BUILDING_NUMBER, THOROUGHFARE_NAME, POSTCODE } = addressResult\n const withoutPostcode = ADDRESS.replace(`, ${POSTCODE}`, '').replace(\n `${BUILDING_NUMBER}, ${THOROUGHFARE_NAME}`,\n `${BUILDING_NUMBER} ${THOROUGHFARE_NAME}`,\n )\n return `${convertToTitleCase(withoutPostcode)}, ${POSTCODE}`\n }\n}\n","import superagent from 'superagent'\nimport { ApiConfig } from '@ministryofjustice/hmpps-rest-client'\nimport { OsPlacesQueryResponse } from '../types/public/addressAutosuggest/osPlaces'\nimport { ConnectDpsComponentLogger } from '../types/public/ConnectDpsComponentLogger'\n\nexport default class OsPlacesApiClient {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly config: ApiConfig & { apiKey: string },\n ) {}\n\n async getAddressesByFreeTextQuery(freeTextQuery: string): Promise<OsPlacesQueryResponse> {\n const queryParams: Record<string, string> = {\n query: freeTextQuery,\n lr: 'EN',\n key: this.config.apiKey,\n }\n\n return this.get<Promise<OsPlacesQueryResponse>>('/find', queryParams)\n }\n\n async getAddressesByUprn(uprn: string): Promise<OsPlacesQueryResponse> {\n const queryParams: Record<string, string> = {\n uprn,\n key: this.config.apiKey,\n }\n\n return this.get<Promise<OsPlacesQueryResponse>>('/uprn', queryParams)\n }\n\n async get<T>(path: string, query: Record<string, string>): Promise<T> {\n const endpoint = `${this.config.url}${path}`\n try {\n const result = await superagent\n .get(endpoint)\n .retry(2, (err, _res) => {\n if (err) this.logger.info(`Retry handler found API error with ${err.code} ${err.message}`)\n return undefined // retry handler only for logging retries, not to influence retry logic\n })\n .query(query)\n .timeout(this.config.timeout)\n\n return result.body\n } catch (error) {\n const errorMessage = error && typeof error === 'object' && 'message' in error ? error.message : error\n throw Error(`Error calling OS Places API: ${errorMessage}`)\n }\n }\n}\n","import { Alert, AlertsServiceAlert } from '../types/public/alertFlags/Alert'\nimport { AlertFlagLabel } from '../types/public/alertFlags/AlertFlagLabel'\n\nexport const alertFlagLabels: AlertFlagLabel[] = [\n { alertCodes: ['HA'], classes: 'dps-alert-status dps-alert-status--self-harm', label: 'ACCT open' },\n {\n alertCodes: ['HA1'],\n classes: 'dps-alert-status dps-alert-status--self-harm',\n label: 'ACCT post closure',\n },\n {\n alertCodes: ['XSA', 'SA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Staff assaulter',\n },\n {\n alertCodes: ['XA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Arsonist',\n },\n {\n alertCodes: ['PEEP'],\n classes: 'dps-alert-status dps-alert-status--medical',\n label: 'PEEP',\n },\n {\n alertCodes: ['HID'],\n classes: 'dps-alert-status dps-alert-status--medical',\n label: 'Hidden disability',\n },\n { alertCodes: ['XEL'], classes: 'dps-alert-status dps-alert-status--security', label: 'E-list' },\n { alertCodes: ['XELH'], classes: 'dps-alert-status dps-alert-status--security', label: 'E-list heightened' },\n { alertCodes: ['XER'], classes: 'dps-alert-status dps-alert-status--security', label: 'Escape risk' },\n {\n alertCodes: ['XRF'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Risk to females',\n },\n { alertCodes: ['XTACT'], classes: 'dps-alert-status dps-alert-status--security', label: 'TACT' },\n {\n alertCodes: ['XCO'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Corruptor',\n },\n {\n alertCodes: ['XCA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Chemical attacker',\n },\n {\n alertCodes: ['XCI'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Concerted indiscipline',\n },\n { alertCodes: ['XR'], classes: 'dps-alert-status dps-alert-status--security', label: 'Racist' },\n {\n alertCodes: ['RTP', 'RLG'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'Risk to LGBT',\n },\n {\n alertCodes: ['XHT'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Hostage taker',\n },\n {\n alertCodes: ['XCU'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Controlled unlock',\n },\n {\n alertCodes: ['XGANG'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Gang member',\n },\n { alertCodes: ['CSIP'], classes: 'dps-alert-status dps-alert-status--other', label: 'CSIP' },\n { alertCodes: ['F1'], classes: 'dps-alert-status dps-alert-status--ex-armed-forces', label: 'Veteran' },\n {\n alertCodes: ['LCE'],\n classes: 'dps-alert-status dps-alert-status--care-leaver',\n label: 'Care experienced',\n },\n {\n alertCodes: ['RNO121'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'No one-to-one',\n },\n { alertCodes: ['RCON'], classes: 'dps-alert-status dps-alert-status--risk', label: 'Conflict' },\n {\n alertCodes: ['RCDR'],\n classes: 'dps-alert-status dps-alert-status--quarantined',\n label: 'Quarantined',\n },\n {\n alertCodes: ['URCU'],\n classes: 'dps-alert-status dps-alert-status--reverse-cohorting-unit',\n label: 'Reverse Cohorting Unit',\n },\n {\n alertCodes: ['UPIU'],\n classes: 'dps-alert-status dps-alert-status--protective-isolation-unit',\n label: 'Protective Isolation Unit',\n },\n { alertCodes: ['USU'], classes: 'dps-alert-status dps-alert-status--shielding-unit', label: 'Shielding Unit' },\n {\n alertCodes: ['URS'],\n classes: 'dps-alert-status dps-alert-status--refusing-to-shield',\n label: 'Refusing to shield',\n },\n {\n alertCodes: ['RKS'],\n classes: 'dps-alert-status dps-alert-status--risk-to-known-adults',\n label: 'Risk to known adults',\n },\n { alertCodes: ['VIP'], classes: 'dps-alert-status dps-alert-status--isolated-prisoner', label: 'Isolated' },\n {\n alertCodes: ['PVN'],\n classes: 'dps-alert-status dps-alert-status--multicase dps-alert-status--visor',\n label: 'ViSOR',\n },\n {\n alertCodes: ['XCDO'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Involved in 2024 civil disorder',\n },\n {\n alertCodes: ['XVL'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Violent',\n },\n {\n alertCodes: ['DOCGM'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'OCG nominal (do not share)',\n },\n {\n alertCodes: ['RSS'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'Risk to staff',\n },\n {\n alertCodes: ['XCOP'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Potential corruptor',\n },\n].sort((a, b) => a.label.localeCompare(b.label))\n\nfunction isAlertsServiceAlert(alert: Alert): alert is AlertsServiceAlert {\n return typeof alert.alertCode !== 'string'\n}\n\nexport function getAlertFlagLabelsForAlerts(prisonerAlerts: Alert[]): AlertFlagLabel[] {\n return alertFlagLabels.reduce(\n (acc: AlertFlagLabel[], flag: { alertCodes: string[]; classes: string; label: string }) => {\n const alertIds = prisonerAlerts\n .filter(alert => {\n const alertsServiceAlert = isAlertsServiceAlert(alert)\n const alertIsActive = alertsServiceAlert ? alert.isActive : alert.active && !alert.expired\n const prisonerAlertCode = alertsServiceAlert ? alert.alertCode.code : alert.alertCode\n\n return alertIsActive && flag.alertCodes.includes(prisonerAlertCode)\n })\n .map(alert => (isAlertsServiceAlert(alert) ? (alert.alertUuid as string) : alert.alertCode))\n\n return alertIds.length ? [...acc, { ...flag, alertIds }] : acc\n },\n [] as AlertFlagLabel[],\n )\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/utils/utils.ts","../src/addressAutosuggest/osPlacesAddressService.ts","../src/addressAutosuggest/osPlacesApiClient.ts","../src/alertFlags/getAlertFlagLabelsForAlerts.ts"],"sourcesContent":["export const isBlank = (str: string | null | undefined): boolean => !str || /^\\s*$/.test(str)\n\nconst properCase = (word: string): string =>\n word.length >= 1 ? word[0].toUpperCase() + word.toLowerCase().slice(1) : word\n\n/**\n * Converts a name (first name, last name, middle name, etc.) to proper case equivalent, handling double-barreled names\n * correctly (i.e. each part in a double-barreled is converted to proper case).\n * @param name name to be converted.\n * @returns name converted to proper case.\n */\nexport const properCaseName = (name: string): string => (isBlank(name) ? '' : name.split('-').map(properCase).join('-'))\n\nexport const convertToTitleCase = (sentence: string): string =>\n isBlank(sentence)\n ? ''\n : sentence\n .split('(') // Handle parentheses\n .map(subsentence => subsentence.split(' ').map(properCaseName).join(' '))\n .join('(')\n","import Fuse, { IFuseOptions } from 'fuse.js'\nimport { OsAddress } from '../types/public/addressAutosuggest/osAddress'\nimport { convertToTitleCase, isBlank } from '../utils/utils'\nimport OsPlacesApiClient from './osPlacesApiClient'\nimport { ConnectDpsComponentLogger } from '../types/public/ConnectDpsComponentLogger'\nimport {\n OsPlacesDeliveryPointAddress,\n OsPlacesLandPropertyIdentifier,\n OsPlacesQueryResponse,\n} from '../types/public/addressAutosuggest/osPlaces'\n\nconst simplePostCodeRegex = /^[A-Z]{1,2}\\d[A-Z\\d]? ?\\d[A-Z]{2}$/i\nconst stringContainingPostCodeRegex = /^(.*?)([A-Z]{1,2}\\d[A-Z\\d]? ?)(\\d[A-Z]{2})(.*)$/i\n\n// Where we know OS Places API struggles with certain search terms, we will perform an additional replacement search:\nconst knownIssueSearchTermReplacements: Record<string, string> = {\n amazon: 'amazon.co.uk',\n}\n\nexport interface AddressesMatchingQueryConfig {\n osPlacesQueryParamOverrides: Record<string, unknown>\n fuzzyMatchOptionOverrides: IFuseOptions<OsAddress>\n}\n\nexport interface AddressesUprnQueryConfig {\n osPlacesQueryParamOverrides: Record<string, unknown>\n}\n\nexport default class OsPlacesAddressService {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly osPlacesApiClient: OsPlacesApiClient,\n ) {}\n\n public async getAddressesMatchingQuery(\n rawSearchQuery: string,\n config: AddressesMatchingQueryConfig = { osPlacesQueryParamOverrides: {}, fuzzyMatchOptionOverrides: {} },\n ): Promise<OsAddress[]> {\n const sanitisedSearchQuery = this.sanitiseString(rawSearchQuery) || ''\n const additionalSearchQuery = Object.keys(knownIssueSearchTermReplacements).reduce((accumulator, currentValue) => {\n const reg = new RegExp(currentValue, 'gi')\n return accumulator.replace(reg, knownIssueSearchTermReplacements[currentValue])\n }, sanitisedSearchQuery)\n\n const rawResults = await this.getOSPlacesResultsForQuery(sanitisedSearchQuery, config)\n const additionalResultsForKnownIssueSearchTerms =\n sanitisedSearchQuery === additionalSearchQuery\n ? []\n : await this.getOSPlacesResultsForQuery(additionalSearchQuery, config)\n\n return this.getOptimisedAddressesMatchingQuery(\n sanitisedSearchQuery,\n [...rawResults, ...additionalResultsForKnownIssueSearchTerms],\n config,\n )\n }\n\n public async getAddressByUprn(\n uprn: string,\n config: AddressesUprnQueryConfig = { osPlacesQueryParamOverrides: {} },\n ): Promise<OsAddress | null> {\n const response = await this.osPlacesApiClient.getAddressesByUprn(uprn, config.osPlacesQueryParamOverrides)\n const result = this.handleResponse(response)\n\n if (result.length === 0) return null\n if (result.length > 1) this.logger.info('Multiple results returned for UPRN')\n\n return result[result.length - 1]\n }\n\n public sanitiseUkPostcode(stringContainingPostcode: string) {\n const postCodeQuery = stringContainingPostCodeRegex.exec(stringContainingPostcode?.replace(/[^A-Z0-9 ]/gi, ''))\n if (!postCodeQuery) return stringContainingPostcode\n\n return `${postCodeQuery[1]}${postCodeQuery[2].toUpperCase().trim()} ${postCodeQuery[3].toUpperCase().trim()}${postCodeQuery[4]}`\n }\n\n private async getOSPlacesResultsForQuery(\n searchQuery: string,\n config: AddressesMatchingQueryConfig,\n ): Promise<OsAddress[]> {\n const response = await this.osPlacesApiClient.getAddressesByFreeTextQuery(\n this.sanitiseUkPostcode(searchQuery), // the API responds better when postcodes are properly formatted\n config.osPlacesQueryParamOverrides,\n )\n\n return this.handleResponse(response)\n }\n\n private getOptimisedAddressesMatchingQuery(\n searchQuery: string,\n rawResults: OsAddress[],\n config: AddressesMatchingQueryConfig,\n ): OsAddress[] {\n const bestMatchResults = new Fuse(rawResults, {\n shouldSort: true,\n threshold: 0.2,\n useExtendedSearch: true, // to allow search terms to be separated\n ignoreLocation: true, // to allow search terms to be separated\n keys: [{ name: 'addressString' }],\n ...config.fuzzyMatchOptionOverrides,\n })\n .search(searchQuery)\n .map(result => result.item)\n\n const queryIsAPostCode = simplePostCodeRegex.test(searchQuery)\n const isExactMatchToQuery = (addressString: string | undefined) =>\n searchQuery && this.sanitiseString(addressString)?.includes(searchQuery!)\n\n // By using `useExtendedSearch` and allowing search terms to be separated, we need to reprioritise exact match results:\n const preferExactMatchSort = (a: OsAddress, b: OsAddress) =>\n isExactMatchToQuery(a?.addressString) && !isExactMatchToQuery(b?.addressString) ? -1 : 1\n\n // This ensures house numbers are ordered numerically, not alphabetically when doing a search just on postcode:\n const buildingNumberSort = (a: OsAddress, b: OsAddress) =>\n a?.addressString && b?.addressString && queryIsAPostCode\n ? a.addressString.localeCompare(b.addressString, undefined, { numeric: true, sensitivity: 'base' })\n : 1\n\n // maximum number of properties in a postcode is 100\n return (bestMatchResults.length ? bestMatchResults : rawResults)\n .sort(queryIsAPostCode ? buildingNumberSort : preferExactMatchSort)\n .slice(0, 100)\n }\n\n private handleResponse(response: OsPlacesQueryResponse): OsAddress[] {\n if (response.header && response.header.totalresults === 0) return []\n\n return (\n response.results\n .map(\n result => (result.DPA && this.dpaToOsAddress(result.DPA)) || (result.LPI && this.lpiToOsAddress(result.LPI)),\n )\n .filter(result => !!result?.uprn)\n // remove duplicate results with the same UPRN:\n .filter((val, index, self) => self.findIndex(address => address?.uprn === val?.uprn) === index) as OsAddress[]\n )\n }\n\n private dpaToOsAddress(addressResult: OsPlacesDeliveryPointAddress): OsAddress | undefined {\n const {\n UPRN,\n ADDRESS,\n DEPENDENT_LOCALITY,\n ORGANISATION_NAME,\n SUB_BUILDING_NAME,\n BUILDING_NAME,\n BUILDING_NUMBER,\n THOROUGHFARE_NAME,\n POST_TOWN,\n POSTCODE,\n COUNTRY_CODE,\n LOCAL_CUSTODIAN_CODE_DESCRIPTION,\n POSTAL_ADDRESS_CODE,\n } = addressResult\n\n // Do not return address data that does not have a valid postal address:\n if (POSTAL_ADDRESS_CODE === 'N') return undefined\n\n const useOrganisationNameAsBuildingName =\n !isBlank(ORGANISATION_NAME) && isBlank(BUILDING_NAME) && isBlank(SUB_BUILDING_NAME)\n\n const buildingName = convertToTitleCase(useOrganisationNameAsBuildingName ? ORGANISATION_NAME : BUILDING_NAME)\n const subBuildingName = [useOrganisationNameAsBuildingName ? null : ORGANISATION_NAME, SUB_BUILDING_NAME]\n .filter(name => !isBlank(name))\n .map(name => convertToTitleCase(name!))\n .join(', ')\n\n return {\n addressString: this.formatAddressString(ADDRESS, BUILDING_NUMBER, THOROUGHFARE_NAME, POSTCODE),\n buildingNumber: BUILDING_NUMBER,\n buildingName,\n subBuildingName,\n thoroughfareName: convertToTitleCase(THOROUGHFARE_NAME),\n dependentLocality: convertToTitleCase(DEPENDENT_LOCALITY),\n postTown: convertToTitleCase(POST_TOWN),\n county: convertToTitleCase(LOCAL_CUSTODIAN_CODE_DESCRIPTION),\n postcode: POSTCODE,\n country: COUNTRY_CODE,\n uprn: UPRN,\n }\n }\n\n private lpiToOsAddress(addressResult: OsPlacesLandPropertyIdentifier): OsAddress | undefined {\n const {\n UPRN,\n ADDRESS,\n ORGANISATION,\n SAO_TEXT,\n PAO_START_NUMBER,\n PAO_TEXT,\n STREET_DESCRIPTION,\n TOWN_NAME,\n POSTCODE_LOCATOR,\n COUNTRY_CODE,\n POSTAL_ADDRESS_CODE,\n } = addressResult\n\n // Do not return address data that does not have a valid postal address:\n if (POSTAL_ADDRESS_CODE === 'N') return undefined\n\n const useOrganisationAsBuildingName = !isBlank(ORGANISATION) && isBlank(PAO_TEXT) && isBlank(SAO_TEXT)\n\n const buildingName = convertToTitleCase(useOrganisationAsBuildingName ? ORGANISATION : PAO_TEXT)\n const subBuildingName = [useOrganisationAsBuildingName ? null : ORGANISATION, SAO_TEXT]\n .filter(name => !isBlank(name))\n .map(name => convertToTitleCase(name!))\n .join(', ')\n\n return {\n addressString: this.formatAddressString(ADDRESS, PAO_START_NUMBER, STREET_DESCRIPTION, POSTCODE_LOCATOR),\n buildingNumber: PAO_START_NUMBER,\n buildingName,\n subBuildingName,\n thoroughfareName: convertToTitleCase(STREET_DESCRIPTION),\n postTown: convertToTitleCase(TOWN_NAME),\n postcode: POSTCODE_LOCATOR,\n country: COUNTRY_CODE,\n uprn: UPRN,\n }\n }\n\n private formatAddressString(\n addressString: string,\n buildingNumber: number,\n thoroughfareName: string,\n postcode: string,\n ) {\n const withoutPostcode = addressString\n .replace(`, ${postcode}`, '')\n .replace(`${buildingNumber}, ${thoroughfareName}`, `${buildingNumber} ${thoroughfareName}`)\n return `${convertToTitleCase(withoutPostcode)}, ${postcode}`\n }\n\n private sanitiseString(input: string | undefined) {\n return input\n ?.trim()\n ?.toLowerCase()\n ?.replace(/[^A-Z0-9-.' ]/gi, '')\n }\n}\n","import superagent from 'superagent'\nimport { ApiConfig } from '@ministryofjustice/hmpps-rest-client'\nimport { OsPlacesQueryResponse } from '../types/public/addressAutosuggest/osPlaces'\nimport { ConnectDpsComponentLogger } from '../types/public/ConnectDpsComponentLogger'\n\nexport default class OsPlacesApiClient {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly config: ApiConfig & { apiKey: string },\n ) {}\n\n async getAddressesByFreeTextQuery(\n freeTextQuery: string,\n queryParamOverrides: Record<string, unknown> = {},\n ): Promise<OsPlacesQueryResponse> {\n const queryParams: Record<string, unknown> = {\n query: freeTextQuery,\n key: this.config.apiKey,\n lr: 'EN',\n fq: ['LOGICAL_STATUS_CODE:1', 'LPI_LOGICAL_STATUS_CODE:1'], // only want active addresses\n dataset: 'LPI', // LPI chosen as default since it appears to perform better, especially for business addresses\n ...queryParamOverrides,\n }\n\n return this.get<Promise<OsPlacesQueryResponse>>('/find', queryParams)\n }\n\n async getAddressesByUprn(\n uprn: string,\n queryParamOverrides: Record<string, unknown> = {},\n ): Promise<OsPlacesQueryResponse> {\n const queryParams: Record<string, string> = {\n uprn,\n key: this.config.apiKey,\n dataset: 'DPA,LPI', // Both datasets available for the lookup by UPRN, preference for DPA because of the preferred data structure\n ...queryParamOverrides,\n }\n\n return this.get<Promise<OsPlacesQueryResponse>>('/uprn', queryParams)\n }\n\n private async get<T>(path: string, query: Record<string, unknown>): Promise<T> {\n const endpoint = `${this.config.url}${path}`\n try {\n const result = await superagent\n .get(endpoint)\n .retry(2, (err, _res) => {\n if (err) this.logger.info(`Retry handler found API error with ${err.code} ${err.message}`)\n return undefined // retry handler only for logging retries, not to influence retry logic\n })\n .query(query)\n .timeout(this.config.timeout)\n\n return result.body\n } catch (error) {\n const errorMessage = error && typeof error === 'object' && 'message' in error ? error.message : error\n throw Error(`Error calling OS Places API: ${errorMessage}`)\n }\n }\n}\n","import { Alert, AlertsServiceAlert } from '../types/public/alertFlags/Alert'\nimport { AlertFlagLabel } from '../types/public/alertFlags/AlertFlagLabel'\n\nexport const alertFlagLabels: AlertFlagLabel[] = [\n { alertCodes: ['HA'], classes: 'dps-alert-status dps-alert-status--self-harm', label: 'ACCT open' },\n {\n alertCodes: ['HA1'],\n classes: 'dps-alert-status dps-alert-status--self-harm',\n label: 'ACCT post closure',\n },\n {\n alertCodes: ['XSA', 'SA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Staff assaulter',\n },\n {\n alertCodes: ['XA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Arsonist',\n },\n {\n alertCodes: ['PEEP'],\n classes: 'dps-alert-status dps-alert-status--medical',\n label: 'PEEP',\n },\n {\n alertCodes: ['HID'],\n classes: 'dps-alert-status dps-alert-status--medical',\n label: 'Hidden disability',\n },\n { alertCodes: ['XEL'], classes: 'dps-alert-status dps-alert-status--security', label: 'E-list' },\n { alertCodes: ['XELH'], classes: 'dps-alert-status dps-alert-status--security', label: 'E-list heightened' },\n { alertCodes: ['XER'], classes: 'dps-alert-status dps-alert-status--security', label: 'Escape risk' },\n {\n alertCodes: ['XRF'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Risk to females',\n },\n { alertCodes: ['XTACT'], classes: 'dps-alert-status dps-alert-status--security', label: 'TACT' },\n {\n alertCodes: ['XCO'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Corruptor',\n },\n {\n alertCodes: ['XCA'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Chemical attacker',\n },\n {\n alertCodes: ['XCI'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Concerted indiscipline',\n },\n { alertCodes: ['XR'], classes: 'dps-alert-status dps-alert-status--security', label: 'Racist' },\n {\n alertCodes: ['RTP', 'RLG'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'Risk to LGBT',\n },\n {\n alertCodes: ['XHT'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Hostage taker',\n },\n {\n alertCodes: ['XCU'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Controlled unlock',\n },\n {\n alertCodes: ['XGANG'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Gang member',\n },\n { alertCodes: ['CSIP'], classes: 'dps-alert-status dps-alert-status--other', label: 'CSIP' },\n { alertCodes: ['F1'], classes: 'dps-alert-status dps-alert-status--ex-armed-forces', label: 'Veteran' },\n {\n alertCodes: ['LCE'],\n classes: 'dps-alert-status dps-alert-status--care-leaver',\n label: 'Care experienced',\n },\n {\n alertCodes: ['RNO121'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'No one-to-one',\n },\n { alertCodes: ['RCON'], classes: 'dps-alert-status dps-alert-status--risk', label: 'Conflict' },\n {\n alertCodes: ['RCDR'],\n classes: 'dps-alert-status dps-alert-status--quarantined',\n label: 'Quarantined',\n },\n {\n alertCodes: ['URCU'],\n classes: 'dps-alert-status dps-alert-status--reverse-cohorting-unit',\n label: 'Reverse Cohorting Unit',\n },\n {\n alertCodes: ['UPIU'],\n classes: 'dps-alert-status dps-alert-status--protective-isolation-unit',\n label: 'Protective Isolation Unit',\n },\n { alertCodes: ['USU'], classes: 'dps-alert-status dps-alert-status--shielding-unit', label: 'Shielding Unit' },\n {\n alertCodes: ['URS'],\n classes: 'dps-alert-status dps-alert-status--refusing-to-shield',\n label: 'Refusing to shield',\n },\n {\n alertCodes: ['RKS'],\n classes: 'dps-alert-status dps-alert-status--risk-to-known-adults',\n label: 'Risk to known adults',\n },\n { alertCodes: ['VIP'], classes: 'dps-alert-status dps-alert-status--isolated-prisoner', label: 'Isolated' },\n {\n alertCodes: ['PVN'],\n classes: 'dps-alert-status dps-alert-status--multicase dps-alert-status--visor',\n label: 'ViSOR',\n },\n {\n alertCodes: ['XCDO'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Involved in 2024 civil disorder',\n },\n {\n alertCodes: ['XVL'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Violent',\n },\n {\n alertCodes: ['DOCGM'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'OCG nominal (do not share)',\n },\n {\n alertCodes: ['RSS'],\n classes: 'dps-alert-status dps-alert-status--risk',\n label: 'Risk to staff',\n },\n {\n alertCodes: ['XCOP'],\n classes: 'dps-alert-status dps-alert-status--security',\n label: 'Potential corruptor',\n },\n].sort((a, b) => a.label.localeCompare(b.label))\n\nfunction isAlertsServiceAlert(alert: Alert): alert is AlertsServiceAlert {\n return typeof alert.alertCode !== 'string'\n}\n\nexport function getAlertFlagLabelsForAlerts(prisonerAlerts: Alert[]): AlertFlagLabel[] {\n return alertFlagLabels.reduce(\n (acc: AlertFlagLabel[], flag: { alertCodes: string[]; classes: string; label: string }) => {\n const alertIds = prisonerAlerts\n .filter(alert => {\n const alertsServiceAlert = isAlertsServiceAlert(alert)\n const alertIsActive = alertsServiceAlert ? alert.isActive : alert.active && !alert.expired\n const prisonerAlertCode = alertsServiceAlert ? alert.alertCode.code : alert.alertCode\n\n return alertIsActive && flag.alertCodes.includes(prisonerAlertCode)\n })\n .map(alert => (isAlertsServiceAlert(alert) ? (alert.alertUuid as string) : alert.alertCode))\n\n return alertIds.length ? [...acc, { ...flag, alertIds }] : acc\n },\n [] as AlertFlagLabel[],\n )\n}\n"],"names":[],"mappings":"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"}
|
package/dist/utils/utils.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const isBlank: (str: string | null | undefined) => boolean;
|
|
1
2
|
/**
|
|
2
3
|
* Converts a name (first name, last name, middle name, etc.) to proper case equivalent, handling double-barreled names
|
|
3
4
|
* correctly (i.e. each part in a double-barreled is converted to proper case).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ministryofjustice/hmpps-connect-dps-shared-items",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "A package to share Connect DPS functionality across applications",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.esm.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"dist/**/*"
|
|
11
11
|
],
|
|
12
12
|
"engines": {
|
|
13
|
-
"node": "20 || 22"
|
|
13
|
+
"node": "20 || 22 || 24"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "rollup -c rollup.config.ts --bundleConfigAsCjs && npm run copy:assets",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@rollup/plugin-multi-entry": "^6.0.1",
|
|
54
54
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
55
55
|
"@rollup/plugin-typescript": "^12.1.4",
|
|
56
|
-
"@tsconfig/
|
|
56
|
+
"@tsconfig/node24": "^24.0.1",
|
|
57
57
|
"@types/jest": "^29.5.14",
|
|
58
58
|
"@types/superagent": "^8.1.9",
|
|
59
59
|
"govuk-frontend": "^5.11.2",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"tslib": "^2.8.1"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@ministryofjustice/hmpps-rest-client": "^0.0
|
|
71
|
+
"@ministryofjustice/hmpps-rest-client": "^1.0.0",
|
|
72
72
|
"@types/bunyan": "^1.8.11",
|
|
73
73
|
"fuse.js": "^7.1.0",
|
|
74
74
|
"superagent": "^10.2.3"
|