@ministryofjustice/hmpps-digital-prison-reporting-frontend 4.28.6 → 4.28.7
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/dpr/data/restClient.js +48 -20
- package/dpr/data/restClient.js.map +3 -3
- package/dpr/data/restClient.ts +59 -31
- package/package.json +1 -1
package/dpr/data/restClient.js
CHANGED
|
@@ -34,6 +34,10 @@ __export(restClient_exports, {
|
|
|
34
34
|
module.exports = __toCommonJS(restClient_exports);
|
|
35
35
|
var import_superagent = __toESM(require("superagent"));
|
|
36
36
|
var import_agentkeepalive = __toESM(require("agentkeepalive"));
|
|
37
|
+
var import_http = __toESM(require("http"));
|
|
38
|
+
var import_https = __toESM(require("https"));
|
|
39
|
+
var import_url = require("url");
|
|
40
|
+
var import_stream = require("stream");
|
|
37
41
|
var import_logger = __toESM(require("../utils/logger"));
|
|
38
42
|
var import_sanitisedError = __toESM(require("../utils/sanitisedError"));
|
|
39
43
|
class RestClient {
|
|
@@ -53,29 +57,53 @@ class RestClient {
|
|
|
53
57
|
return this.getWithHeaders(request).then((result) => result.data);
|
|
54
58
|
}
|
|
55
59
|
async getStream({ path = "", query = {}, headers = {}, token }, res) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
const url = new import_url.URL(`${this.apiUrl()}${path}`);
|
|
61
|
+
Object.entries(query).forEach(([key, value]) => {
|
|
62
|
+
if (value === void 0) return;
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
value.forEach((v) => url.searchParams.append(key, String(v)));
|
|
65
|
+
} else {
|
|
66
|
+
url.searchParams.append(key, String(value));
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const client = url.protocol === "https:" ? import_https.default : import_http.default;
|
|
70
|
+
const req = client.request(
|
|
71
|
+
url,
|
|
72
|
+
{
|
|
73
|
+
method: "GET",
|
|
74
|
+
headers: {
|
|
75
|
+
...headers,
|
|
76
|
+
Authorization: token ? `Bearer ${token}` : void 0
|
|
77
|
+
},
|
|
78
|
+
agent: this.agent
|
|
79
|
+
},
|
|
80
|
+
(upstream) => {
|
|
81
|
+
res.status(upstream.statusCode || 500);
|
|
82
|
+
Object.entries(upstream.headers).forEach(([key, value]) => {
|
|
83
|
+
if (value !== void 0) {
|
|
84
|
+
res.setHeader(key, value);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
res.flushHeaders();
|
|
88
|
+
res.on("close", () => {
|
|
89
|
+
req.destroy();
|
|
90
|
+
});
|
|
91
|
+
(0, import_stream.pipeline)(upstream, res, (err) => {
|
|
92
|
+
if (err) {
|
|
93
|
+
res.destroy(err);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
req.setTimeout(this.timeoutConfig(), () => {
|
|
99
|
+
req.destroy(new Error("Upstream request timed out."));
|
|
72
100
|
});
|
|
73
|
-
req.on("error", (
|
|
74
|
-
import_logger.default.warn({
|
|
101
|
+
req.on("error", (err) => {
|
|
102
|
+
import_logger.default.warn({ err }, `Error streaming from ${this.name}, path: '${path}'`);
|
|
75
103
|
if (!res.headersSent) {
|
|
76
|
-
res.status(502).end("Download request failed
|
|
104
|
+
res.status(502).end("Download request failed");
|
|
77
105
|
} else {
|
|
78
|
-
res.destroy(
|
|
106
|
+
res.destroy(err);
|
|
79
107
|
}
|
|
80
108
|
});
|
|
81
109
|
req.end();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../dpr/data/restClient.ts"],
|
|
4
|
-
"sourcesContent": ["import superagent, { ResponseError } from 'superagent'\nimport Agent, { HttpsAgent } from 'agentkeepalive'\nimport { Response as ExpressResponse } from 'express'\n\nimport logger from '../utils/logger'\nimport sanitiseError from '../utils/sanitisedError'\nimport { ApiConfig, GetRequest } from './types'\nimport Dict = NodeJS.Dict\n\nexport interface ResultWithHeaders<T> {\n data: T\n headers: Dict<string>\n}\n\ninterface Request {\n path: string\n query?: object | string\n headers?: Record<string, string>\n responseType?: string\n raw?: boolean\n}\ninterface RequestWithBody extends Request {\n data?: Record<string, unknown> | string\n retry?: boolean\n}\n\nclass RestClient {\n agent: Agent\n\n constructor(private readonly name: string, private readonly config: ApiConfig) {\n this.agent = config.url.startsWith('https') ? new HttpsAgent(config.agent) : new Agent(config.agent)\n }\n\n private apiUrl() {\n return this.config.url\n }\n\n private timeoutConfig() {\n return this.config.agent.timeout\n }\n\n async get<T>(request: GetRequest): Promise<T> {\n return this.getWithHeaders<T>(request).then((result) => result.data)\n }\n\n async getStream({ path = '', query = {}, headers = {}, token }: GetRequest, res: ExpressResponse): Promise<void> {\n
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAA0C;AAC1C,4BAAkC;
|
|
6
|
-
"names": ["Agent", "logger", "superagent", "sanitiseError"]
|
|
4
|
+
"sourcesContent": ["import superagent, { ResponseError } from 'superagent'\nimport Agent, { HttpsAgent } from 'agentkeepalive'\nimport http from 'http'\nimport https from 'https'\nimport { URL } from 'url'\nimport { pipeline } from 'stream'\nimport { Response as ExpressResponse } from 'express'\n\nimport logger from '../utils/logger'\nimport sanitiseError from '../utils/sanitisedError'\nimport { ApiConfig, GetRequest } from './types'\nimport Dict = NodeJS.Dict\n\nexport interface ResultWithHeaders<T> {\n data: T\n headers: Dict<string>\n}\n\ninterface Request {\n path: string\n query?: object | string\n headers?: Record<string, string>\n responseType?: string\n raw?: boolean\n}\ninterface RequestWithBody extends Request {\n data?: Record<string, unknown> | string\n retry?: boolean\n}\n\nclass RestClient {\n agent: Agent\n\n constructor(private readonly name: string, private readonly config: ApiConfig) {\n this.agent = config.url.startsWith('https') ? new HttpsAgent(config.agent) : new Agent(config.agent)\n }\n\n private apiUrl() {\n return this.config.url\n }\n\n private timeoutConfig() {\n return this.config.agent.timeout\n }\n\n async get<T>(request: GetRequest): Promise<T> {\n return this.getWithHeaders<T>(request).then((result) => result.data)\n }\n\n async getStream({ path = '', query = {}, headers = {}, token }: GetRequest, res: ExpressResponse): Promise<void> {\n const url = new URL(`${this.apiUrl()}${path}`)\n\n Object.entries(query).forEach(([key, value]) => {\n if (value === undefined) return\n if (Array.isArray(value)) {\n value.forEach((v) => url.searchParams.append(key, String(v)))\n } else {\n url.searchParams.append(key, String(value))\n }\n })\n\n const client = url.protocol === 'https:' ? https : http\n\n const req = client.request(\n url,\n {\n method: 'GET',\n headers: {\n ...headers,\n Authorization: token ? `Bearer ${token}` : undefined,\n },\n agent: this.agent,\n },\n (upstream) => {\n // Forward status\n res.status(upstream.statusCode || 500)\n\n // Forward headers\n Object.entries(upstream.headers).forEach(([key, value]) => {\n if (value !== undefined) {\n res.setHeader(key, value as string)\n }\n })\n\n res.flushHeaders()\n\n res.on('close', () => {\n req.destroy()\n })\n\n pipeline(upstream, res, (err) => {\n if (err) {\n res.destroy(err)\n }\n })\n },\n )\n\n req.setTimeout(this.timeoutConfig(), () => {\n req.destroy(new Error('Upstream request timed out.'))\n })\n\n req.on('error', (err) => {\n logger.warn({ err }, `Error streaming from ${this.name}, path: '${path}'`)\n if (!res.headersSent) {\n res.status(502).end('Download request failed')\n } else {\n res.destroy(err)\n }\n })\n\n req.end()\n }\n\n private async requestWithBody<Response = unknown>(\n method: 'patch' | 'post' | 'put',\n { path, query = {}, headers = {}, responseType = '', data = {}, raw = false, retry = false }: RequestWithBody,\n token: string,\n ): Promise<Response> {\n logger.info(`${this.name} ${method.toUpperCase()}: ${path}`)\n logger.info(`info about request: ${method} | ${path} | ${JSON.stringify(data)} | ${query}`)\n try {\n const result = await superagent[method](`${this.apiUrl()}${path}`)\n .query(query)\n .send(data)\n .agent(this.agent)\n .retry(2, (err) => {\n if (retry === false) {\n return false\n }\n if (err) 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 .auth(token, { type: 'bearer' })\n .set(headers)\n .responseType(responseType)\n .timeout(this.timeoutConfig())\n\n return raw ? (result as Response) : result.body\n } catch (error) {\n if (!(error instanceof Error)) {\n throw error\n }\n const sanitisedError = sanitiseError(error)\n logger.warn({ ...sanitisedError }, `Error calling ${this.name}, path: '${path}', verb: '${method.toUpperCase()}'`)\n throw sanitisedError\n }\n }\n\n async post<Response = unknown>(request: RequestWithBody, token: string): Promise<Response> {\n return this.requestWithBody('post', request, token)\n }\n\n async getWithHeaders<T>({\n path = '',\n query = {},\n headers = {},\n responseType = '',\n raw = false,\n token,\n }: GetRequest): Promise<ResultWithHeaders<T>> {\n const loggerData = {\n path: `${this.config.url}${path}`,\n query,\n }\n logger.info(`${this.name}: ${JSON.stringify(loggerData, null, 2)}`)\n try {\n const result = await superagent\n .get(`${this.apiUrl()}${path}`)\n .agent(this.agent)\n .retry(2, (err) => {\n if (err) 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 .auth(token, { type: 'bearer' })\n .set(headers)\n .responseType(responseType)\n .timeout(this.timeoutConfig())\n\n return {\n data: raw ? result : result.body,\n headers: result.headers,\n }\n } catch (error) {\n const sanitisedError = sanitiseError(<ResponseError>error)\n logger.warn({ ...sanitisedError, query }, `Error calling ${this.name}, path: '${path}', verb: 'GET'`)\n throw sanitisedError\n }\n }\n\n async deleteWithHeaders<T>({\n path = '',\n query = {},\n headers = {},\n responseType = '',\n raw = false,\n token,\n }: GetRequest): Promise<ResultWithHeaders<T>> {\n const loggerData = {\n path: `${this.config.url}${path}`,\n query,\n }\n logger.info(`${this.name}: ${JSON.stringify(loggerData, null, 2)}`)\n try {\n const result = await superagent\n .delete(`${this.apiUrl()}${path}`)\n .agent(this.agent)\n .retry(2, (err) => {\n if (err) 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 .auth(token, { type: 'bearer' })\n .set(headers)\n .responseType(responseType)\n .timeout(this.timeoutConfig())\n\n return {\n data: raw ? result : result.body,\n headers: result.headers,\n }\n } catch (error) {\n const sanitisedError = sanitiseError(<ResponseError>error)\n logger.warn({ ...sanitisedError, query }, `Error calling ${this.name}, path: '${path}', verb: 'GET'`)\n throw sanitisedError\n }\n }\n\n async delete<T>(request: GetRequest): Promise<T> {\n return this.deleteWithHeaders<T>(request).then((result) => result.data)\n }\n}\n\nexport { RestClient }\nexport default RestClient\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAA0C;AAC1C,4BAAkC;AAClC,kBAAiB;AACjB,mBAAkB;AAClB,iBAAoB;AACpB,oBAAyB;AAGzB,oBAAmB;AACnB,4BAA0B;AAqB1B,MAAM,WAAW;AAAA,EAGf,YAA6B,MAA+B,QAAmB;AAAlD;AAA+B;AAC1D,SAAK,QAAQ,OAAO,IAAI,WAAW,OAAO,IAAI,IAAI,iCAAW,OAAO,KAAK,IAAI,IAAI,sBAAAA,QAAM,OAAO,KAAK;AAAA,EACrG;AAAA,EAJA;AAAA,EAMQ,SAAS;AACf,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEQ,gBAAgB;AACtB,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AAAA,EAEA,MAAM,IAAO,SAAiC;AAC5C,WAAO,KAAK,eAAkB,OAAO,EAAE,KAAK,CAAC,WAAW,OAAO,IAAI;AAAA,EACrE;AAAA,EAEA,MAAM,UAAU,EAAE,OAAO,IAAI,QAAQ,CAAC,GAAG,UAAU,CAAC,GAAG,MAAM,GAAe,KAAqC;AAC/G,UAAM,MAAM,IAAI,eAAI,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,EAAE;AAE7C,WAAO,QAAQ,KAAK,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAC9C,UAAI,UAAU,OAAW;AACzB,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,cAAM,QAAQ,CAAC,MAAM,IAAI,aAAa,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC;AAAA,MAC9D,OAAO;AACL,YAAI,aAAa,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,MAC5C;AAAA,IACF,CAAC;AAED,UAAM,SAAS,IAAI,aAAa,WAAW,aAAAC,UAAQ,YAAAC;AAEnD,UAAM,MAAM,OAAO;AAAA,MACjB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,GAAG;AAAA,UACH,eAAe,QAAQ,UAAU,KAAK,KAAK;AAAA,QAC7C;AAAA,QACA,OAAO,KAAK;AAAA,MACd;AAAA,MACA,CAAC,aAAa;AAEZ,YAAI,OAAO,SAAS,cAAc,GAAG;AAGrC,eAAO,QAAQ,SAAS,OAAO,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AACzD,cAAI,UAAU,QAAW;AACvB,gBAAI,UAAU,KAAK,KAAe;AAAA,UACpC;AAAA,QACF,CAAC;AAED,YAAI,aAAa;AAEjB,YAAI,GAAG,SAAS,MAAM;AACpB,cAAI,QAAQ;AAAA,QACd,CAAC;AAED,oCAAS,UAAU,KAAK,CAAC,QAAQ;AAC/B,cAAI,KAAK;AACP,gBAAI,QAAQ,GAAG;AAAA,UACjB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,WAAW,KAAK,cAAc,GAAG,MAAM;AACzC,UAAI,QAAQ,IAAI,MAAM,6BAA6B,CAAC;AAAA,IACtD,CAAC;AAED,QAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,oBAAAC,QAAO,KAAK,EAAE,IAAI,GAAG,wBAAwB,KAAK,IAAI,YAAY,IAAI,GAAG;AACzE,UAAI,CAAC,IAAI,aAAa;AACpB,YAAI,OAAO,GAAG,EAAE,IAAI,yBAAyB;AAAA,MAC/C,OAAO;AACL,YAAI,QAAQ,GAAG;AAAA,MACjB;AAAA,IACF,CAAC;AAED,QAAI,IAAI;AAAA,EACV;AAAA,EAEA,MAAc,gBACZ,QACA,EAAE,MAAM,QAAQ,CAAC,GAAG,UAAU,CAAC,GAAG,eAAe,IAAI,OAAO,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,GAC3F,OACmB;AACnB,kBAAAA,QAAO,KAAK,GAAG,KAAK,IAAI,IAAI,OAAO,YAAY,CAAC,KAAK,IAAI,EAAE;AAC3D,kBAAAA,QAAO,KAAK,uBAAuB,MAAM,MAAM,IAAI,MAAM,KAAK,UAAU,IAAI,CAAC,MAAM,KAAK,EAAE;AAC1F,QAAI;AACF,YAAM,SAAS,MAAM,kBAAAC,QAAW,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,EAAE,EAC9D,MAAM,KAAK,EACX,KAAK,IAAI,EACT,MAAM,KAAK,KAAK,EAChB,MAAM,GAAG,CAAC,QAAQ;AACjB,YAAI,UAAU,OAAO;AACnB,iBAAO;AAAA,QACT;AACA,YAAI,IAAK,eAAAD,QAAO,KAAK,sCAAsC,IAAI,IAAI,IAAI,IAAI,OAAO,EAAE;AACpF,eAAO;AAAA,MACT,CAAC,EACA,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC,EAC9B,IAAI,OAAO,EACX,aAAa,YAAY,EACzB,QAAQ,KAAK,cAAc,CAAC;AAE/B,aAAO,MAAO,SAAsB,OAAO;AAAA,IAC7C,SAAS,OAAO;AACd,UAAI,EAAE,iBAAiB,QAAQ;AAC7B,cAAM;AAAA,MACR;AACA,YAAM,qBAAiB,sBAAAE,SAAc,KAAK;AAC1C,oBAAAF,QAAO,KAAK,EAAE,GAAG,eAAe,GAAG,iBAAiB,KAAK,IAAI,YAAY,IAAI,aAAa,OAAO,YAAY,CAAC,GAAG;AACjH,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,KAAyB,SAA0B,OAAkC;AACzF,WAAO,KAAK,gBAAgB,QAAQ,SAAS,KAAK;AAAA,EACpD;AAAA,EAEA,MAAM,eAAkB;AAAA,IACtB,OAAO;AAAA,IACP,QAAQ,CAAC;AAAA,IACT,UAAU,CAAC;AAAA,IACX,eAAe;AAAA,IACf,MAAM;AAAA,IACN;AAAA,EACF,GAA8C;AAC5C,UAAM,aAAa;AAAA,MACjB,MAAM,GAAG,KAAK,OAAO,GAAG,GAAG,IAAI;AAAA,MAC/B;AAAA,IACF;AACA,kBAAAA,QAAO,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC,EAAE;AAClE,QAAI;AACF,YAAM,SAAS,MAAM,kBAAAC,QAClB,IAAI,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,EAAE,EAC7B,MAAM,KAAK,KAAK,EAChB,MAAM,GAAG,CAAC,QAAQ;AACjB,YAAI,IAAK,eAAAD,QAAO,KAAK,sCAAsC,IAAI,IAAI,IAAI,IAAI,OAAO,EAAE;AACpF,eAAO;AAAA,MACT,CAAC,EACA,MAAM,KAAK,EACX,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC,EAC9B,IAAI,OAAO,EACX,aAAa,YAAY,EACzB,QAAQ,KAAK,cAAc,CAAC;AAE/B,aAAO;AAAA,QACL,MAAM,MAAM,SAAS,OAAO;AAAA,QAC5B,SAAS,OAAO;AAAA,MAClB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,qBAAiB,sBAAAE,SAA6B,KAAK;AACzD,oBAAAF,QAAO,KAAK,EAAE,GAAG,gBAAgB,MAAM,GAAG,iBAAiB,KAAK,IAAI,YAAY,IAAI,gBAAgB;AACpG,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,kBAAqB;AAAA,IACzB,OAAO;AAAA,IACP,QAAQ,CAAC;AAAA,IACT,UAAU,CAAC;AAAA,IACX,eAAe;AAAA,IACf,MAAM;AAAA,IACN;AAAA,EACF,GAA8C;AAC5C,UAAM,aAAa;AAAA,MACjB,MAAM,GAAG,KAAK,OAAO,GAAG,GAAG,IAAI;AAAA,MAC/B;AAAA,IACF;AACA,kBAAAA,QAAO,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC,EAAE;AAClE,QAAI;AACF,YAAM,SAAS,MAAM,kBAAAC,QAClB,OAAO,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,EAAE,EAChC,MAAM,KAAK,KAAK,EAChB,MAAM,GAAG,CAAC,QAAQ;AACjB,YAAI,IAAK,eAAAD,QAAO,KAAK,sCAAsC,IAAI,IAAI,IAAI,IAAI,OAAO,EAAE;AACpF,eAAO;AAAA,MACT,CAAC,EACA,MAAM,KAAK,EACX,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC,EAC9B,IAAI,OAAO,EACX,aAAa,YAAY,EACzB,QAAQ,KAAK,cAAc,CAAC;AAE/B,aAAO;AAAA,QACL,MAAM,MAAM,SAAS,OAAO;AAAA,QAC5B,SAAS,OAAO;AAAA,MAClB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,qBAAiB,sBAAAE,SAA6B,KAAK;AACzD,oBAAAF,QAAO,KAAK,EAAE,GAAG,gBAAgB,MAAM,GAAG,iBAAiB,KAAK,IAAI,YAAY,IAAI,gBAAgB;AACpG,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,OAAU,SAAiC;AAC/C,WAAO,KAAK,kBAAqB,OAAO,EAAE,KAAK,CAAC,WAAW,OAAO,IAAI;AAAA,EACxE;AACF;AAGA,IAAO,qBAAQ;",
|
|
6
|
+
"names": ["Agent", "https", "http", "logger", "superagent", "sanitiseError"]
|
|
7
7
|
}
|
package/dpr/data/restClient.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import superagent, { ResponseError } from 'superagent'
|
|
2
2
|
import Agent, { HttpsAgent } from 'agentkeepalive'
|
|
3
|
+
import http from 'http'
|
|
4
|
+
import https from 'https'
|
|
5
|
+
import { URL } from 'url'
|
|
6
|
+
import { pipeline } from 'stream'
|
|
3
7
|
import { Response as ExpressResponse } from 'express'
|
|
4
8
|
|
|
5
9
|
import logger from '../utils/logger'
|
|
@@ -44,43 +48,67 @@ class RestClient {
|
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
async getStream({ path = '', query = {}, headers = {}, token }: GetRequest, res: ExpressResponse): Promise<void> {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.timeout(this.timeoutConfig())
|
|
57
|
-
|
|
58
|
-
req.on('response', (upstream) => {
|
|
59
|
-
// Forward status
|
|
60
|
-
res.status(upstream.status)
|
|
61
|
-
|
|
62
|
-
// Forward headers
|
|
63
|
-
Object.entries(upstream.headers).forEach(([key, value]) => {
|
|
64
|
-
if (value !== undefined) {
|
|
65
|
-
res.setHeader(key, value as string)
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
res.on('close', () => {
|
|
69
|
-
logger.info('Client disconnected, aborting upstream request.')
|
|
70
|
-
req.abort()
|
|
71
|
-
})
|
|
72
|
-
res.flushHeaders()
|
|
73
|
-
upstream.pipe(res)
|
|
51
|
+
const url = new URL(`${this.apiUrl()}${path}`)
|
|
52
|
+
|
|
53
|
+
Object.entries(query).forEach(([key, value]) => {
|
|
54
|
+
if (value === undefined) return
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
value.forEach((v) => url.searchParams.append(key, String(v)))
|
|
57
|
+
} else {
|
|
58
|
+
url.searchParams.append(key, String(value))
|
|
59
|
+
}
|
|
74
60
|
})
|
|
75
61
|
|
|
76
|
-
|
|
77
|
-
|
|
62
|
+
const client = url.protocol === 'https:' ? https : http
|
|
63
|
+
|
|
64
|
+
const req = client.request(
|
|
65
|
+
url,
|
|
66
|
+
{
|
|
67
|
+
method: 'GET',
|
|
68
|
+
headers: {
|
|
69
|
+
...headers,
|
|
70
|
+
Authorization: token ? `Bearer ${token}` : undefined,
|
|
71
|
+
},
|
|
72
|
+
agent: this.agent,
|
|
73
|
+
},
|
|
74
|
+
(upstream) => {
|
|
75
|
+
// Forward status
|
|
76
|
+
res.status(upstream.statusCode || 500)
|
|
77
|
+
|
|
78
|
+
// Forward headers
|
|
79
|
+
Object.entries(upstream.headers).forEach(([key, value]) => {
|
|
80
|
+
if (value !== undefined) {
|
|
81
|
+
res.setHeader(key, value as string)
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
res.flushHeaders()
|
|
86
|
+
|
|
87
|
+
res.on('close', () => {
|
|
88
|
+
req.destroy()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
pipeline(upstream, res, (err) => {
|
|
92
|
+
if (err) {
|
|
93
|
+
res.destroy(err)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
req.setTimeout(this.timeoutConfig(), () => {
|
|
100
|
+
req.destroy(new Error('Upstream request timed out.'))
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
req.on('error', (err) => {
|
|
104
|
+
logger.warn({ err }, `Error streaming from ${this.name}, path: '${path}'`)
|
|
78
105
|
if (!res.headersSent) {
|
|
79
|
-
res.status(502).end('Download request failed
|
|
106
|
+
res.status(502).end('Download request failed')
|
|
80
107
|
} else {
|
|
81
|
-
res.destroy(
|
|
108
|
+
res.destroy(err)
|
|
82
109
|
}
|
|
83
110
|
})
|
|
111
|
+
|
|
84
112
|
req.end()
|
|
85
113
|
}
|
|
86
114
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ministryofjustice/hmpps-digital-prison-reporting-frontend",
|
|
3
3
|
"description": "The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.",
|
|
4
|
-
"version": "4.28.
|
|
4
|
+
"version": "4.28.7",
|
|
5
5
|
"main": "dpr/all",
|
|
6
6
|
"sass": "dpr/all.scss",
|
|
7
7
|
"engines": {
|