@ministryofjustice/hmpps-connect-dps-components 2.2.0 → 3.0.0-beta.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.
Files changed (63) hide show
  1. package/dist/allocationService.d.ts +14 -2
  2. package/dist/allocationService.test.d.ts +1 -0
  3. package/dist/caseLoadService.d.ts +14 -2
  4. package/dist/caseLoadService.test.d.ts +1 -0
  5. package/dist/componentsService.d.ts +15 -1
  6. package/dist/componentsService.test.d.ts +1 -0
  7. package/dist/data/allocationsApi/allocationsApiClient.d.ts +6 -6
  8. package/dist/data/componentApi/componentApiClient.d.ts +6 -6
  9. package/dist/data/prisonApi/prisonApiClient.d.ts +6 -6
  10. package/dist/index.cjs +246 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.d.ts +152 -66
  13. package/dist/index.esm.js +242 -0
  14. package/dist/index.esm.js.map +1 -0
  15. package/dist/middleware/getFrontendComponents.d.ts +9 -0
  16. package/dist/types/ConnectDpsComponentLogger.d.ts +2 -0
  17. package/dist/types/public/middleware/index.d.ts +1 -0
  18. package/dist/types/public/services/index.d.ts +3 -0
  19. package/dist/utils/fallbacks.d.ts +1 -1
  20. package/dist/utils/updateCsp.d.ts +1 -1
  21. package/dist/utils/updateCsp.test.d.ts +1 -0
  22. package/package.json +34 -47
  23. package/dist/allocationService.js +0 -48
  24. package/dist/allocationService.js.map +0 -1
  25. package/dist/caseLoadService.js +0 -55
  26. package/dist/caseLoadService.js.map +0 -1
  27. package/dist/componentsService.js +0 -61
  28. package/dist/componentsService.js.map +0 -1
  29. package/dist/config.d.ts +0 -14
  30. package/dist/config.js +0 -28
  31. package/dist/config.js.map +0 -1
  32. package/dist/data/allocationsApi/allocationsApiClient.js +0 -23
  33. package/dist/data/allocationsApi/allocationsApiClient.js.map +0 -1
  34. package/dist/data/componentApi/componentApiClient.js +0 -23
  35. package/dist/data/componentApi/componentApiClient.js.map +0 -1
  36. package/dist/data/prisonApi/prisonApiClient.js +0 -24
  37. package/dist/data/prisonApi/prisonApiClient.js.map +0 -1
  38. package/dist/index.js +0 -72
  39. package/dist/index.js.map +0 -1
  40. package/dist/types/AllocationJobResponsibility.js +0 -3
  41. package/dist/types/AllocationJobResponsibility.js.map +0 -1
  42. package/dist/types/AvailableComponent.js +0 -3
  43. package/dist/types/AvailableComponent.js.map +0 -1
  44. package/dist/types/CaseLoad.js +0 -3
  45. package/dist/types/CaseLoad.js.map +0 -1
  46. package/dist/types/CaseLoadOptions.js +0 -3
  47. package/dist/types/CaseLoadOptions.js.map +0 -1
  48. package/dist/types/Component.js +0 -3
  49. package/dist/types/Component.js.map +0 -1
  50. package/dist/types/HeaderFooterSharedData.js +0 -3
  51. package/dist/types/HeaderFooterSharedData.js.map +0 -1
  52. package/dist/types/HmppsUser.js +0 -3
  53. package/dist/types/HmppsUser.js.map +0 -1
  54. package/dist/types/RequestOptions.js +0 -3
  55. package/dist/types/RequestOptions.js.map +0 -1
  56. package/dist/types/Service.js +0 -3
  57. package/dist/types/Service.js.map +0 -1
  58. package/dist/types/TimeoutOptions.js +0 -3
  59. package/dist/types/TimeoutOptions.js.map +0 -1
  60. package/dist/utils/fallbacks.js +0 -36
  61. package/dist/utils/fallbacks.js.map +0 -1
  62. package/dist/utils/updateCsp.js +0 -25
  63. package/dist/utils/updateCsp.js.map +0 -1
@@ -1,3 +1,15 @@
1
1
  import { type RequestHandler } from 'express';
2
- import CaseLoadOptions from './types/CaseLoadOptions';
3
- export default function retrieveAllocationJobResponsibilities(options?: CaseLoadOptions): RequestHandler;
2
+ import { ApiConfig, AuthenticationClient } from '@ministryofjustice/hmpps-rest-client';
3
+ import AllocationsApiClient from './data/allocationsApi/allocationsApiClient';
4
+ import { ConnectDpsComponentLogger } from './types/ConnectDpsComponentLogger';
5
+ export default class AllocationService {
6
+ private readonly logger;
7
+ private readonly allocationsApiClient;
8
+ constructor(logger: ConnectDpsComponentLogger, allocationsApiClient: AllocationsApiClient);
9
+ static create({ logger, allocationsApiConfig, authenticationClient, }: {
10
+ logger?: ConnectDpsComponentLogger;
11
+ allocationsApiConfig: ApiConfig;
12
+ authenticationClient: AuthenticationClient;
13
+ }): AllocationService;
14
+ retrieveAllocationJobResponsibilities(): RequestHandler;
15
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,15 @@
1
1
  import { type RequestHandler } from 'express';
2
- import CaseLoadOptions from './types/CaseLoadOptions';
3
- export default function retrieveCaseLoadData(caseLoadOptions?: CaseLoadOptions): RequestHandler;
2
+ import { ApiConfig, AuthenticationClient } from '@ministryofjustice/hmpps-rest-client';
3
+ import PrisonApiClient from './data/prisonApi/prisonApiClient';
4
+ import { ConnectDpsComponentLogger } from './types/ConnectDpsComponentLogger';
5
+ export default class CaseLoadService {
6
+ private readonly logger;
7
+ private readonly prisonApiClient;
8
+ constructor(logger: ConnectDpsComponentLogger, prisonApiClient: PrisonApiClient);
9
+ static create({ logger, prisonApiConfig, authenticationClient, }: {
10
+ logger?: ConnectDpsComponentLogger;
11
+ prisonApiConfig: ApiConfig;
12
+ authenticationClient: AuthenticationClient;
13
+ }): CaseLoadService;
14
+ retrieveCaseLoadData(): RequestHandler;
15
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,17 @@
1
1
  import { type RequestHandler } from 'express';
2
+ import { ApiConfig, AuthenticationClient } from '@ministryofjustice/hmpps-rest-client';
3
+ import ComponentApiClient from './data/componentApi/componentApiClient';
2
4
  import RequestOptions from './types/RequestOptions';
3
- export default function getFrontendComponents(requestOptions?: RequestOptions): RequestHandler;
5
+ import { ConnectDpsComponentLogger } from './types/ConnectDpsComponentLogger';
6
+ export default class ComponentsService {
7
+ private readonly logger;
8
+ private readonly componentApiConfig;
9
+ private readonly componentApiClient;
10
+ constructor(logger: ConnectDpsComponentLogger, componentApiConfig: ApiConfig, componentApiClient: ComponentApiClient);
11
+ static create({ logger, componentApiConfig, authenticationClient, }: {
12
+ logger?: ConnectDpsComponentLogger;
13
+ componentApiConfig: ApiConfig;
14
+ authenticationClient: AuthenticationClient;
15
+ }): ComponentsService;
16
+ getFrontendComponents(requestOptions: RequestOptions): RequestHandler;
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,10 +1,10 @@
1
- import type bunyan from 'bunyan';
2
- import TimeoutOptions from '../../types/TimeoutOptions';
1
+ import { ApiConfig, AuthenticationClient, RestClient } from '@ministryofjustice/hmpps-rest-client';
3
2
  import { PrisonUser } from '../../types/HmppsUser';
4
3
  import { AllocationJobResponsibility } from '../../types/AllocationJobResponsibility';
5
- declare const _default: {
6
- getStaffAllocationPolicies(user: PrisonUser, timeoutOptions: TimeoutOptions, log: bunyan | typeof console): Promise<{
4
+ import { ConnectDpsComponentLogger } from '../../types/ConnectDpsComponentLogger';
5
+ export default class AllocationsApiClient extends RestClient {
6
+ constructor(logger: ConnectDpsComponentLogger, config: ApiConfig, authenticationClient: AuthenticationClient);
7
+ getStaffAllocationPolicies(user: PrisonUser): Promise<{
7
8
  policies: AllocationJobResponsibility[];
8
9
  }>;
9
- };
10
- export default _default;
10
+ }
@@ -1,12 +1,12 @@
1
- import type bunyan from 'bunyan';
1
+ import { ApiConfig, AuthenticationClient, RestClient } from '@ministryofjustice/hmpps-rest-client';
2
2
  import AvailableComponent from '../../types/AvailableComponent';
3
3
  import Component from '../../types/Component';
4
- import TimeoutOptions from '../../types/TimeoutOptions';
4
+ import { ConnectDpsComponentLogger } from '../../types/ConnectDpsComponentLogger';
5
5
  import { ComponentsSharedData } from '../../types/HeaderFooterSharedData';
6
6
  export type ComponentsApiResponse<T extends AvailableComponent[]> = Record<T[number], Component> & {
7
7
  meta: ComponentsSharedData[T[number]];
8
8
  };
9
- declare const _default: {
10
- getComponents<T extends AvailableComponent[]>(userToken: string, timeoutOptions: TimeoutOptions, log: bunyan | typeof console): Promise<ComponentsApiResponse<T>>;
11
- };
12
- export default _default;
9
+ export default class ComponentApiClient extends RestClient {
10
+ constructor(logger: ConnectDpsComponentLogger, config: ApiConfig, authenticationClient: AuthenticationClient);
11
+ getComponents<T extends AvailableComponent[]>(userToken: string): Promise<ComponentsApiResponse<T>>;
12
+ }
@@ -1,7 +1,7 @@
1
- import type bunyan from 'bunyan';
2
- import TimeoutOptions from '../../types/TimeoutOptions';
1
+ import { ApiConfig, AuthenticationClient, RestClient } from '@ministryofjustice/hmpps-rest-client';
3
2
  import CaseLoad from '../../types/CaseLoad';
4
- declare const _default: {
5
- getUserCaseLoads(userToken: string, timeoutOptions: TimeoutOptions, log: bunyan | typeof console): Promise<CaseLoad[]>;
6
- };
7
- export default _default;
3
+ import { ConnectDpsComponentLogger } from '../../types/ConnectDpsComponentLogger';
4
+ export default class PrisonApiClient extends RestClient {
5
+ constructor(logger: ConnectDpsComponentLogger, config: ApiConfig, authenticationClient: AuthenticationClient);
6
+ getUserCaseLoads(userToken: string): Promise<CaseLoad[]>;
7
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,246 @@
1
+ 'use strict';
2
+
3
+ var hmppsRestClient = require('@ministryofjustice/hmpps-rest-client');
4
+ var nunjucks = require('nunjucks');
5
+
6
+ class ComponentApiClient extends hmppsRestClient.RestClient {
7
+ constructor(logger, config, authenticationClient) {
8
+ super('Component API Client', config, logger, authenticationClient);
9
+ }
10
+ async getComponents(userToken) {
11
+ return this.get({
12
+ path: `/components`,
13
+ query: 'component=header&component=footer',
14
+ headers: { 'x-user-token': userToken },
15
+ }, hmppsRestClient.asSystem());
16
+ }
17
+ }
18
+
19
+ function getFallbackHeader(user, requestOptions) {
20
+ const { dpsUrl, environmentName, authUrl, supportUrl } = requestOptions;
21
+ return nunjucks.render('dpsComponents/header-bar.njk', {
22
+ isPrisonUser: !user || user.authSource === 'nomis',
23
+ user,
24
+ dpsUrl,
25
+ environmentName,
26
+ authUrl,
27
+ supportUrl,
28
+ name: initialiseName(user?.displayName),
29
+ });
30
+ }
31
+ function getFallbackFooter(user, requestOptions) {
32
+ const { supportUrl, authUrl } = requestOptions;
33
+ return nunjucks.render('dpsComponents/footer.njk', {
34
+ isPrisonUser: !user || user.authSource === 'nomis',
35
+ supportUrl,
36
+ authUrl,
37
+ });
38
+ }
39
+ function initialiseName(fullName) {
40
+ if (!fullName)
41
+ return null;
42
+ const array = fullName.split(' ');
43
+ return `${array[0][0]}. ${array.reverse()[0]}`;
44
+ }
45
+
46
+ function updateCsp(feComponentsUrl, res) {
47
+ const csp = res.getHeaders()['content-security-policy'];
48
+ const allDirectives = csp?.split(';') ?? [];
49
+ const directivesToUpdate = ['script-src', 'style-src', 'img-src', 'font-src'];
50
+ const updatedCspDirectives = allDirectives.map(directive => {
51
+ // if directive is not in cspToUpdate or includes fe components url already return as is
52
+ if (directive.includes(feComponentsUrl) || !directivesToUpdate.some(p => directive.includes(`${p} `)))
53
+ return directive;
54
+ // if directive is in cspToUpdate and does not have fe components url, add in
55
+ return `${directive} ${feComponentsUrl}`;
56
+ });
57
+ const requiredAndNotPresent = directivesToUpdate
58
+ .filter(p => !updatedCspDirectives.find(directive => directive.includes(`${p} `)))
59
+ .map(p => `${p} 'self' ${feComponentsUrl}`);
60
+ res.set('content-security-policy', [...updatedCspDirectives, ...requiredAndNotPresent].join(';'));
61
+ }
62
+
63
+ const defaultOptions = {
64
+ logger: console,
65
+ timeoutOptions: { response: 2500, deadline: 2500 },
66
+ includeSharedData: false,
67
+ useFallbacksByDefault: false,
68
+ };
69
+ class ComponentsService {
70
+ logger;
71
+ componentApiConfig;
72
+ componentApiClient;
73
+ constructor(logger, componentApiConfig, componentApiClient) {
74
+ this.logger = logger;
75
+ this.componentApiConfig = componentApiConfig;
76
+ this.componentApiClient = componentApiClient;
77
+ }
78
+ static create({ logger = console, componentApiConfig, authenticationClient, }) {
79
+ return new ComponentsService(logger, componentApiConfig, new ComponentApiClient(logger, componentApiConfig, authenticationClient));
80
+ }
81
+ getFrontendComponents(requestOptions) {
82
+ const requestOptionsWithDefaults = {
83
+ ...defaultOptions,
84
+ ...requestOptions,
85
+ };
86
+ const { includeSharedData, useFallbacksByDefault } = {
87
+ ...defaultOptions,
88
+ ...requestOptions,
89
+ };
90
+ return async (_req, res, next) => {
91
+ const useFallbacks = (user) => {
92
+ res.locals.feComponents = {
93
+ header: getFallbackHeader(user, requestOptionsWithDefaults),
94
+ footer: getFallbackFooter(user, requestOptionsWithDefaults),
95
+ cssIncludes: [],
96
+ jsIncludes: [],
97
+ };
98
+ };
99
+ if (!res.locals.user) {
100
+ this.logger.info('Using fallback frontend components when no user in context');
101
+ useFallbacks(null);
102
+ return next();
103
+ }
104
+ if (useFallbacksByDefault) {
105
+ this.logger.info('Using fallback frontend components by default');
106
+ useFallbacks(res.locals.user);
107
+ return next();
108
+ }
109
+ try {
110
+ const { header, footer, meta } = await this.componentApiClient.getComponents(res.locals.user.token);
111
+ res.locals.feComponents = {
112
+ header: header.html,
113
+ footer: footer.html,
114
+ cssIncludes: [...header.css, ...footer.css],
115
+ jsIncludes: [...header.javascript, ...footer.javascript],
116
+ };
117
+ if (includeSharedData) {
118
+ res.locals.feComponents.sharedData = meta;
119
+ }
120
+ updateCsp(this.componentApiConfig.url, res);
121
+ return next();
122
+ }
123
+ catch (_error) {
124
+ this.logger.error('Failed to retrieve front end components, using fallbacks');
125
+ useFallbacks(res.locals.user);
126
+ return next();
127
+ }
128
+ };
129
+ }
130
+ }
131
+
132
+ class PrisonApiClient extends hmppsRestClient.RestClient {
133
+ constructor(logger, config, authenticationClient) {
134
+ super('Prison API Client', config, logger, authenticationClient);
135
+ }
136
+ async getUserCaseLoads(userToken) {
137
+ return this.get({
138
+ path: '/api/users/me/caseloads',
139
+ query: { allCaseloads: true },
140
+ }, hmppsRestClient.asUser(userToken));
141
+ }
142
+ }
143
+
144
+ class CaseLoadService {
145
+ logger;
146
+ prisonApiClient;
147
+ constructor(logger, prisonApiClient) {
148
+ this.logger = logger;
149
+ this.prisonApiClient = prisonApiClient;
150
+ }
151
+ static create({ logger = console, prisonApiConfig, authenticationClient, }) {
152
+ return new CaseLoadService(logger, new PrisonApiClient(logger, prisonApiConfig, authenticationClient));
153
+ }
154
+ retrieveCaseLoadData() {
155
+ return async (req, res, next) => {
156
+ if (!req.session)
157
+ throw new Error('User session required in order to cache case loads');
158
+ if (res.locals.user && res.locals.user.token && res.locals.user.authSource === 'nomis') {
159
+ try {
160
+ // Update cache with values from res.feComponents.sharedData if present
161
+ if (res.locals.feComponents && res.locals.feComponents.sharedData) {
162
+ req.session.caseLoads = res.locals.feComponents.sharedData.caseLoads;
163
+ req.session.activeCaseLoad = res.locals.feComponents.sharedData.activeCaseLoad;
164
+ req.session.activeCaseLoadId = res.locals.feComponents.sharedData.activeCaseLoad?.caseLoadId;
165
+ }
166
+ // If cache is empty, fetch data from Prison API
167
+ if (!req.session.caseLoads) {
168
+ this.logger.info(`Falling back to Prison API to retrieve case loads for: ${res.locals.user.username}`);
169
+ const userCaseLoads = await this.prisonApiClient.getUserCaseLoads(res.locals.user.token);
170
+ const caseLoads = userCaseLoads.filter(caseload => caseload.type !== 'APP');
171
+ const activeCaseLoad = caseLoads.find((caseLoad) => caseLoad.currentlyActive);
172
+ req.session.caseLoads = caseLoads;
173
+ req.session.activeCaseLoad = activeCaseLoad;
174
+ req.session.activeCaseLoadId = activeCaseLoad?.caseLoadId;
175
+ }
176
+ // Populate res.locals.user with values from cache
177
+ res.locals.user.caseLoads = req.session.caseLoads;
178
+ res.locals.user.activeCaseLoad = req.session.activeCaseLoad;
179
+ res.locals.user.activeCaseLoadId = req.session.activeCaseLoadId;
180
+ }
181
+ catch (error) {
182
+ this.logger.error(error, `Failed to retrieve case loads for: ${res.locals.user.username}`);
183
+ return next(error);
184
+ }
185
+ }
186
+ return next();
187
+ };
188
+ }
189
+ }
190
+
191
+ class AllocationsApiClient extends hmppsRestClient.RestClient {
192
+ constructor(logger, config, authenticationClient) {
193
+ super('Allocations API Client', config, logger, authenticationClient);
194
+ }
195
+ async getStaffAllocationPolicies(user) {
196
+ return this.get({
197
+ path: `/prisons/${user.activeCaseLoadId}/staff/${user.userId}/job-classifications`,
198
+ }, hmppsRestClient.asSystem());
199
+ }
200
+ }
201
+
202
+ class AllocationService {
203
+ logger;
204
+ allocationsApiClient;
205
+ constructor(logger, allocationsApiClient) {
206
+ this.logger = logger;
207
+ this.allocationsApiClient = allocationsApiClient;
208
+ }
209
+ static create({ logger = console, allocationsApiConfig, authenticationClient, }) {
210
+ return new AllocationService(logger, new AllocationsApiClient(logger, allocationsApiConfig, authenticationClient));
211
+ }
212
+ retrieveAllocationJobResponsibilities() {
213
+ return async (req, res, next) => {
214
+ if (!req.session)
215
+ throw new Error('User session required in order to cache allocation job responsibilities');
216
+ if (!res.locals.user.token)
217
+ throw new Error('Caseload details needs to be populated before retrieving allocation job responsibilities. Please run retrieveCaseLoadData before retrieveAllocationJobResponsibilities.');
218
+ if (res.locals.user && res.locals.user.authSource === 'nomis') {
219
+ try {
220
+ // Update cache with values from res.feComponents.sharedData if present
221
+ if (res.locals.feComponents && res.locals.feComponents.sharedData) {
222
+ req.session.allocationJobResponsibilities = res.locals.feComponents.sharedData.allocationJobResponsibilities;
223
+ }
224
+ // If cache is empty, fetch data from Prison API
225
+ if (!req.session.allocationJobResponsibilities) {
226
+ this.logger.info(`Falling back to Allocations API to retrieve job responsibilities for: ${res.locals.user.username}`);
227
+ const allocationPolicies = await this.allocationsApiClient.getStaffAllocationPolicies(res.locals.user);
228
+ req.session.allocationJobResponsibilities = allocationPolicies.policies;
229
+ }
230
+ // Populate res.locals.user with values from cache
231
+ res.locals.user.allocationJobResponsibilities = req.session.allocationJobResponsibilities;
232
+ }
233
+ catch (error) {
234
+ this.logger.error(error, `Failed to retrieve allocation job responsibilities for: ${res.locals.user.username}`);
235
+ return next(error);
236
+ }
237
+ }
238
+ return next();
239
+ };
240
+ }
241
+ }
242
+
243
+ exports.AllocationService = AllocationService;
244
+ exports.CaseLoadService = CaseLoadService;
245
+ exports.ComponentsService = ComponentsService;
246
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/data/componentApi/componentApiClient.ts","../src/utils/fallbacks.ts","../src/utils/updateCsp.ts","../src/componentsService.ts","../src/data/prisonApi/prisonApiClient.ts","../src/caseLoadService.ts","../src/data/allocationsApi/allocationsApiClient.ts","../src/allocationService.ts"],"sourcesContent":["import { ApiConfig, asSystem, AuthenticationClient, RestClient } from '@ministryofjustice/hmpps-rest-client'\nimport AvailableComponent from '../../types/AvailableComponent'\nimport Component from '../../types/Component'\nimport { ConnectDpsComponentLogger } from '../../types/ConnectDpsComponentLogger'\nimport { ComponentsSharedData } from '../../types/HeaderFooterSharedData'\n\nexport type ComponentsApiResponse<T extends AvailableComponent[]> = Record<T[number], Component> & {\n meta: ComponentsSharedData[T[number]] // TODO: rename 'meta' in the API response\n}\n\nexport default class ComponentApiClient extends RestClient {\n constructor(logger: ConnectDpsComponentLogger, config: ApiConfig, authenticationClient: AuthenticationClient) {\n super('Component API Client', config, logger, authenticationClient)\n }\n\n async getComponents<T extends AvailableComponent[]>(userToken: string): Promise<ComponentsApiResponse<T>> {\n return this.get<ComponentsApiResponse<T>>(\n {\n path: `/components`,\n query: 'component=header&component=footer',\n headers: { 'x-user-token': userToken },\n },\n asSystem(),\n )\n }\n}\n","import nunjucks from 'nunjucks'\nimport RequestOptions from '../types/RequestOptions'\nimport { HmppsUser } from '../types/HmppsUser'\n\nexport function getFallbackHeader(user: HmppsUser | null, requestOptions: RequestOptions): string {\n const { dpsUrl, environmentName, authUrl, supportUrl } = requestOptions\n\n return nunjucks.render('dpsComponents/header-bar.njk', {\n isPrisonUser: !user || user.authSource === 'nomis',\n user,\n dpsUrl,\n environmentName,\n authUrl,\n supportUrl,\n name: initialiseName(user?.displayName),\n })\n}\n\nexport function getFallbackFooter(user: HmppsUser | null, requestOptions: RequestOptions): string {\n const { supportUrl, authUrl } = requestOptions\n return nunjucks.render('dpsComponents/footer.njk', {\n isPrisonUser: !user || user.authSource === 'nomis',\n supportUrl,\n authUrl,\n })\n}\n\nfunction initialiseName(fullName?: string): string | null {\n if (!fullName) return null\n\n const array = fullName.split(' ')\n return `${array[0][0]}. ${array.reverse()[0]}`\n}\n","import { type Response } from 'express'\n\nexport default function updateCsp(feComponentsUrl: string, res: Response) {\n const csp = res.getHeaders()['content-security-policy']\n const allDirectives = csp?.split(';') ?? []\n const directivesToUpdate = ['script-src', 'style-src', 'img-src', 'font-src']\n\n const updatedCspDirectives = allDirectives.map(directive => {\n // if directive is not in cspToUpdate or includes fe components url already return as is\n if (directive.includes(feComponentsUrl as string) || !directivesToUpdate.some(p => directive.includes(`${p} `)))\n return directive\n\n // if directive is in cspToUpdate and does not have fe components url, add in\n return `${directive} ${feComponentsUrl}`\n })\n\n const requiredAndNotPresent = directivesToUpdate\n .filter(p => !updatedCspDirectives.find(directive => directive.includes(`${p} `)))\n .map(p => `${p} 'self' ${feComponentsUrl}`)\n\n res.set('content-security-policy', [...updatedCspDirectives, ...requiredAndNotPresent].join(';'))\n}\n","import { type NextFunction, type Request, type Response, type RequestHandler } from 'express'\nimport { ApiConfig, AuthenticationClient } from '@ministryofjustice/hmpps-rest-client'\nimport ComponentApiClient from './data/componentApi/componentApiClient'\nimport { getFallbackFooter, getFallbackHeader } from './utils/fallbacks'\nimport RequestOptions from './types/RequestOptions'\nimport updateCsp from './utils/updateCsp'\nimport { HmppsUser } from './types/HmppsUser'\nimport { ConnectDpsComponentLogger } from './types/ConnectDpsComponentLogger'\n\nconst defaultOptions: Partial<RequestOptions> = {\n logger: console,\n timeoutOptions: { response: 2500, deadline: 2500 },\n includeSharedData: false,\n useFallbacksByDefault: false,\n}\n\nexport default class ComponentsService {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly componentApiConfig: ApiConfig,\n private readonly componentApiClient: ComponentApiClient,\n ) {}\n\n static create({\n logger = console,\n componentApiConfig,\n authenticationClient,\n }: {\n logger?: ConnectDpsComponentLogger\n componentApiConfig: ApiConfig\n authenticationClient: AuthenticationClient\n }) {\n return new ComponentsService(\n logger,\n componentApiConfig,\n new ComponentApiClient(logger, componentApiConfig, authenticationClient),\n )\n }\n\n getFrontendComponents(requestOptions: RequestOptions): RequestHandler {\n const requestOptionsWithDefaults = {\n ...defaultOptions,\n ...requestOptions,\n }\n const { includeSharedData, useFallbacksByDefault } = {\n ...defaultOptions,\n ...requestOptions,\n }\n\n return async (_req: Request, res: Response, next: NextFunction) => {\n const useFallbacks = (user: HmppsUser | null) => {\n res.locals.feComponents = {\n header: getFallbackHeader(user, requestOptionsWithDefaults),\n footer: getFallbackFooter(user, requestOptionsWithDefaults),\n cssIncludes: [],\n jsIncludes: [],\n }\n }\n\n if (!res.locals.user) {\n this.logger.info('Using fallback frontend components when no user in context')\n useFallbacks(null)\n return next()\n }\n\n if (useFallbacksByDefault) {\n this.logger.info('Using fallback frontend components by default')\n useFallbacks(res.locals.user)\n return next()\n }\n\n try {\n const { header, footer, meta } = await this.componentApiClient.getComponents(res.locals.user.token as string)\n\n res.locals.feComponents = {\n header: header.html,\n footer: footer.html,\n cssIncludes: [...header.css, ...footer.css],\n jsIncludes: [...header.javascript, ...footer.javascript],\n }\n\n if (includeSharedData) {\n res.locals.feComponents.sharedData = meta\n }\n\n updateCsp(this.componentApiConfig.url, res)\n\n return next()\n } catch (_error) {\n this.logger.error('Failed to retrieve front end components, using fallbacks')\n useFallbacks(res.locals.user)\n return next()\n }\n }\n }\n}\n","import { ApiConfig, asUser, AuthenticationClient, RestClient } from '@ministryofjustice/hmpps-rest-client'\nimport CaseLoad from '../../types/CaseLoad'\nimport { ConnectDpsComponentLogger } from '../../types/ConnectDpsComponentLogger'\n\nexport default class PrisonApiClient extends RestClient {\n constructor(logger: ConnectDpsComponentLogger, config: ApiConfig, authenticationClient: AuthenticationClient) {\n super('Prison API Client', config, logger, authenticationClient)\n }\n\n async getUserCaseLoads(userToken: string): Promise<CaseLoad[]> {\n return this.get<CaseLoad[]>(\n {\n path: '/api/users/me/caseloads',\n query: { allCaseloads: true },\n },\n asUser(userToken),\n )\n }\n}\n","import { type RequestHandler } from 'express'\nimport { ApiConfig, AuthenticationClient } from '@ministryofjustice/hmpps-rest-client'\nimport CaseLoad from './types/CaseLoad'\nimport PrisonApiClient from './data/prisonApi/prisonApiClient'\nimport { ConnectDpsComponentLogger } from './types/ConnectDpsComponentLogger'\n\nexport default class CaseLoadService {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly prisonApiClient: PrisonApiClient,\n ) {}\n\n static create({\n logger = console,\n prisonApiConfig,\n authenticationClient,\n }: {\n logger?: ConnectDpsComponentLogger\n prisonApiConfig: ApiConfig\n authenticationClient: AuthenticationClient\n }) {\n return new CaseLoadService(logger, new PrisonApiClient(logger, prisonApiConfig, authenticationClient))\n }\n\n retrieveCaseLoadData(): RequestHandler {\n return async (req, res, next) => {\n if (!req.session) throw new Error('User session required in order to cache case loads')\n\n if (res.locals.user && res.locals.user.token && res.locals.user.authSource === 'nomis') {\n try {\n // Update cache with values from res.feComponents.sharedData if present\n if (res.locals.feComponents && res.locals.feComponents.sharedData) {\n req.session.caseLoads = res.locals.feComponents.sharedData.caseLoads\n req.session.activeCaseLoad = res.locals.feComponents.sharedData.activeCaseLoad\n req.session.activeCaseLoadId = res.locals.feComponents.sharedData.activeCaseLoad?.caseLoadId\n }\n\n // If cache is empty, fetch data from Prison API\n if (!req.session.caseLoads) {\n this.logger.info(`Falling back to Prison API to retrieve case loads for: ${res.locals.user.username}`)\n const userCaseLoads = await this.prisonApiClient.getUserCaseLoads(res.locals.user.token)\n const caseLoads = userCaseLoads.filter(caseload => caseload.type !== 'APP')\n const activeCaseLoad = caseLoads.find((caseLoad: CaseLoad) => caseLoad.currentlyActive)\n\n req.session.caseLoads = caseLoads\n req.session.activeCaseLoad = activeCaseLoad\n req.session.activeCaseLoadId = activeCaseLoad?.caseLoadId\n }\n\n // Populate res.locals.user with values from cache\n res.locals.user.caseLoads = req.session.caseLoads\n res.locals.user.activeCaseLoad = req.session.activeCaseLoad\n res.locals.user.activeCaseLoadId = req.session.activeCaseLoadId\n } catch (error) {\n this.logger.error(error, `Failed to retrieve case loads for: ${res.locals.user.username}`)\n return next(error)\n }\n }\n\n return next()\n }\n }\n}\n","import { ApiConfig, asSystem, AuthenticationClient, RestClient } from '@ministryofjustice/hmpps-rest-client'\nimport { PrisonUser } from '../../types/HmppsUser'\nimport { AllocationJobResponsibility } from '../../types/AllocationJobResponsibility'\nimport { ConnectDpsComponentLogger } from '../../types/ConnectDpsComponentLogger'\n\nexport default class AllocationsApiClient extends RestClient {\n constructor(logger: ConnectDpsComponentLogger, config: ApiConfig, authenticationClient: AuthenticationClient) {\n super('Allocations API Client', config, logger, authenticationClient)\n }\n\n async getStaffAllocationPolicies(user: PrisonUser): Promise<{ policies: AllocationJobResponsibility[] }> {\n return this.get<{ policies: AllocationJobResponsibility[] }>(\n {\n path: `/prisons/${user.activeCaseLoadId}/staff/${user.userId}/job-classifications`,\n },\n asSystem(),\n )\n }\n}\n","import { type RequestHandler } from 'express'\nimport { ApiConfig, AuthenticationClient } from '@ministryofjustice/hmpps-rest-client'\nimport AllocationsApiClient from './data/allocationsApi/allocationsApiClient'\nimport { ConnectDpsComponentLogger } from './types/ConnectDpsComponentLogger'\n\nexport default class AllocationService {\n constructor(\n private readonly logger: ConnectDpsComponentLogger,\n private readonly allocationsApiClient: AllocationsApiClient,\n ) {}\n\n static create({\n logger = console,\n allocationsApiConfig,\n authenticationClient,\n }: {\n logger?: ConnectDpsComponentLogger\n allocationsApiConfig: ApiConfig\n authenticationClient: AuthenticationClient\n }) {\n return new AllocationService(logger, new AllocationsApiClient(logger, allocationsApiConfig, authenticationClient))\n }\n\n public retrieveAllocationJobResponsibilities(): RequestHandler {\n return async (req, res, next) => {\n if (!req.session) throw new Error('User session required in order to cache allocation job responsibilities')\n if (!res.locals.user.token)\n throw new Error(\n 'Caseload details needs to be populated before retrieving allocation job responsibilities. Please run retrieveCaseLoadData before retrieveAllocationJobResponsibilities.',\n )\n\n if (res.locals.user && res.locals.user.authSource === 'nomis') {\n try {\n // Update cache with values from res.feComponents.sharedData if present\n if (res.locals.feComponents && res.locals.feComponents.sharedData) {\n req.session.allocationJobResponsibilities = res.locals.feComponents.sharedData.allocationJobResponsibilities\n }\n\n // If cache is empty, fetch data from Prison API\n if (!req.session.allocationJobResponsibilities) {\n this.logger.info(\n `Falling back to Allocations API to retrieve job responsibilities for: ${res.locals.user.username}`,\n )\n const allocationPolicies = await this.allocationsApiClient.getStaffAllocationPolicies(res.locals.user)\n req.session.allocationJobResponsibilities = allocationPolicies.policies\n }\n\n // Populate res.locals.user with values from cache\n res.locals.user.allocationJobResponsibilities = req.session.allocationJobResponsibilities\n } catch (error) {\n this.logger.error(\n error,\n `Failed to retrieve allocation job responsibilities for: ${res.locals.user.username}`,\n )\n return next(error)\n }\n }\n\n return next()\n }\n }\n}\n"],"names":["RestClient","asSystem","asUser"],"mappings":";;;;;AAUc,MAAO,kBAAmB,SAAQA,0BAAU,CAAA;AACxD,IAAA,WAAA,CAAY,MAAiC,EAAE,MAAiB,EAAE,oBAA0C,EAAA;QAC1G,KAAK,CAAC,sBAAsB,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,CAAC;IACrE;IAEA,MAAM,aAAa,CAAiC,SAAiB,EAAA;QACnE,OAAO,IAAI,CAAC,GAAG,CACb;AACE,YAAA,IAAI,EAAE,CAAA,WAAA,CAAa;AACnB,YAAA,KAAK,EAAE,mCAAmC;AAC1C,YAAA,OAAO,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE;SACvC,EACDC,wBAAQ,EAAE,CACX;IACH;AACD;;ACrBK,SAAU,iBAAiB,CAAC,IAAsB,EAAE,cAA8B,EAAA;IACtF,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,cAAc;AAEvE,IAAA,OAAO,QAAQ,CAAC,MAAM,CAAC,8BAA8B,EAAE;QACrD,YAAY,EAAE,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,KAAK,OAAO;QAClD,IAAI;QACJ,MAAM;QACN,eAAe;QACf,OAAO;QACP,UAAU;AACV,QAAA,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC;AACxC,KAAA,CAAC;AACJ;AAEM,SAAU,iBAAiB,CAAC,IAAsB,EAAE,cAA8B,EAAA;AACtF,IAAA,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,cAAc;AAC9C,IAAA,OAAO,QAAQ,CAAC,MAAM,CAAC,0BAA0B,EAAE;QACjD,YAAY,EAAE,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,KAAK,OAAO;QAClD,UAAU;QACV,OAAO;AACR,KAAA,CAAC;AACJ;AAEA,SAAS,cAAc,CAAC,QAAiB,EAAA;AACvC,IAAA,IAAI,CAAC,QAAQ;AAAE,QAAA,OAAO,IAAI;IAE1B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC;AACjC,IAAA,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA,EAAA,EAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE;AAChD;;AC9Bc,SAAU,SAAS,CAAC,eAAuB,EAAE,GAAa,EAAA;IACtE,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC,yBAAyB,CAAC;IACvD,MAAM,aAAa,GAAG,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE;IAC3C,MAAM,kBAAkB,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;IAE7E,MAAM,oBAAoB,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,IAAG;;QAEzD,IAAI,SAAS,CAAC,QAAQ,CAAC,eAAyB,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAA,EAAG,CAAC,CAAA,CAAA,CAAG,CAAC,CAAC;AAC7G,YAAA,OAAO,SAAS;;AAGlB,QAAA,OAAO,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,eAAe,EAAE;AAC1C,IAAA,CAAC,CAAC;IAEF,MAAM,qBAAqB,GAAG;SAC3B,MAAM,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA,CAAA,CAAG,CAAC,CAAC;SAChF,GAAG,CAAC,CAAC,IAAI,CAAA,EAAG,CAAC,CAAA,QAAA,EAAW,eAAe,CAAA,CAAE,CAAC;AAE7C,IAAA,GAAG,CAAC,GAAG,CAAC,yBAAyB,EAAE,CAAC,GAAG,oBAAoB,EAAE,GAAG,qBAAqB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnG;;ACZA,MAAM,cAAc,GAA4B;AAC9C,IAAA,MAAM,EAAE,OAAO;IACf,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;AAClD,IAAA,iBAAiB,EAAE,KAAK;AACxB,IAAA,qBAAqB,EAAE,KAAK;CAC7B;AAEa,MAAO,iBAAiB,CAAA;AAEjB,IAAA,MAAA;AACA,IAAA,kBAAA;AACA,IAAA,kBAAA;AAHnB,IAAA,WAAA,CACmB,MAAiC,EACjC,kBAA6B,EAC7B,kBAAsC,EAAA;QAFtC,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,kBAAkB,GAAlB,kBAAkB;QAClB,IAAA,CAAA,kBAAkB,GAAlB,kBAAkB;IAClC;IAEH,OAAO,MAAM,CAAC,EACZ,MAAM,GAAG,OAAO,EAChB,kBAAkB,EAClB,oBAAoB,GAKrB,EAAA;AACC,QAAA,OAAO,IAAI,iBAAiB,CAC1B,MAAM,EACN,kBAAkB,EAClB,IAAI,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,EAAE,oBAAoB,CAAC,CACzE;IACH;AAEA,IAAA,qBAAqB,CAAC,cAA8B,EAAA;AAClD,QAAA,MAAM,0BAA0B,GAAG;AACjC,YAAA,GAAG,cAAc;AACjB,YAAA,GAAG,cAAc;SAClB;AACD,QAAA,MAAM,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,GAAG;AACnD,YAAA,GAAG,cAAc;AACjB,YAAA,GAAG,cAAc;SAClB;QAED,OAAO,OAAO,IAAa,EAAE,GAAa,EAAE,IAAkB,KAAI;AAChE,YAAA,MAAM,YAAY,GAAG,CAAC,IAAsB,KAAI;AAC9C,gBAAA,GAAG,CAAC,MAAM,CAAC,YAAY,GAAG;AACxB,oBAAA,MAAM,EAAE,iBAAiB,CAAC,IAAI,EAAE,0BAA0B,CAAC;AAC3D,oBAAA,MAAM,EAAE,iBAAiB,CAAC,IAAI,EAAE,0BAA0B,CAAC;AAC3D,oBAAA,WAAW,EAAE,EAAE;AACf,oBAAA,UAAU,EAAE,EAAE;iBACf;AACH,YAAA,CAAC;AAED,YAAA,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;AACpB,gBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,4DAA4D,CAAC;gBAC9E,YAAY,CAAC,IAAI,CAAC;gBAClB,OAAO,IAAI,EAAE;YACf;YAEA,IAAI,qBAAqB,EAAE;AACzB,gBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC;AACjE,gBAAA,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;gBAC7B,OAAO,IAAI,EAAE;YACf;AAEA,YAAA,IAAI;gBACF,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC;AAE7G,gBAAA,GAAG,CAAC,MAAM,CAAC,YAAY,GAAG;oBACxB,MAAM,EAAE,MAAM,CAAC,IAAI;oBACnB,MAAM,EAAE,MAAM,CAAC,IAAI;oBACnB,WAAW,EAAE,CAAC,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC;oBAC3C,UAAU,EAAE,CAAC,GAAG,MAAM,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC;iBACzD;gBAED,IAAI,iBAAiB,EAAE;oBACrB,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,GAAG,IAAI;gBAC3C;gBAEA,SAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC;gBAE3C,OAAO,IAAI,EAAE;YACf;YAAE,OAAO,MAAM,EAAE;AACf,gBAAA,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0DAA0D,CAAC;AAC7E,gBAAA,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;gBAC7B,OAAO,IAAI,EAAE;YACf;AACF,QAAA,CAAC;IACH;AACD;;AC3Fa,MAAO,eAAgB,SAAQD,0BAAU,CAAA;AACrD,IAAA,WAAA,CAAY,MAAiC,EAAE,MAAiB,EAAE,oBAA0C,EAAA;QAC1G,KAAK,CAAC,mBAAmB,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,CAAC;IAClE;IAEA,MAAM,gBAAgB,CAAC,SAAiB,EAAA;QACtC,OAAO,IAAI,CAAC,GAAG,CACb;AACE,YAAA,IAAI,EAAE,yBAAyB;AAC/B,YAAA,KAAK,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;AAC9B,SAAA,EACDE,sBAAM,CAAC,SAAS,CAAC,CAClB;IACH;AACD;;ACZa,MAAO,eAAe,CAAA;AAEf,IAAA,MAAA;AACA,IAAA,eAAA;IAFnB,WAAA,CACmB,MAAiC,EACjC,eAAgC,EAAA;QADhC,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,eAAe,GAAf,eAAe;IAC/B;IAEH,OAAO,MAAM,CAAC,EACZ,MAAM,GAAG,OAAO,EAChB,eAAe,EACf,oBAAoB,GAKrB,EAAA;AACC,QAAA,OAAO,IAAI,eAAe,CAAC,MAAM,EAAE,IAAI,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,oBAAoB,CAAC,CAAC;IACxG;IAEA,oBAAoB,GAAA;QAClB,OAAO,OAAO,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;YAC9B,IAAI,CAAC,GAAG,CAAC,OAAO;AAAE,gBAAA,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC;YAEvF,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,KAAK,OAAO,EAAE;AACtF,gBAAA,IAAI;;AAEF,oBAAA,IAAI,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE;AACjE,wBAAA,GAAG,CAAC,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS;AACpE,wBAAA,GAAG,CAAC,OAAO,CAAC,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,cAAc;AAC9E,wBAAA,GAAG,CAAC,OAAO,CAAC,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,cAAc,EAAE,UAAU;oBAC9F;;AAGA,oBAAA,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE;AAC1B,wBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA,uDAAA,EAA0D,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAA,CAAE,CAAC;AACtG,wBAAA,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;AACxF,wBAAA,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,KAAK,KAAK,CAAC;AAC3E,wBAAA,MAAM,cAAc,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,QAAkB,KAAK,QAAQ,CAAC,eAAe,CAAC;AAEvF,wBAAA,GAAG,CAAC,OAAO,CAAC,SAAS,GAAG,SAAS;AACjC,wBAAA,GAAG,CAAC,OAAO,CAAC,cAAc,GAAG,cAAc;wBAC3C,GAAG,CAAC,OAAO,CAAC,gBAAgB,GAAG,cAAc,EAAE,UAAU;oBAC3D;;AAGA,oBAAA,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS;AACjD,oBAAA,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc;AAC3D,oBAAA,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB;gBACjE;gBAAE,OAAO,KAAK,EAAE;AACd,oBAAA,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA,mCAAA,EAAsC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAA,CAAE,CAAC;AAC1F,oBAAA,OAAO,IAAI,CAAC,KAAK,CAAC;gBACpB;YACF;YAEA,OAAO,IAAI,EAAE;AACf,QAAA,CAAC;IACH;AACD;;ACzDa,MAAO,oBAAqB,SAAQF,0BAAU,CAAA;AAC1D,IAAA,WAAA,CAAY,MAAiC,EAAE,MAAiB,EAAE,oBAA0C,EAAA;QAC1G,KAAK,CAAC,wBAAwB,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,CAAC;IACvE;IAEA,MAAM,0BAA0B,CAAC,IAAgB,EAAA;QAC/C,OAAO,IAAI,CAAC,GAAG,CACb;YACE,IAAI,EAAE,YAAY,IAAI,CAAC,gBAAgB,CAAA,OAAA,EAAU,IAAI,CAAC,MAAM,CAAA,oBAAA,CAAsB;SACnF,EACDC,wBAAQ,EAAE,CACX;IACH;AACD;;ACba,MAAO,iBAAiB,CAAA;AAEjB,IAAA,MAAA;AACA,IAAA,oBAAA;IAFnB,WAAA,CACmB,MAAiC,EACjC,oBAA0C,EAAA;QAD1C,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,oBAAoB,GAApB,oBAAoB;IACpC;IAEH,OAAO,MAAM,CAAC,EACZ,MAAM,GAAG,OAAO,EAChB,oBAAoB,EACpB,oBAAoB,GAKrB,EAAA;AACC,QAAA,OAAO,IAAI,iBAAiB,CAAC,MAAM,EAAE,IAAI,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;IACpH;IAEO,qCAAqC,GAAA;QAC1C,OAAO,OAAO,GAAG,EAAE,GAAG,EAAE,IAAI,KAAI;YAC9B,IAAI,CAAC,GAAG,CAAC,OAAO;AAAE,gBAAA,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC;AAC5G,YAAA,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK;AACxB,gBAAA,MAAM,IAAI,KAAK,CACb,yKAAyK,CAC1K;AAEH,YAAA,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,KAAK,OAAO,EAAE;AAC7D,gBAAA,IAAI;;AAEF,oBAAA,IAAI,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE;AACjE,wBAAA,GAAG,CAAC,OAAO,CAAC,6BAA6B,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,6BAA6B;oBAC9G;;AAGA,oBAAA,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,6BAA6B,EAAE;AAC9C,wBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,CAAA,sEAAA,EAAyE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAA,CAAE,CACpG;AACD,wBAAA,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,0BAA0B,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;wBACtG,GAAG,CAAC,OAAO,CAAC,6BAA6B,GAAG,kBAAkB,CAAC,QAAQ;oBACzE;;AAGA,oBAAA,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,6BAA6B,GAAG,GAAG,CAAC,OAAO,CAAC,6BAA6B;gBAC3F;gBAAE,OAAO,KAAK,EAAE;AACd,oBAAA,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,KAAK,EACL,CAAA,wDAAA,EAA2D,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAA,CAAE,CACtF;AACD,oBAAA,OAAO,IAAI,CAAC,KAAK,CAAC;gBACpB;YACF;YAEA,OAAO,IAAI,EAAE;AACf,QAAA,CAAC;IACH;AACD;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,67 +1,153 @@
1
- import getPageComponents from './componentsService';
2
- import retrieveCaseLoadData from './caseLoadService';
3
- import retrieveAllocationJobResponsibilities from './allocationService';
4
- declare const _default: {
5
- /**
6
- * Returns a request handler for adding header and footer frontend components to res.locals
7
- *
8
- * Adds stringified html for each component along with lists of css javascript links.
9
- *
10
- * Expects nunjucks and res.locals.user to be set up inline with the hmpps-template-typescript project
11
- *
12
- * @param requestOptions - config object for request
13
- * @param requestOptions.dpsUrl - url to the dps homepage to be used in the header
14
- * @param requestOptions.authUrl - if your service has users with non-nomis auth sources, pass in the url to the auth service for the home link
15
- * @param requestOptions.supportUrl - if your service has users with non-nomis auth sources, pass in the support url for the support link
16
- * @param requestOptions.environmentName - if you require environment tags on the fallback banner "DEV", "PRE-PRODUCTION" or "PRODUCTION" can be passed in
17
- * @param requestOptions.logger - pass in the bunyen logger if you want to use it. Falls back to console if not provided
18
- * @param requestOptions.timeoutOptions - timeout object for superagent. Defaults to 2500ms
19
- * @param requestOptions.includeSharedData - adds sharedData to res.locals.feComponents if true. Contains data that the components have collected in order to render, including: activeCaseLoad, caseLoads and available services for user
20
- * @param requestOptions.useFallbacksByDefault - if your service requires only the basic fallback header and footer to be used by default, set this to true, it will not attempt to fetch the components from the frontend components service
21
- */
22
- getPageComponents: typeof getPageComponents;
23
- /**
24
- * Ensures that:
25
- * - `res.locals.user.caseLoads`
26
- * - `res.locals.user.activeCaseLoad`
27
- * - `res.locals.user.activeCaseLoadId`
28
- * are set for NOMIS users (or will propagate an error). It will also attempt to cache in `req.session.caseLoads`,
29
- * `req.session.activeCaseLoad` and `req.session.activeCaseLoadId` (this is so that extra requests to Prison API
30
- * are not required for routes that do not need frontend components, such as image data, or if the frontend component
31
- * API errors or is temporarily down).
32
- *
33
- * It will do the following in priority order, and will attempt the next if the previous fails:
34
- *
35
- * * Use values from `res.feComponents.sharedData` if present (and cache in `req.session`)
36
- * * Use cached data from `req.session`
37
- * * Fetch data from Prison API as a fallback and cache in `req.session`
38
- *
39
- * Expects res.locals.user to be set up inline with the hmpps-template-typescript project
40
- *
41
- * @param caseLoadOptions - config object for request
42
- * @param caseLoadOptions.logger - pass in the bunyen logger if you want to use it. Falls back to console if not provided
43
- * @param caseLoadOptions.timeoutOptions - timeout object for superagent. Defaults to 2500ms
44
- */
45
- retrieveCaseLoadData: typeof retrieveCaseLoadData;
46
- /**
47
- * Ensures that:
48
- * - `res.locals.user.allocationJobResponsibilities`
49
- * is set for NOMIS users (or will propagate an error). It will also attempt to cache in `req.session.allocationJobResponsibilities`
50
- * (this is so that extra requests to Allocations API are not required for routes that do not need frontend components,
51
- * such as image data, or if the frontend component API errors or is temporarily down).
52
- *
53
- * It will do the following in priority order, and will attempt the next if the previous fails:
54
- *
55
- * * Use values from `res.feComponents.sharedData` if present (and cache in `req.session`)
56
- * * Use cached data from `req.session`
57
- * * Fetch data from Allocations API as a fallback and cache in `req.session`
58
- *
59
- * Expects res.locals.user to be set up inline with the hmpps-template-typescript project
60
- *
61
- * @param caseLoadOptions - config object for request
62
- * @param caseLoadOptions.logger - pass in the bunyen logger if you want to use it. Falls back to console if not provided
63
- * @param caseLoadOptions.timeoutOptions - timeout object for superagent. Defaults to 2500ms
64
- */
65
- retrieveAllocationJobResponsibilities: typeof retrieveAllocationJobResponsibilities;
1
+ import { RequestHandler } from 'express';
2
+ import { ApiConfig, AuthenticationClient, RestClient } from '@ministryofjustice/hmpps-rest-client';
3
+ import Logger from 'bunyan';
4
+
5
+ type ConnectDpsComponentLogger = Logger | typeof console;
6
+
7
+ interface TimeoutOptions {
8
+ response: number;
9
+ deadline: number;
10
+ }
11
+
12
+ interface RequestOptions {
13
+ dpsUrl: string;
14
+ authUrl?: string;
15
+ supportUrl?: string;
16
+ environmentName?: 'DEV' | 'PRE-PRODUCTION' | 'PRODUCTION';
17
+ logger?: Logger | typeof console;
18
+ includeSharedData?: boolean;
19
+ useFallbacksByDefault?: boolean;
20
+ timeoutOptions?: TimeoutOptions;
21
+ }
22
+
23
+ declare function getFrontendComponents({ logger, componentApiConfig, authenticationClient, }: {
24
+ logger?: ConnectDpsComponentLogger;
25
+ componentApiConfig: ApiConfig;
26
+ authenticationClient: AuthenticationClient;
27
+ }): (requestOptions: RequestOptions) => RequestHandler;
28
+
29
+ type AvailableComponent = 'header' | 'footer';
30
+
31
+ interface Component {
32
+ html: string;
33
+ css: string[];
34
+ javascript: string[];
35
+ }
36
+
37
+ interface CaseLoad {
38
+ caseLoadId: string;
39
+ description: string;
40
+ type: string;
41
+ caseloadFunction: string;
42
+ currentlyActive: boolean;
43
+ }
44
+
45
+ interface Service {
46
+ id: string;
47
+ heading: string;
48
+ description: string;
49
+ href: string;
50
+ navEnabled: boolean;
51
+ }
52
+
53
+ type AllocationJobResponsibility = 'KEY_WORKER' | 'PERSONAL_OFFICER';
54
+
55
+ interface HeaderFooterSharedData {
56
+ activeCaseLoad?: CaseLoad;
57
+ caseLoads: CaseLoad[];
58
+ services: Service[];
59
+ allocationJobResponsibilities: AllocationJobResponsibility[];
60
+ }
61
+ interface ComponentsSharedData {
62
+ header: HeaderFooterSharedData;
63
+ footer: HeaderFooterSharedData;
64
+ }
65
+
66
+ type ComponentsApiResponse<T extends AvailableComponent[]> = Record<T[number], Component> & {
67
+ meta: ComponentsSharedData[T[number]];
66
68
  };
67
- export default _default;
69
+ declare class ComponentApiClient extends RestClient {
70
+ constructor(logger: ConnectDpsComponentLogger, config: ApiConfig, authenticationClient: AuthenticationClient);
71
+ getComponents<T extends AvailableComponent[]>(userToken: string): Promise<ComponentsApiResponse<T>>;
72
+ }
73
+
74
+ declare class ComponentsService {
75
+ private readonly logger;
76
+ private readonly componentApiConfig;
77
+ private readonly componentApiClient;
78
+ constructor(logger: ConnectDpsComponentLogger, componentApiConfig: ApiConfig, componentApiClient: ComponentApiClient);
79
+ static create({ logger, componentApiConfig, authenticationClient, }: {
80
+ logger?: ConnectDpsComponentLogger;
81
+ componentApiConfig: ApiConfig;
82
+ authenticationClient: AuthenticationClient;
83
+ }): ComponentsService;
84
+ getFrontendComponents(requestOptions: RequestOptions): RequestHandler;
85
+ }
86
+
87
+ declare class PrisonApiClient extends RestClient {
88
+ constructor(logger: ConnectDpsComponentLogger, config: ApiConfig, authenticationClient: AuthenticationClient);
89
+ getUserCaseLoads(userToken: string): Promise<CaseLoad[]>;
90
+ }
91
+
92
+ declare class CaseLoadService {
93
+ private readonly logger;
94
+ private readonly prisonApiClient;
95
+ constructor(logger: ConnectDpsComponentLogger, prisonApiClient: PrisonApiClient);
96
+ static create({ logger, prisonApiConfig, authenticationClient, }: {
97
+ logger?: ConnectDpsComponentLogger;
98
+ prisonApiConfig: ApiConfig;
99
+ authenticationClient: AuthenticationClient;
100
+ }): CaseLoadService;
101
+ retrieveCaseLoadData(): RequestHandler;
102
+ }
103
+
104
+ type AuthSource = 'nomis' | 'delius' | 'external' | 'azuread';
105
+ /**
106
+ * These are the details that all user types share.
107
+ */
108
+ interface BaseUser {
109
+ authSource: AuthSource;
110
+ username: string;
111
+ userId: string;
112
+ name: string;
113
+ displayName: string;
114
+ token?: string;
115
+ backLink?: string;
116
+ }
117
+ /**
118
+ * Prison users are those that have a user account in NOMIS.
119
+ * HMPPS Auth automatically grants these users a `ROLE_PRISON` role.
120
+ * Prison users have an additional numerical staffId. The userId is
121
+ * a stringified version of the staffId. Some teams may need to separately
122
+ * retrieve the user case load (which prisons that a prison user has access
123
+ * to) and store it here, an example can be found in `hmpps-prisoner-profile`.
124
+ */
125
+ interface PrisonUser extends BaseUser {
126
+ authSource: 'nomis';
127
+ staffId: number;
128
+ caseLoads: CaseLoad[];
129
+ activeCaseLoad?: CaseLoad;
130
+ activeCaseLoadId?: string;
131
+ allocationJobResponsibilities?: AllocationJobResponsibility[];
132
+ }
133
+
134
+ declare class AllocationsApiClient extends RestClient {
135
+ constructor(logger: ConnectDpsComponentLogger, config: ApiConfig, authenticationClient: AuthenticationClient);
136
+ getStaffAllocationPolicies(user: PrisonUser): Promise<{
137
+ policies: AllocationJobResponsibility[];
138
+ }>;
139
+ }
140
+
141
+ declare class AllocationService {
142
+ private readonly logger;
143
+ private readonly allocationsApiClient;
144
+ constructor(logger: ConnectDpsComponentLogger, allocationsApiClient: AllocationsApiClient);
145
+ static create({ logger, allocationsApiConfig, authenticationClient, }: {
146
+ logger?: ConnectDpsComponentLogger;
147
+ allocationsApiConfig: ApiConfig;
148
+ authenticationClient: AuthenticationClient;
149
+ }): AllocationService;
150
+ retrieveAllocationJobResponsibilities(): RequestHandler;
151
+ }
152
+
153
+ export { AllocationService, CaseLoadService, ComponentsService, getFrontendComponents };