Merged PR 2206: split out actions to services and clients

Related work items: #22260
This commit is contained in:
Robert Bond
2026-03-25 12:16:40 +00:00
30 changed files with 3487 additions and 1253 deletions
+15 -3
View File
@@ -1,6 +1,18 @@
# Actions Clients # Actions Clients
This folder is reserved for extracted client wrappers from `actions/index.js` as part of the Priority 1 refactor plan. This folder contains extracted client wrappers from `actions/index.js` as part of the Priority 1 refactor plan.
Phase 1 delivered core helper extraction and compatibility barrel support. Current extracted clients:
Client-level extraction (`relayClient`, `endpointClient`, `fileClient`, `notifyClient`) is planned for the next increment.
- `relayClient` (shared hash-signing helper used by account/portal/document direct services)
- `endpointClient` (shared JSON request helpers for GET and generic axios config requests)
Current usage includes:
- relay hash-signing helper reuse across account/portal/document direct services
- shared endpoint JSON request helpers reused by admin/notify/integration/reference-data direct services
Notes:
- Core helper extraction and compatibility barrel support remain in place.
- Additional client extraction (`fileClient`, `notifyClient`) can be layered in incrementally without changing public exports from `actions/index.js`.
+11
View File
@@ -0,0 +1,11 @@
import axios from "axios";
export const getJson = async (url, config) => {
const response = await axios.get(url, config);
return response.data;
};
export const requestJson = async (config) => {
const response = await axios(config);
return response.data;
};
+34
View File
@@ -0,0 +1,34 @@
import { getJson, requestJson } from "./endpointClient";
import { buildHashedQueryUrl } from "./relayClient";
export const getFileJson = (url) => {
return getJson(url);
};
export const getSignedFileJson = async (queryUrl) => {
const signedUrl = await buildHashedQueryUrl(queryUrl);
return requestJson({
method: "get",
url: signedUrl
});
};
export const downloadFileBlob = (url) => {
return requestJson({
method: "get",
url,
responseType: "blob"
});
};
export const postSignedFileJson = async (queryUrl, data, config = {}) => {
const signedUrl = await buildHashedQueryUrl(queryUrl);
return requestJson({
method: "post",
url: signedUrl,
data,
...config
});
};
+33
View File
@@ -0,0 +1,33 @@
const encodeRouteParam = (value) => encodeURIComponent(String(value));
export const buildFileQuery = (path, params = {}, options = {}) => {
const { encode = false } = options;
const entries = Object.entries(params).filter(([, value]) => {
return value !== undefined && value !== null;
});
if (entries.length === 0) {
return path;
}
const query = entries
.map(([key, value]) => {
if (!encode) {
return `${key}=${String(value)}`;
}
return `${encodeRouteParam(key)}=${encodeRouteParam(value)}`;
})
.join("&");
return `${path}?${query}`;
};
export const withBaseUrl = (baseUrl, route) => {
return `${baseUrl}${route}`;
};
export const appendQuerySuffix = (route, suffix = "") => {
return `${route}${suffix}`;
};
+4
View File
@@ -0,0 +1,4 @@
export * from "./relayClient";
export * from "./endpointClient";
export * from "./fileClient";
export * from "./fileRouteBuilder";
+22
View File
@@ -0,0 +1,22 @@
import axios from "axios";
import { hashAPIPath } from "../core/hash";
export const buildHashedQueryUrl = async (queryUrl) => {
try {
const signRes = await axios.get(
"/api/endpoint/gethash_api?path=" + encodeURIComponent(queryUrl)
);
if (!signRes?.data?.hash) {
throw new Error("Hash signature unavailable");
}
return queryUrl + signRes.data.hash;
} catch (error) {
if (typeof window === "undefined" && process.env.HASHKEY) {
return queryUrl + hashAPIPath(queryUrl);
}
throw error;
}
};
+15 -16
View File
@@ -1,4 +1,4 @@
import axios from "axios"; import { requestJson } from "../clients/endpointClient";
import { ACCESS_TOKEN_ENDPOINT, TENANT_ID } from "./env"; import { ACCESS_TOKEN_ENDPOINT, TENANT_ID } from "./env";
import { consoleLogger } from "./logger"; import { consoleLogger } from "./logger";
@@ -23,20 +23,19 @@ const tokenConfig = {
} }
}; };
export const getToken = () => { export const getToken = async () => {
return axios try {
.post( const data = await requestJson({
`${ACCESS_TOKEN_ENDPOINT}${TENANT_ID}/oauth2/v2.0/token`, method: "post",
tokenBody, url: `${ACCESS_TOKEN_ENDPOINT}${TENANT_ID}/oauth2/v2.0/token`,
tokenConfig data: tokenBody,
) ...tokenConfig
.then((res) => res.data)
.then((data) => {
cache.tokenResponse = data;
return data;
})
.catch((error) => {
consoleLogger(error);
return error;
}); });
cache.tokenResponse = data;
return data;
} catch (error) {
consoleLogger(error);
return error;
}
}; };
+1
View File
@@ -5,4 +5,5 @@ export * from "./core/token";
export * from "./core/headers"; export * from "./core/headers";
export * from "./core/guards"; export * from "./core/guards";
export * from "./clients";
export * from "./services"; export * from "./services";
+45 -100
View File
@@ -1,55 +1,25 @@
import axios from "axios";
import { BASE_URL } from "../core/env"; import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger"; import { consoleLogger } from "../core/logger";
import { hashAPIPath } from "../core/hash"; import { buildHashedQueryUrl } from "../clients/relayClient";
import { getJson, requestJson } from "../clients/endpointClient";
const buildHashedQueryUrl = async (queryUrl) => {
try {
const signRes = await axios.get(
"/api/endpoint/gethash_api?path=" + encodeURIComponent(queryUrl)
);
if (!signRes?.data?.hash) {
throw new Error("Hash signature unavailable");
}
return queryUrl + signRes.data.hash;
} catch (error) {
if (typeof window === "undefined" && process.env.HASHKEY) {
return queryUrl + hashAPIPath(queryUrl);
}
throw error;
}
};
export const getPersonalAccount = (contactid) => { export const getPersonalAccount = (contactid) => {
return axios return getJson(
.get( BASE_URL + "/api/endpoint/getpersonalaccount_api?contactid=" + contactid
BASE_URL + ).catch((error) => {
"/api/endpoint/getpersonalaccount_api?contactid=" + consoleLogger(error);
contactid });
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const getLogin = (emailAddress, pwd) => { export const getLogin = (emailAddress, pwd) => {
return axios return getJson(
.get( "/api/endpoint/getlogin_api?emailAddress=" +
"/api/endpoint/getlogin_api?emailAddress=" + emailAddress +
emailAddress + "&pwd=" +
"&pwd=" + pwd
pwd ).catch((error) => {
) consoleLogger(error);
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error);
});
}; };
export const updatePassword = (contactId, newpassword) => { export const updatePassword = (contactId, newpassword) => {
@@ -61,13 +31,9 @@ export const updatePassword = (contactId, newpassword) => {
data: data data: data
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const updateAccount = async (contactId, updateBody, ssr) => { export const updateAccount = async (contactId, updateBody, ssr) => {
@@ -81,13 +47,9 @@ export const updateAccount = async (contactId, updateBody, ssr) => {
url: queryUrl, url: queryUrl,
data: data data: data
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const createAccount = (formValues) => { export const createAccount = (formValues) => {
@@ -99,67 +61,50 @@ export const createAccount = (formValues) => {
data: data data: data
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const getEmailAccountCheck = (emailAddress) => { export const getEmailAccountCheck = (emailAddress) => {
return axios return getJson(
.get( "/api/endpoint/getemailaccountcheck_api?emailAddress=" + emailAddress
"/api/endpoint/getemailaccountcheck_api?emailAddress=" + ).catch((error) => {
emailAddress consoleLogger(error);
) });
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
}; };
export const getPortalLogin = async (emailAddress) => { export const getPortalLogin = async (emailAddress) => {
var queryUrl = var queryUrl =
"/api/endpoint/getportallogin_api?emailAddress=" + emailAddress; "/api/endpoint/getportallogin_api?emailAddress=" + emailAddress;
return axios return getJson(BASE_URL + (await buildHashedQueryUrl(queryUrl))).catch(
.get(BASE_URL + (await buildHashedQueryUrl(queryUrl))) (error) => {
.then((res) => res.data)
.catch((error) => {
consoleLogger(error); consoleLogger(error);
return JSON.stringify(error); return JSON.stringify(error);
}); }
);
}; };
export const getPortalLoginProxy = async (emailAddress) => { export const getPortalLoginProxy = async (emailAddress) => {
var queryUrl = var queryUrl =
"/api/endpoint/getportalloginproxy_api?emailAddress=" + emailAddress; "/api/endpoint/getportalloginproxy_api?emailAddress=" + emailAddress;
return axios return getJson(queryUrl).catch((error) => {
.get(queryUrl) consoleLogger(error);
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
return JSON.stringify(error); return JSON.stringify(error);
}); });
}; };
export const getPreferredLanguage = async (email) => { export const getPreferredLanguage = async (email) => {
return axios return getJson(
.get( BASE_URL +
BASE_URL + "/api/endpoint/getpreferredlanguage_api?emailAddress=" +
"/api/endpoint/getpreferredlanguage_api?emailAddress=" + email
email ).catch((error) => {
) consoleLogger(error);
.then((res) => { return error.response;
return res.data; });
})
.catch((error) => {
consoleLogger(error);
return error.response;
});
}; };
+14 -17
View File
@@ -1,11 +1,10 @@
import axios from "axios";
import { BASE_URL } from "../core/env"; import { BASE_URL } from "../core/env";
import { logAndReturnResponse } from "./httpServiceUtils"; import { logAndReturnResponse } from "./httpServiceUtils";
import { getJson } from "../clients/endpointClient";
export const getNewAppeals = async (searchString) => { export const getNewAppeals = async (searchString) => {
try { try {
const res = await axios.get(BASE_URL + "/api/admin/getnewappeals_api"); return await getJson(BASE_URL + "/api/admin/getnewappeals_api");
return res.data;
} catch (error) { } catch (error) {
return logAndReturnResponse(error); return logAndReturnResponse(error);
} }
@@ -18,8 +17,8 @@ export const getNewAppealsPage = async (
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords
) => { ) => {
return axios try {
.get( return await getJson(
"/api/admin/getnewappeals_api?searchString=" + "/api/admin/getnewappeals_api?searchString=" +
searchString + searchString +
"&pageNumber=" + "&pageNumber=" +
@@ -30,11 +29,10 @@ export const getNewAppealsPage = async (
fieldSort + fieldSort +
"&showNumberOfRecords=" + "&showNumberOfRecords=" +
showNumberOfRecords showNumberOfRecords
) );
.then((res) => { } catch (error) {
return res.data; return logAndReturnResponse(error);
}) }
.catch(logAndReturnResponse);
}; };
export const getNewDocumentsPaged = async ( export const getNewDocumentsPaged = async (
@@ -46,8 +44,8 @@ export const getNewDocumentsPaged = async (
documentOrigin, documentOrigin,
selectedWeeks selectedWeeks
) => { ) => {
return axios try {
.get( return await getJson(
"/api/admin/getlatestdocuments_api?pageNumber=" + "/api/admin/getlatestdocuments_api?pageNumber=" +
pageNumber + pageNumber +
"&orderby=" + "&orderby=" +
@@ -62,9 +60,8 @@ export const getNewDocumentsPaged = async (
selectedWeeks + selectedWeeks +
"&documentOrigin=" + "&documentOrigin=" +
documentOrigin documentOrigin
) );
.then((res) => { } catch (error) {
return res.data; return logAndReturnResponse(error);
}) }
.catch(logAndReturnResponse);
}; };
+126 -194
View File
@@ -1,70 +1,55 @@
import axios from "axios";
import { BASE_URL } from "../core/env"; import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger"; import { consoleLogger } from "../core/logger";
import { logAndReturnResponse } from "./httpServiceUtils"; import { logAndReturnResponse } from "./httpServiceUtils";
import { getJson, requestJson } from "../clients/endpointClient";
import { buildFileQuery, withBaseUrl } from "../clients/fileRouteBuilder";
export const getCaseMessage = (searchString) => { export const getCaseMessage = (searchString) => {
return axios const route = buildFileQuery("/api/endpoint/getcasemessage_api", {
.get(BASE_URL + "/api/endpoint/getcasemessage_api?id=" + searchString) id: searchString
.then((res) => { });
return res.data;
}) return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
.catch(logAndReturnResponse);
}; };
export const getIncidentbyID = (searchString) => { export const getIncidentbyID = (searchString) => {
return axios const route = buildFileQuery("/api/endpoint/getincidentbyid_api", {
.get( searchString
BASE_URL + });
"/api/endpoint/getincidentbyid_api?searchString=" +
searchString return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
}; };
export const getIsPublishedbyID = (searchString) => { export const getIsPublishedbyID = (searchString) => {
return axios const route = buildFileQuery("/api/endpoint/getispublishedbyid_api", {
.get( searchString
"/api/endpoint/getispublishedbyid_api?searchString=" + searchString });
)
.then((res) => { return getJson(route).catch(logAndReturnResponse);
return res.data;
})
.catch(logAndReturnResponse);
}; };
export const getPartSavedAppeal = (searchString) => { export const getPartSavedAppeal = (searchString) => {
return axios const route = buildFileQuery("/api/endpoint/getpartsavedappeal_api", {
.get( searchString
BASE_URL + });
"/api/endpoint/getpartsavedappeal_api?searchString=" +
searchString return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
}; };
export const getSIPSEvents = async (caseid) => { export const getSIPSEvents = async (caseid) => {
return axios const route = buildFileQuery("/api/endpoint/getsipsevents_api", {
.get(BASE_URL + "/api/endpoint/getsipsevents_api?caseid=" + caseid) caseid
.then((res) => { });
return res.data;
}) return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
.catch(logAndReturnResponse);
}; };
export const getSIPSMedia = async (caseid) => { export const getSIPSMedia = async (caseid) => {
return axios const route = buildFileQuery("/api/endpoint/getsipsmedia_api", {
.get(BASE_URL + "/api/endpoint/getsipsmedia_api?caseid=" + caseid) caseid
.then((res) => { });
return res.data;
}) return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
.catch(logAndReturnResponse);
}; };
export const getAppealID = ( export const getAppealID = (
@@ -72,17 +57,15 @@ export const getAppealID = (
updateFormCollection, updateFormCollection,
primaryAttribute primaryAttribute
) => { ) => {
return axios const route = buildFileQuery("/api/endpoint/getappealid_api", {
.get( updateFormCollection,
"/api/endpoint/getappealid_api?updateFormCollection=" + primaryAttribute,
updateFormCollection + caseReference
"&primaryAttribute=" + });
primaryAttribute +
"&caseReference=" + return getJson(route)
caseReference .then((data) => {
) const result = Object.entries(data.value[0]).filter(
.then((res) => {
const result = Object.entries(res.data.value[0]).filter(
([key]) => !key.startsWith("_") ([key]) => !key.startsWith("_")
)[0][1]; )[0][1];
var appealID = result; var appealID = result;
@@ -104,29 +87,22 @@ export const createNewCase = (
var data = createBody; var data = createBody;
var queryUrl = var queryUrl = buildFileQuery("/api/endpoint/createcase_api", {
"/api/endpoint/createcase_api?appealTypeId=" + appealTypeId,
appealTypeId + lpaID,
"&lpaID=" + contactid,
lpaID + containername: containerName
"&contactid=" + });
contactid +
"&containername=" +
containerName;
var config = { var config = {
method: "post", method: "post",
url: BASE_URL + queryUrl, url: withBaseUrl(BASE_URL, queryUrl),
data: data data: data
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const createNewCaseBlob = ( export const createNewCaseBlob = (
@@ -144,15 +120,12 @@ export const createNewCaseBlob = (
data.pinswg_lpaname = lpaName; data.pinswg_lpaname = lpaName;
data.createdon = new Date(); data.createdon = new Date();
var queryUrl = var queryUrl = buildFileQuery("/api/file/createcase_api", {
"/api/file/createcase_api?appealTypeId=" + appealTypeId,
appealTypeId + lpaID,
"&lpaID=" + contactid,
lpaID + containername: containerName
"&contactid=" + });
contactid +
"&containername=" +
containerName;
var config = { var config = {
method: "post", method: "post",
@@ -160,13 +133,9 @@ export const createNewCaseBlob = (
data: data data: data
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const updateCase = async ( export const updateCase = async (
@@ -184,11 +153,10 @@ export const updateCase = async (
primaryAttribute primaryAttribute
); );
var queryUrl = var queryUrl = buildFileQuery("/api/endpoint/updatecase_api", {
"/api/endpoint/updatecase_api?updateFormCollection=" + updateFormCollection,
updateFormCollection + appealObj
"&appealObj=" + });
appealObj;
var config = { var config = {
method: "post", method: "post",
@@ -196,13 +164,9 @@ export const updateCase = async (
data: updateBody data: updateBody
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const updateCaseBlob = async ( export const updateCaseBlob = async (
@@ -220,11 +184,10 @@ export const updateCaseBlob = async (
primaryAttribute primaryAttribute
); );
var queryUrl = var queryUrl = buildFileQuery("/api/file/updatecase_api", {
"/api/file/updatecase_api?updateFormCollection=" + updateFormCollection,
updateFormCollection + appealObj
"&appealObj=" + });
appealObj;
var config = { var config = {
method: "post", method: "post",
@@ -232,113 +195,82 @@ export const updateCaseBlob = async (
data: updateBody data: updateBody
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const patchCase = async (incidentid) => { export const patchCase = async (incidentid) => {
var queryUrl = "/api/endpoint/patchcase_api?incidentid=" + incidentid; var queryUrl = buildFileQuery("/api/endpoint/patchcase_api", {
var config = { incidentid
method: "get", });
url: queryUrl return getJson(queryUrl).catch((error) => {
}; //console.log("this error:", error);
});
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
//console.log("this error:", error);
});
}; };
export const getCase = (incidentID) => { export const getCase = (incidentID) => {
return axios const route = buildFileQuery("/api/endpoint/getcase_api", {
.get(BASE_URL + "/api/endpoint/getcase_api?incidentID=" + incidentID) incidentID
.then((res) => { });
return res.data;
}) return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
.catch((error) => { consoleLogger(error);
consoleLogger(error); });
});
}; };
export const getCaseByID = (incidentID) => { export const getCaseByID = (incidentID) => {
return axios const route = buildFileQuery("/api/endpoint/getcasebyid_api", {
.get( incidentID
BASE_URL + "/api/endpoint/getcasebyid_api?incidentID=" + incidentID });
)
.then((res) => { return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
return res.data; consoleLogger(error);
}) });
.catch((error) => {
consoleLogger(error);
});
}; };
export const getAppealPDFDocs = (incidentID) => { export const getAppealPDFDocs = (incidentID) => {
return axios const route = buildFileQuery("/api/endpoint/getappealpdfdocuments_api", {
.get( incidentid: incidentID
BASE_URL + });
"/api/endpoint/getappealpdfdocuments_api?incidentid=" +
incidentID return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
) consoleLogger(error);
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error);
});
}; };
export const getAppealPDFDocument = async (incidentid) => { export const getAppealPDFDocument = async (incidentid) => {
return axios const route = buildFileQuery("/api/endpoint/getappealpdfdocuments_api", {
.get( incidentid
BASE_URL + });
"/api/endpoint/getappealpdfdocuments_api?incidentid=" +
incidentid return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
) consoleLogger(error);
.then((res) => { return error.response;
return res.data; });
})
.catch((error) => {
consoleLogger(error);
return error.response;
});
}; };
export const getPortalModuleDetails = async (appealType, caseReference) => { export const getPortalModuleDetails = async (appealType, caseReference) => {
var config = { const route = buildFileQuery("/api/endpoint/getportalmoduledetails_api", {
method: "get", appealType,
url: caseReference: encodeURI(caseReference)
BASE_URL + });
"/api/endpoint/getportalmoduledetails_api?appealType=" +
appealType +
"&caseReference=" +
encodeURI(caseReference)
};
try { return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
const res = await axios(config);
return res.data;
} catch (error) {
consoleLogger(error); consoleLogger(error);
} });
}; };
export const getPortalModuleDetailsProxy = (appealType, caseReference) => { export const getPortalModuleDetailsProxy = (appealType, caseReference) => {
return axios const route = buildFileQuery(
.get( "/api/endpoint/getportalmoduledetailsproxy_api",
"/api/endpoint/getportalmoduledetailsproxy_api?appealType=" + {
appealType + appealType,
"&caseReference=" + caseReference: caseReference.replace(/\'/g, "''")
caseReference.replace(/\'/g, "''") }
) );
.then((res) => res.data)
.catch((error) => { return getJson(route).catch((error) => {
consoleLogger(error); consoleLogger(error);
}); });
}; };
+170 -282
View File
@@ -1,110 +1,88 @@
import axios from "axios";
import { BASE_URL } from "../core/env"; import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger"; import { consoleLogger } from "../core/logger";
import { hashAPIPath } from "../core/hash"; import { hashAPIPath } from "../core/hash";
import {
const buildHashedQueryUrl = async (queryUrl) => { buildFileQuery,
try { withBaseUrl,
const signRes = await axios.get( appendQuerySuffix
"/api/endpoint/gethash_api?path=" + encodeURIComponent(queryUrl) } from "../clients/fileRouteBuilder";
); import {
getFileJson,
if (!signRes?.data?.hash) { getSignedFileJson,
throw new Error("Hash signature unavailable"); downloadFileBlob,
} postSignedFileJson
} from "../clients/fileClient";
return queryUrl + signRes.data.hash;
} catch (error) {
if (typeof window === "undefined" && process.env.HASHKEY) {
return queryUrl + hashAPIPath(queryUrl);
}
throw error;
}
};
export const getAwaitingSubmissionFromBlob = (containerName) => { export const getAwaitingSubmissionFromBlob = (containerName) => {
return axios const route = buildFileQuery("/api/file/getawaitingsubmissionfromblob", {
.get( container: containerName
BASE_URL + });
"/api/file/getawaitingsubmissionfromblob?container=" +
containerName + return getFileJson(
hashAPIPath( withBaseUrl(BASE_URL, appendQuerySuffix(route, hashAPIPath(route)))
"/api/file/getawaitingsubmissionfromblob?container=" + ).catch((error) => {
containerName consoleLogger(error);
) });
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const getRepsFromBlob = (containerName) => { export const getRepsFromBlob = (containerName) => {
return axios const route = buildFileQuery("/api/file/getrepsblob", {
.get( container: containerName
BASE_URL + });
"/api/file/getrepsblob?container=" +
containerName + return getFileJson(
hashAPIPath("/api/file/getrepsblob?container=" + containerName) withBaseUrl(BASE_URL, appendQuerySuffix(route, hashAPIPath(route)))
) ).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const getRepsFromBlobProxy = async (containerName) => { export const getRepsFromBlobProxy = async (containerName) => {
try { const route = buildFileQuery("/api/file/getrepsblobproxy", {
const res = await axios.get( container: containerName
"/api/file/getrepsblobproxy?container=" + containerName });
);
return res.data; return getFileJson(route).catch((error) => {
} catch (error) {
consoleLogger(error); consoleLogger(error);
} });
}; };
export const getAwaitingSubmissionFromBlobProxy = async (containerName) => { export const getAwaitingSubmissionFromBlobProxy = async (containerName) => {
try { const route = buildFileQuery(
const res = await axios.get( "/api/file/getawaitingsubmissionfromblobproxy",
BASE_URL + {
"/api/file/getawaitingsubmissionfromblobproxy?container=" + container: containerName
containerName }
); );
return res.data;
} catch (error) { return getFileJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error); consoleLogger(error);
} });
};
export const getFilesFromBlobproxy = (containerName, casefolderID) => {
const route = buildFileQuery("/api/file/getbloblistproxy", {
container: containerName,
casefolderID
});
return getFileJson(route).catch((error) => {
consoleLogger(error);
});
}; };
export const deleteAwaitingSubmissionsFromBlob = ( export const deleteAwaitingSubmissionsFromBlob = (
containerID, containerID,
casefolderID casefolderID
) => { ) => {
var queryUrl = var queryUrl = buildFileQuery("/api/file/deleteblobcase", {
"/api/file/deleteblobcase?container=" + container: containerID,
containerID + casefolderID
"&casefolderID=" + });
casefolderID;
return buildHashedQueryUrl(queryUrl) return getSignedFileJson(queryUrl).catch((error) => {
.then((signedUrl) => consoleLogger(error);
axios({ });
method: "get",
url: signedUrl
})
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const deleteMyRepresentationsFromBlob = ( export const deleteMyRepresentationsFromBlob = (
@@ -112,27 +90,15 @@ export const deleteMyRepresentationsFromBlob = (
casefolderID, casefolderID,
repfile repfile
) => { ) => {
var queryUrl = var queryUrl = buildFileQuery("/api/file/deleteblobrep", {
"/api/file/deleteblobrep?container=" + container: containerID,
containerID + casefolderID,
"&casefolderID=" + repfile
casefolderID + });
"&repfile=" +
repfile;
return buildHashedQueryUrl(queryUrl) return getSignedFileJson(queryUrl).catch((error) => {
.then((signedUrl) => consoleLogger(error);
axios({ });
method: "get",
url: signedUrl
})
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const uploadFiles = async ( export const uploadFiles = async (
@@ -156,18 +122,10 @@ export const uploadFiles = async (
var queryUrl = "/api/file/upload"; var queryUrl = "/api/file/upload";
const hashedUrl = await buildHashedQueryUrl(queryUrl);
const config = {
method: "post",
url: hashedUrl,
data: formData,
headers: { "content-type": "multipart/form-data" }
};
try { try {
const res = await axios(config); return await postSignedFileJson(queryUrl, formData, {
return res.data; headers: { "content-type": "multipart/form-data" }
});
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
} }
@@ -186,18 +144,10 @@ export const uploadSingleFile = async (filesObj, containerID, casefolderID) => {
var queryUrl = "/api/file/uploadsinglefile"; var queryUrl = "/api/file/uploadsinglefile";
const hashedUrl = await buildHashedQueryUrl(queryUrl);
const config = {
method: "post",
url: hashedUrl,
data: formData,
headers: { "content-type": "multipart/form-data" }
};
try { try {
const res = await axios(config); return await postSignedFileJson(queryUrl, formData, {
return res.data; headers: { "content-type": "multipart/form-data" }
});
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
} }
@@ -217,18 +167,10 @@ export const uploadRepFiles = async (
var queryUrl = "/api/file/upload"; var queryUrl = "/api/file/upload";
const hashedUrl = await buildHashedQueryUrl(queryUrl);
const config = {
method: "post",
url: hashedUrl,
data: formData,
headers: { "content-type": "multipart/form-data" }
};
try { try {
const res = await axios(config); return await postSignedFileJson(queryUrl, formData, {
return res.data; headers: { "content-type": "multipart/form-data" }
});
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
} }
@@ -243,18 +185,10 @@ export const generateRepPDF = async (
var queryUrl = var queryUrl =
"/api/file/generatepdf" + (options.download ? "?download=true" : ""); "/api/file/generatepdf" + (options.download ? "?download=true" : "");
const hashedUrl = await buildHashedQueryUrl(queryUrl);
const config = {
method: "post",
url: hashedUrl,
data: formValues,
...(options.download ? { responseType: "blob" } : {})
};
try { try {
const res = await axios(config); return await postSignedFileJson(queryUrl, formValues, {
return res.data; ...(options.download ? { responseType: "blob" } : {})
});
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
} }
@@ -277,60 +211,26 @@ export const generateAppealPDF = async (
appealType + appealType +
(options.download ? "&download=true" : ""); (options.download ? "&download=true" : "");
const hashedUrl = await buildHashedQueryUrl(queryUrl);
const config = {
method: "post",
url: hashedUrl,
data: formValues,
...(options.download ? { responseType: "blob" } : {})
};
try { try {
const res = await axios(config); return await postSignedFileJson(queryUrl, formValues, {
return res.data; ...(options.download ? { responseType: "blob" } : {})
});
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
} }
}; };
export const getFilesFromBlob = (containerName, casefolderID) => { export const getFilesFromBlob = (containerName, casefolderID) => {
return axios const route = buildFileQuery("/api/file/getbloblist", {
.get( container: containerName,
BASE_URL + casefolderID
"/api/file/getbloblist?container=" + });
containerName +
"&casefolderID=" +
casefolderID +
hashAPIPath(
"/api/file/getbloblist?container=" +
containerName +
"&casefolderID=" +
casefolderID
)
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
};
export const getFilesFromBlobproxy = (containerName, casefolderID) => { return getFileJson(
return axios withBaseUrl(BASE_URL, appendQuerySuffix(route, hashAPIPath(route)))
.get( ).catch((error) => {
"/api/file/getbloblistproxy?container=" + consoleLogger(error);
containerName + });
"&casefolderID=" +
casefolderID
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const getFilesFromBlobHashed = ( export const getFilesFromBlobHashed = (
@@ -338,20 +238,16 @@ export const getFilesFromBlobHashed = (
getblobshash, getblobshash,
casefolderID casefolderID
) => { ) => {
return axios const route = buildFileQuery("/api/file/getbloblist", {
.get( container: containerName,
"/api/file/getbloblist?container=" + casefolderID
containerName + });
"&casefolderID=" +
casefolderID + return getFileJson(appendQuerySuffix(route, getblobshash)).catch(
getblobshash (error) => {
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error); consoleLogger(error);
}); }
);
}; };
export const deleteBlob = async ( export const deleteBlob = async (
@@ -360,20 +256,23 @@ export const deleteBlob = async (
deleteblobhash, deleteblobhash,
casefolderID casefolderID
) => { ) => {
try { const route = buildFileQuery(
const res = await axios.get( "/api/file/deleteblob",
"/api/file/deleteblob?container=" + {
containerName + container: containerName,
"&casefolderID=" + casefolderID,
encodeURIComponent(casefolderID) + blobname: blobName
"&blobname=" + },
encodeURIComponent(blobName) + {
deleteblobhash encode: true
); }
return res.data; );
} catch (error) {
consoleLogger(error); return getFileJson(appendQuerySuffix(route, deleteblobhash)).catch(
} (error) => {
consoleLogger(error);
}
);
}; };
export const deleteRepBlob = async ( export const deleteRepBlob = async (
@@ -383,78 +282,67 @@ export const deleteRepBlob = async (
casefolderID, casefolderID,
filenamePrefix filenamePrefix
) => { ) => {
try { const route = buildFileQuery(
const res = await axios.get( "/api/file/deleteblob",
"/api/file/deleteblob?container=" + {
containerName + container: containerName,
"&casefolderID=" + casefolderID: casefolderID + "/" + filenamePrefix,
encodeURIComponent(casefolderID + "/" + filenamePrefix) + blobname: blobName
"&blobname=" + },
encodeURIComponent(blobName) + {
deleteblobhash encode: true
); }
return res.data; );
} catch (error) {
consoleLogger(error); return getFileJson(appendQuerySuffix(route, deleteblobhash)).catch(
} (error) => {
consoleLogger(error);
}
);
}; };
export const downloadBlob = (containerName, blobName) => { export const downloadBlob = (containerName, blobName) => {
return axios const queryUrl = withBaseUrl(
.get( BASE_URL,
BASE_URL + buildFileQuery("/api/file/downloadblob", {
"/api/file/downloadblob?container=" + container: containerName.toLowerCase(),
containerName.toLowerCase() + blobname: blobName
"&blobname=" +
blobName,
{ responseType: "blob" }
)
.then((response) => {
res.setHeader(
"content-disposition",
"attachment; filename=" + blobName
);
return res.status(200).send(response.data);
}) })
.catch((error) => { );
consoleLogger(error);
}); return downloadFileBlob(queryUrl).catch((error) => {
consoleLogger(error);
});
}; };
export const getProgressFromBlob = async (containerName, casereference) => { export const getProgressFromBlob = async (containerName, casereference) => {
try { const route = buildFileQuery(
const res = await axios.get( "/api/file/getprogressobjblob",
BASE_URL + {
"/api/file/getprogressobjblob?container=" + container: containerName,
encodeURIComponent(containerName) + casefolderID: casereference
"&casefolderID=" + },
encodeURIComponent(casereference) + {
hashAPIPath( encode: true
"/api/file/getprogressobjblob?container=" + }
encodeURIComponent(containerName) + );
"&casefolderID=" +
encodeURIComponent(casereference) return getFileJson(
) withBaseUrl(BASE_URL, appendQuerySuffix(route, hashAPIPath(route)))
); ).catch((error) => {
return res.data;
} catch (error) {
consoleLogger(error); consoleLogger(error);
} });
}; };
export const createContainerProxy = (containerName) => { export const createContainerProxy = (containerName) => {
var queryUrl = var route = buildFileQuery("/api/file/setupcontainer", {
"/api/file/setupcontainer?ident=" + ident: containerName
containerName + });
hashAPIPath("/api/file/setupcontainer?ident=" + containerName); var queryUrl = appendQuerySuffix(route, hashAPIPath(route));
return axios return getFileJson(withBaseUrl(BASE_URL, queryUrl)).catch((error) => {
.get(BASE_URL + queryUrl) consoleLogger(error);
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
return JSON.stringify(error); return JSON.stringify(error);
}); });
}; };
+2 -3
View File
@@ -1,5 +1,5 @@
import axios from "axios";
import { consoleLogger } from "../core/logger"; import { consoleLogger } from "../core/logger";
import { requestJson } from "../clients/endpointClient";
export const createCRMTask = async (formValues) => { export const createCRMTask = async (formValues) => {
var queryUrl = "/api/endpoint/createcrmtask_api"; var queryUrl = "/api/endpoint/createcrmtask_api";
@@ -12,8 +12,7 @@ export const createCRMTask = async (formValues) => {
}; };
try { try {
const res = await axios(config); return await requestJson(config);
return res.data;
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
} }
+6 -3
View File
@@ -1,5 +1,5 @@
import axios from "axios";
import { consoleLogger } from "../core/logger"; import { consoleLogger } from "../core/logger";
import { requestJson } from "../clients/endpointClient";
export const sendEmail = async ( export const sendEmail = async (
templateId, templateId,
@@ -15,8 +15,11 @@ export const sendEmail = async (
}; };
try { try {
const response = await axios.post("/api/email/notify", mailData); return await requestJson({
return response.data; method: "post",
url: "/api/email/notify",
data: mailData
});
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
throw error; throw error;
+152 -201
View File
@@ -1,167 +1,132 @@
import axios from "axios";
import { BASE_URL } from "../core/env"; import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger"; import { consoleLogger } from "../core/logger";
import { hashAPIPath } from "../core/hash"; import { buildHashedQueryUrl } from "../clients/relayClient";
import { getJson, requestJson } from "../clients/endpointClient";
const buildHashedQueryUrl = async (queryUrl) => { import {
try { buildFileQuery,
const signRes = await axios.get( withBaseUrl,
"/api/endpoint/gethash_api?path=" + encodeURIComponent(queryUrl) appendQuerySuffix
); } from "../clients/fileRouteBuilder";
if (!signRes?.data?.hash) {
throw new Error("Hash signature unavailable");
}
return queryUrl + signRes.data.hash;
} catch (error) {
if (typeof window === "undefined" && process.env.HASHKEY) {
return queryUrl + hashAPIPath(queryUrl);
}
throw error;
}
};
export const getMyCases = (loggedInUserId) => { export const getMyCases = (loggedInUserId) => {
return axios const route = buildFileQuery("/api/endpoint/getmycases_api", {
.get( loggedInUserId
BASE_URL + });
"/api/endpoint/getmycases_api?loggedInUserId=" +
loggedInUserId return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
) consoleLogger(error);
.then((res) => { });
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const getMyInvolvements = async (loggedInUserId) => { export const getMyInvolvements = async (loggedInUserId) => {
try { const route = buildFileQuery("/api/endpoint/getmyinvolvements_api", {
const res = await axios.get( loggedInUserId
BASE_URL + });
"/api/endpoint/getmyinvolvements_api?loggedInUserId=" +
loggedInUserId return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
);
return res.data;
} catch (error) {
consoleLogger(error); consoleLogger(error);
} });
}; };
export const getMyLPACases = (lpaid) => { export const getMyLPACases = (lpaid) => {
return axios const route = buildFileQuery("/api/endpoint/getmylpacases_api", {
.get(BASE_URL + "/api/endpoint/getmylpacases_api?lpaid=" + lpaid) lpaid
.then((res) => { });
return res.data;
}) return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
.catch((error) => { consoleLogger(error);
consoleLogger(error); });
});
}; };
export const getMyRepresentations = (loggedInUserId) => { export const getMyRepresentations = (loggedInUserId) => {
return axios const route = buildFileQuery("/api/endpoint/getmyrepresentations_api", {
.get( loggedInUserId
BASE_URL + });
"/api/endpoint/getmyrepresentations_api?loggedInUserId=" +
loggedInUserId return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
) consoleLogger(error);
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error);
});
}; };
export const getMyRepresentationsProxy = (loggedInUserId) => { export const getMyRepresentationsProxy = (loggedInUserId) => {
return axios const route = buildFileQuery(
.get( "/api/endpoint/getmyrepresentationsproxy_api",
"/api/endpoint/getmyrepresentationsproxy_api?loggedInUserId=" + {
loggedInUserId loggedInUserId
) }
.then((res) => res.data) );
.catch((error) => {
consoleLogger(error); return getJson(route).catch((error) => {
}); consoleLogger(error);
});
}; };
export const getRepresentations = (incidentID) => { export const getRepresentations = (incidentID) => {
return axios const route = buildFileQuery("/api/endpoint/getrepresentations_api", {
.get("/api/endpoint/getrepresentations_api?incidentID=" + incidentID) incidentID
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error); return getJson(route).catch((error) => {
}); consoleLogger(error);
});
}; };
export const getRepresentationsProxy = (incidentID) => { export const getRepresentationsProxy = (incidentID) => {
return axios const route = buildFileQuery("/api/endpoint/getrepresentationsproxy_api", {
.get( incidentID
"/api/endpoint/getrepresentationsproxy_api?incidentID=" + incidentID });
)
.then((res) => res.data) return getJson(route).catch((error) => {
.catch((error) => { consoleLogger(error);
consoleLogger(error); });
});
}; };
export const getWatchedCases = (loggedInUserId) => { export const getWatchedCases = (loggedInUserId) => {
return axios const route = buildFileQuery("/api/endpoint/getwatchedcases_api", {
.get( loggedInUserId
BASE_URL + });
"/api/endpoint/getwatchedcases_api?loggedInUserId=" +
loggedInUserId return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
) consoleLogger(error);
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error);
});
}; };
export const getWatchedCasesProxy = (loggedInUserId) => { export const getWatchedCasesProxy = (loggedInUserId) => {
return axios const route = buildFileQuery("/api/endpoint/getwatchedcasesproxy_api", {
.get( loggedInUserId
"/api/endpoint/getwatchedcasesproxy_api?loggedInUserId=" + });
loggedInUserId
) return getJson(route).catch((error) => {
.then((res) => res.data) consoleLogger(error);
.catch((error) => { });
consoleLogger(error);
});
}; };
export const getAwaitingSubmissionProxy = (loggedInUserId) => { export const getAwaitingSubmissionProxy = (loggedInUserId) => {
return axios const route = buildFileQuery(
.get( "/api/endpoint/getawaitingsubmissionproxy_api",
"/api/endpoint/getawaitingsubmissionproxy_api?loggedInUserId=" + {
loggedInUserId loggedInUserId
) }
.then((res) => res.data) );
.catch((error) => {
consoleLogger(error); return getJson(route).catch((error) => {
}); consoleLogger(error);
});
}; };
export const getAwaitingSubmission = (loggedInUserId) => { export const getAwaitingSubmission = (loggedInUserId) => {
return axios const route = buildFileQuery("/api/endpoint/getawaitingsubmission_api", {
.get( loggedInUserId
BASE_URL + });
"/api/endpoint/getawaitingsubmission_api?loggedInUserId=" +
loggedInUserId return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
) consoleLogger(error);
.then((res) => { });
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const createWatchedCases = async (formValues) => { export const createWatchedCases = async (formValues) => {
var data = formValues; var data = formValues;
var queryUrl = "/api/endpoint/createwatchedcases_api"; var queryUrl = buildFileQuery("/api/endpoint/createwatchedcases_api");
var config = { var config = {
method: "post", method: "post",
@@ -170,21 +135,20 @@ export const createWatchedCases = async (formValues) => {
}; };
try { try {
const res = await axios(config); return await requestJson(config);
return res.data;
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
} }
}; };
export const deleteMyRepresentations = (myRepresentationsID) => { export const deleteMyRepresentations = (myRepresentationsID) => {
var queryUrl = var queryUrl = buildFileQuery("/api/endpoint/deletemyrepresentations_api", {
"/api/endpoint/deletemyrepresentations_api?myRepresentationsID=" + myRepresentationsID
myRepresentationsID; });
return buildHashedQueryUrl(queryUrl) return buildHashedQueryUrl(queryUrl)
.then((signedUrl) => .then((signedUrl) =>
axios({ requestJson({
method: "delete", method: "delete",
url: signedUrl, url: signedUrl,
headers: { headers: {
@@ -197,46 +161,41 @@ export const deleteMyRepresentations = (myRepresentationsID) => {
} }
}) })
) )
.then((res) => {
return res.data;
})
.catch((error) => { .catch((error) => {
consoleLogger(error); consoleLogger(error);
}); });
}; };
export const deleteAwaitingSubmissions = (incidentID) => { export const deleteAwaitingSubmissions = (incidentID) => {
var queryUrl = var queryUrl = buildFileQuery(
"/api/endpoint/deleteawaitingsubmissions_api?incidentID=" + incidentID; "/api/endpoint/deleteawaitingsubmissions_api",
{
incidentID
}
);
var config = { var config = {
method: "delete", method: "delete",
url: queryUrl url: queryUrl
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const deleteWatchedCases = async (watchedCaseID) => { export const deleteWatchedCases = async (watchedCaseID) => {
var queryUrl = var queryUrl = buildFileQuery("/api/endpoint/deletewatchedcases_api", {
"/api/endpoint/deletewatchedcases_api?watchedCaseID=" + watchedCaseID; watchedCaseID
});
return buildHashedQueryUrl(queryUrl) return buildHashedQueryUrl(queryUrl)
.then((signedUrl) => .then((signedUrl) =>
axios({ requestJson({
method: "delete", method: "delete",
url: signedUrl url: signedUrl
}) })
) )
.then((res) => {
return res.data;
})
.catch((error) => { .catch((error) => {
consoleLogger(error); consoleLogger(error);
}); });
@@ -247,60 +206,57 @@ export const sendCaseCompleteMessage = async (
caseReference, caseReference,
inv inv
) => { ) => {
var hashQueryPath = var hashQueryPath = buildFileQuery(
"/api/file/createappealcompletemessage_api?container=" + "/api/file/createappealcompletemessage_api",
containerID + {
"&tempcaseref=" + container: containerID,
caseReference; tempcaseref: caseReference
}
);
var queryUrl = var queryUrl = buildFileQuery("/api/file/createappealcompletemessage_api", {
"/api/file/createappealcompletemessage_api?container=" + container: containerID,
containerID + tempcaseref: caseReference,
"&tempcaseref=" + inv
caseReference + });
"&inv=" +
inv;
var signedQueryUrl = await buildHashedQueryUrl(hashQueryPath); var signedQueryUrl = await buildHashedQueryUrl(hashQueryPath);
queryUrl = queryUrl + signedQueryUrl.replace(hashQueryPath, ""); queryUrl = appendQuerySuffix(
queryUrl,
signedQueryUrl.replace(hashQueryPath, "")
);
var config = { var config = {
method: "get", method: "get",
url: queryUrl url: queryUrl
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const sendCaseCompleteMessageProxy = async ( export const sendCaseCompleteMessageProxy = async (
containerID, containerID,
caseReference caseReference
) => { ) => {
var queryUrl = var queryUrl = buildFileQuery(
"/api/file/createappealcompletemessageproxy_api?container=" + "/api/file/createappealcompletemessageproxy_api",
containerID + {
"&tempcaseref=" + container: containerID,
caseReference; tempcaseref: caseReference
}
);
var config = { var config = {
method: "get", method: "get",
url: queryUrl url: queryUrl
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const sendRepCompleteMessage = async ( export const sendRepCompleteMessage = async (
@@ -308,13 +264,14 @@ export const sendRepCompleteMessage = async (
caseReference, caseReference,
fileName fileName
) => { ) => {
var hashQueryPath = var hashQueryPath = buildFileQuery(
"/api/file/createrepcompletemessage_api?container=" + "/api/file/createrepcompletemessage_api",
containerID + {
"&tempcaseref=" + container: containerID,
caseReference + tempcaseref: caseReference,
"&repid=" + repid: fileName
fileName; }
);
var queryUrl = await buildHashedQueryUrl(hashQueryPath); var queryUrl = await buildHashedQueryUrl(hashQueryPath);
@@ -323,17 +280,13 @@ export const sendRepCompleteMessage = async (
url: queryUrl url: queryUrl
}; };
return axios(config) return requestJson(config).catch((error) => {
.then((res) => { consoleLogger(error);
return res.data; });
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const setRepInvolvment = async (caseid, contactid) => { export const setRepInvolvment = async (caseid, contactid) => {
var queryUrl = "/api/file/createrepinvolvement_api"; var queryUrl = buildFileQuery("/api/file/createrepinvolvement_api");
var data = { "incidentid": caseid, "contactid": contactid }; var data = { "incidentid": caseid, "contactid": contactid };
var config = { var config = {
@@ -343,15 +296,14 @@ export const setRepInvolvment = async (caseid, contactid) => {
}; };
try { try {
const res = await axios(config); return await requestJson(config);
return res.data;
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
} }
}; };
export const setCaseInvolvment = async (caseid, contactid) => { export const setCaseInvolvment = async (caseid, contactid) => {
var queryUrl = "/api/file/createrepinvolvement_api"; var queryUrl = buildFileQuery("/api/file/createrepinvolvement_api");
var data = { "incidentid": caseid, "contactid": contactid }; var data = { "incidentid": caseid, "contactid": contactid };
var config = { var config = {
@@ -361,8 +313,7 @@ export const setCaseInvolvment = async (caseid, contactid) => {
}; };
try { try {
const res = await axios(config); return await requestJson(config);
return res.data;
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
} }
+31 -49
View File
@@ -1,76 +1,58 @@
import axios from "axios";
import { BASE_URL } from "../core/env"; import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger"; import { consoleLogger } from "../core/logger";
import { logAndReturnEmptyValueErrorResponse } from "./httpServiceUtils"; import { logAndReturnEmptyValueErrorResponse } from "./httpServiceUtils";
import { getJson } from "../clients/endpointClient";
export const getAppealsTypes = () => { export const getAppealsTypes = () => {
return axios return getJson(BASE_URL + "/api/endpoint/getappealtypes_api").catch(
.get(BASE_URL + "/api/endpoint/getappealtypes_api") logAndReturnEmptyValueErrorResponse
.then((res) => res.data) );
.catch(logAndReturnEmptyValueErrorResponse);
}; };
export const getProjectTypes = () => { export const getProjectTypes = () => {
return axios return getJson(BASE_URL + "/api/endpoint/getprojecttypes_api").catch(
.get(BASE_URL + "/api/endpoint/getprojecttypes_api") logAndReturnEmptyValueErrorResponse
.then((res) => res.data) );
.catch(logAndReturnEmptyValueErrorResponse);
}; };
export const getAppealsTypesForNewAppeal = () => { export const getAppealsTypesForNewAppeal = () => {
return axios return getJson(
.get(BASE_URL + "/api/endpoint/getappealtypesfornewappeal_api") BASE_URL + "/api/endpoint/getappealtypesfornewappeal_api"
.then((res) => res.data) ).catch(logAndReturnEmptyValueErrorResponse);
.catch(logAndReturnEmptyValueErrorResponse);
}; };
export const getLPA = () => { export const getLPA = () => {
return axios return getJson(BASE_URL + "/api/endpoint/getlpa_api").catch(
.get(BASE_URL + "/api/endpoint/getlpa_api") logAndReturnEmptyValueErrorResponse
.then((res) => { );
return res.data;
})
.catch(logAndReturnEmptyValueErrorResponse);
}; };
export const getFormData = (whichForm) => { export const getFormData = (whichForm) => {
return axios return getJson(
.get(BASE_URL + "/api/endpoint/getformdata_api?whichForm=" + whichForm) BASE_URL + "/api/endpoint/getformdata_api?whichForm=" + whichForm
.then((res) => res.data) ).catch((error) => {
.catch((error) => { consoleLogger(error);
consoleLogger(error); });
});
}; };
export const getMandatoryFields = (whichForm) => { export const getMandatoryFields = (whichForm) => {
return axios return getJson(
.get( BASE_URL + "/api/endpoint/getmandatoryfields_api?whichForm=" + whichForm
BASE_URL + ).catch((error) => {
"/api/endpoint/getmandatoryfields_api?whichForm=" + consoleLogger(error);
whichForm });
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
}; };
export const getPickLists = (whichForm) => { export const getPickLists = (whichForm) => {
return axios return getJson(
.get(BASE_URL + "/api/endpoint/getpicklists_api?whichForm=" + whichForm) BASE_URL + "/api/endpoint/getpicklists_api?whichForm=" + whichForm
.then((res) => res.data) ).catch((error) => {
.catch((error) => { consoleLogger(error);
consoleLogger(error); });
});
}; };
export const getNotice = () => { export const getNotice = () => {
return axios return getJson(BASE_URL + "/api/notices").catch((error) => {
.get(BASE_URL + "/api/notices") consoleLogger(error);
.then((res) => { });
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
}; };
+128 -193
View File
@@ -1,35 +1,25 @@
import axios from "axios";
import { BASE_URL } from "../core/env"; import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger"; import { consoleLogger } from "../core/logger";
import { getJson } from "../clients/endpointClient";
import { import {
logAndReturnResponse, logAndReturnResponse,
logAndReturnEmptyValueErrorResponse logAndReturnEmptyValueErrorResponse
} from "./httpServiceUtils"; } from "./httpServiceUtils";
export const getBasicSearch = (searchString) => { export const getBasicSearch = (searchString) => {
return axios return getJson(
.get( BASE_URL +
BASE_URL + "/api/endpoint/getbasicsearch_api?searchString=" +
"/api/endpoint/getbasicsearch_api?searchString=" + searchString
searchString ).catch(logAndReturnResponse);
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
}; };
export const getBasicDNSURLSearch = (searchString) => { export const getBasicDNSURLSearch = (searchString) => {
return axios return getJson(
.get( BASE_URL +
BASE_URL + "/api/endpoint/getbasicdnsurlsearch_api?searchString=" +
"/api/endpoint/getbasicdnsurlsearch_api?searchString=" + searchString
searchString ).catch(logAndReturnResponse);
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
}; };
export const getAddressSearchPaged = ( export const getAddressSearchPaged = (
@@ -39,23 +29,18 @@ export const getAddressSearchPaged = (
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords
) => { ) => {
return axios return getJson(
.get( "/api/endpoint/getbasicsearch_by_address_paged_api?searchString=" +
"/api/endpoint/getbasicsearch_by_address_paged_api?searchString=" + searchString +
searchString + "&pageNumber=" +
"&pageNumber=" + pageNumber +
pageNumber + "&orderby=" +
"&orderby=" + orderBy +
orderBy + "&fieldSort=" +
"&fieldSort=" + fieldSort +
fieldSort + "&showNumberOfRecords=" +
"&showNumberOfRecords=" + showNumberOfRecords
showNumberOfRecords ).catch(logAndReturnResponse);
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
}; };
export const getBasicSearchPaged = ( export const getBasicSearchPaged = (
@@ -65,46 +50,36 @@ export const getBasicSearchPaged = (
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords
) => { ) => {
return axios return getJson(
.get( "/api/endpoint/getbasicsearchpaged_api?searchString=" +
"/api/endpoint/getbasicsearchpaged_api?searchString=" + searchString +
searchString + "&pageNumber=" +
"&pageNumber=" + pageNumber +
pageNumber + "&orderby=" +
"&orderby=" + orderBy +
orderBy + "&fieldSort=" +
"&fieldSort=" + fieldSort +
fieldSort + "&showNumberOfRecords=" +
"&showNumberOfRecords=" + showNumberOfRecords
showNumberOfRecords ).catch(logAndReturnResponse);
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
}; };
export const getDNSCoords = () => { export const getDNSCoords = () => {
return axios return getJson(BASE_URL + "/api/endpoint/getdnscoords_api").catch(
.get(BASE_URL + "/api/endpoint/getdnscoords_api") logAndReturnEmptyValueErrorResponse
.then((res) => { );
return res.data;
})
.catch(logAndReturnEmptyValueErrorResponse);
}; };
export const getDNSList = (searchString) => { export const getDNSList = (searchString) => {
return axios return getJson(BASE_URL + "/api/endpoint/getdnslist_api").catch(
.get(BASE_URL + "/api/endpoint/getdnslist_api") logAndReturnResponse
.then((res) => res.data) );
.catch(logAndReturnResponse);
}; };
export const getBasicDNSSearch = (searchString) => { export const getBasicDNSSearch = (searchString) => {
return axios return getJson(BASE_URL + "/api/endpoint/getbasicdnssearch_api").catch(
.get(BASE_URL + "/api/endpoint/getbasicdnssearch_api") logAndReturnResponse
.then((res) => res.data) );
.catch(logAndReturnResponse);
}; };
export const getBasicDNSSearchPaged = ( export const getBasicDNSSearchPaged = (
@@ -113,30 +88,24 @@ export const getBasicDNSSearchPaged = (
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords
) => { ) => {
return axios return getJson(
.get( "/api/endpoint/getbasicdnssearchpaged_api?pageNumber=" +
"/api/endpoint/getbasicdnssearchpaged_api?pageNumber=" + pageNumber +
pageNumber + "&orderby=" +
"&orderby=" + orderBy +
orderBy + "&fieldSort=" +
"&fieldSort=" + fieldSort +
fieldSort + "&showNumberOfRecords=" +
"&showNumberOfRecords=" + showNumberOfRecords
showNumberOfRecords ).catch(logAndReturnResponse);
)
.then((res) => res.data)
.catch(logAndReturnResponse);
}; };
export const getAdvancedSearch = (searchString) => { export const getAdvancedSearch = (searchString) => {
return axios return getJson(
.get( BASE_URL +
BASE_URL + "/api/endpoint/getadvancedsearch_api?searchstring=" +
"/api/endpoint/getadvancedsearch_api?searchstring=" + JSON.stringify(searchString)
JSON.stringify(searchString) ).catch(logAndReturnEmptyValueErrorResponse);
)
.then((res) => res.data)
.catch(logAndReturnEmptyValueErrorResponse);
}; };
export const getAdvancedSearchPaged = ( export const getAdvancedSearchPaged = (
@@ -146,21 +115,18 @@ export const getAdvancedSearchPaged = (
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords
) => { ) => {
return axios return getJson(
.get( "/api/endpoint/getadvancedsearchpaged_api?searchstring=" +
"/api/endpoint/getadvancedsearchpaged_api?searchstring=" + JSON.stringify(searchString) +
JSON.stringify(searchString) + "&pageNumber=" +
"&pageNumber=" + pageNumber +
pageNumber + "&orderby=" +
"&orderby=" + orderBy +
orderBy + "&fieldSort=" +
"&fieldSort=" + fieldSort +
fieldSort + "&showNumberOfRecords=" +
"&showNumberOfRecords=" + showNumberOfRecords
showNumberOfRecords ).catch(logAndReturnEmptyValueErrorResponse);
)
.then((res) => res.data)
.catch(logAndReturnEmptyValueErrorResponse);
}; };
export const getBasicSearchDetails = async ( export const getBasicSearchDetails = async (
@@ -169,20 +135,17 @@ export const getBasicSearchDetails = async (
primaryIdAttribute, primaryIdAttribute,
incidentID incidentID
) => { ) => {
return axios return getJson(
.get( BASE_URL +
BASE_URL + "/api/endpoint/getbasicsearchdetails_api?appealTypeName=" +
"/api/endpoint/getbasicsearchdetails_api?appealTypeName=" + appealTypeName +
appealTypeName + "&primaryIdAttribute=" +
"&primaryIdAttribute=" + primaryIdAttribute +
primaryIdAttribute + "&incidentID=" +
"&incidentID=" + incidentID
incidentID ).catch((error) => {
) consoleLogger(error);
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error);
});
}; };
export const getBasicPartSavedDetails = ( export const getBasicPartSavedDetails = (
@@ -191,20 +154,17 @@ export const getBasicPartSavedDetails = (
primaryIdAttribute, primaryIdAttribute,
incidentID incidentID
) => { ) => {
return axios return getJson(
.get( BASE_URL +
BASE_URL + "/api/endpoint/getbasicpartsaveddetails_api?appealTypeName=" +
"/api/endpoint/getbasicpartsaveddetails_api?appealTypeName=" + appealTypeName +
appealTypeName + "&primaryIdAttribute=" +
"&primaryIdAttribute=" + primaryIdAttribute +
primaryIdAttribute + "&incidentID=" +
"&incidentID=" + incidentID
incidentID ).catch((error) => {
) consoleLogger(error);
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error);
});
}; };
export const getBasicSearchDetailsPaged = async ( export const getBasicSearchDetailsPaged = async (
@@ -231,8 +191,8 @@ export const getBasicSearchDetailsPaged = async (
)}`; )}`;
try { try {
const res = await axios.get(url); const data = await getJson(url);
return res.data.value || []; return data.value || [];
} catch (error) { } catch (error) {
consoleLogger(error); consoleLogger(error);
return []; return [];
@@ -240,27 +200,20 @@ export const getBasicSearchDetailsPaged = async (
}; };
export const getSearchDocumentDetails = (incidentID) => { export const getSearchDocumentDetails = (incidentID) => {
return axios return getJson(
.get( "/api/endpoint/getsearchdocumentdetails_api?incidentid=" + incidentID
"/api/endpoint/getsearchdocumentdetails_api?incidentid=" + ).catch((error) => {
incidentID consoleLogger(error);
) throw error;
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error);
throw error;
});
}; };
export const getSearchDocumentTypes = (incidentID) => { export const getSearchDocumentTypes = (incidentID) => {
return axios return getJson(
.get( "/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID
"/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID ).catch((error) => {
) consoleLogger(error);
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error);
});
}; };
export const getSearchDocumentDetailsPaged = ( export const getSearchDocumentDetailsPaged = (
@@ -271,39 +224,30 @@ export const getSearchDocumentDetailsPaged = (
showNumberOfRecords, showNumberOfRecords,
documentType documentType
) => { ) => {
return axios return getJson(
.get( "/api/endpoint/getsearchdocumentdetailspaged_api?incidentid=" +
"/api/endpoint/getsearchdocumentdetailspaged_api?incidentid=" + incidentID +
incidentID + "&pageNumber=" +
"&pageNumber=" + pageNumber +
pageNumber + "&orderby=" +
"&orderby=" + orderBy +
orderBy + "&fieldSort=" +
"&fieldSort=" + fieldSort +
fieldSort + "&showNumberOfRecords=" +
"&showNumberOfRecords=" + showNumberOfRecords +
showNumberOfRecords + "&documentType=" +
"&documentType=" + documentType
documentType ).catch((error) => {
) consoleLogger(error);
.then((res) => res.data) });
.catch((error) => {
consoleLogger(error);
});
}; };
export const getLinkedCases = (parentIncidentid) => { export const getLinkedCases = (parentIncidentid) => {
return axios return getJson(
.get( "/api/endpoint/getlinkedcases_api?parentincidentid=" + parentIncidentid
"/api/endpoint/getlinkedcases_api?parentincidentid=" + ).catch((error) => {
parentIncidentid consoleLogger(error);
) });
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
}; };
export const getAddressSearch = async (searchString) => { export const getAddressSearch = async (searchString) => {
@@ -313,14 +257,5 @@ export const getAddressSearch = async (searchString) => {
var queryUrl = var queryUrl =
BASE_URL + "/api/endpoint/getbasicsearch_by_address_api?" + str; BASE_URL + "/api/endpoint/getbasicsearch_by_address_api?" + str;
var config = { return getJson(queryUrl).catch(logAndReturnEmptyValueErrorResponse);
method: "get",
url: queryUrl
};
return axios(config)
.then((res) => {
return res.data;
})
.catch(logAndReturnEmptyValueErrorResponse);
}; };
+55
View File
@@ -75,3 +75,58 @@ Guidance:
- Do not treat these files as active runtime architecture unless explicitly reactivated. - Do not treat these files as active runtime architecture unless explicitly reactivated.
- If reactivation is proposed, document rationale and rollout/rollback in `memory-bank/change-log.md` and `context/runbook.md`. - If reactivation is proposed, document rationale and rollout/rollback in `memory-bank/change-log.md` and `context/runbook.md`.
## Current State Assessment and Prioritised Next Steps (2026-03-25)
### Assessment summary
The platform has moved into a stronger operational and architectural posture through sustained bounded refactor slices and contract hardening.
Strengths:
1. **Governance maturity is high**
- Guardrails are explicit for auth/session integrity, CSP/security headers, Prisma source-of-truth, relay hash integrity, and EN/CY parity.
2. **API reliability posture has improved materially**
- Endpoint contract hardening and phase21 contract test expansion have reduced inconsistency in negative-path handling.
3. **Relay operations are significantly more robust**
- Shared relay forwarding now includes bounded retry/timeout policy, structured redacted lifecycle logging, and documented rollout/rollback controls.
4. **Façade decomposition is delivering low-risk progress**
- `actions` layer migration to shared clients (`relayClient`, `endpointClient`) is reducing duplicated request boilerplate and lowering drift risk.
Primary residual risks/gaps:
1. **Remaining direct-service inconsistency**
- Some direct services still contain legacy axios/request patterns and bespoke signed-request blocks.
2. **Coverage concentration**
- Contract tests are strong in targeted slices, but end-to-end/high-value journey coverage in sensitive flows remains comparatively sparse.
3. **Logging hygiene variance**
- Structured redaction exists in relay paths, but broader codebase logging still has uneven consistency.
4. **i18n parity assurance remains process-heavy**
- EN/CY parity relies heavily on manual discipline rather than automated parity checks.
### Prioritised next steps
1. **Complete direct-service consistency sweep (low risk, high maintainability)**
- Prioritise `actions/services/searchDirectService.js` for `getJson`/`requestJson` adoption in bounded slices.
- Preserve existing error-return behavior contracts per function.
2. **Consolidate signed-request patterns (medium risk, high security clarity)**
- Introduce a focused signed-request helper for hash-based/signed delete/get pathways currently repeated in service modules.
- Keep existing hash/header semantics unchanged while reducing duplication.
3. **Add high-value regression automation (high value)**
- Add focused automated checks for:
- auth callback/redirect safety
- one signed-delete negative path
- one upload/document authorization negative path
- one EN/CY route parity check
4. **Perform targeted logging hardening in sensitive paths**
- Continue replacing direct/verbose logging in `auth`, `file`, `email`, and account-sensitive endpoint paths with redacted structured logging patterns.
5. **Introduce EN/CY parity CI checks**
- Add automated checks for route rewrite parity and locale key alignment to reduce drift and manual burden.
6. **Continue endpoint sprawl reduction**
- Keep collapsing duplicated proxy/request patterns behind shared helpers in bounded route clusters while preserving public response contracts.
### Recommended execution sequence
- **Sequence A (immediate):** Step 1 + Step 3 (fastest risk reduction per effort)
- **Sequence B (next):** Step 2 + Step 4 (security/logging consistency consolidation)
- **Sequence C (after):** Step 5 + Step 6 (institutionalise parity and reduce long-tail maintenance cost)
File diff suppressed because it is too large Load Diff
+167
View File
@@ -0,0 +1,167 @@
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
const loadAuthInternals = () => {
const filePath = path.join(
__dirname,
"..",
"..",
"pages",
"api",
"auth",
"[...nextauth].js"
);
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
source = source.replace(
/export default NextAuthPEDW;\s*$/,
"module.exports = { appendParamsAndPathToNewUrl, resolveLocale, authOptions, NextAuthPEDW };"
);
const context = {
module: { exports: {} },
exports: {},
require: (id) => {
if (id === "notifications-node-client") {
return {
NotifyClient: function NotifyClient() {
return {
sendEmail: async () => ({})
};
}
};
}
throw new Error(`Unexpected require in auth test: ${id}`);
},
URL,
URLSearchParams,
process: {
env: {
NEXTAUTH_URL: "https://english.example",
CY_API_ROOT: "https://welsh.example",
NEXTAUTH_SECRET: "test-secret"
}
},
PrismaAdapter: () => ({}),
PrismaClient: function PrismaClient() {
return {};
},
NextAuth: () => ({}),
EmailProvider: () => ({}),
consoleLogger: () => {},
console: {
log: () => {},
info: () => {},
warn: () => {},
error: () => {}
}
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
test("auth/resolveLocale prefers query then body then cookie then default", async () => {
const mod = loadAuthInternals();
assert.strictEqual(
mod.resolveLocale({
query: { locale: "cy" },
body: { locale: "en" },
cookies: { pedw_locale: "en" }
}),
"cy"
);
assert.strictEqual(
mod.resolveLocale({
body: { locale: "cy" },
cookies: { pedw_locale: "en" }
}),
"cy"
);
assert.strictEqual(
mod.resolveLocale({
cookies: { pedw_locale: "cy" }
}),
"cy"
);
assert.strictEqual(mod.resolveLocale({}), "en");
});
test("auth/redirect callback keeps relative URLs on same base", async () => {
const mod = loadAuthInternals();
const req = { query: { locale: "en" }, body: {}, cookies: {} };
const options = mod.authOptions(req, {});
const result = options.callbacks.redirect({
url: "/account/register",
baseUrl: "https://pedw.example"
});
assert.strictEqual(result, "https://pedw.example/account/register");
});
test("auth/redirect callback keeps same-origin absolute URLs unchanged", async () => {
const mod = loadAuthInternals();
const req = { query: { locale: "en" }, body: {}, cookies: {} };
const options = mod.authOptions(req, {});
const result = options.callbacks.redirect({
url: "https://pedw.example/auth/verify-request?token=abc",
baseUrl: "https://pedw.example"
});
assert.strictEqual(
result,
"https://pedw.example/auth/verify-request?token=abc"
);
});
test("auth/redirect callback rewrites external URL to locale-safe base origin", async () => {
const mod = loadAuthInternals();
const req = { query: { locale: "cy" }, body: {}, cookies: {} };
const options = mod.authOptions(req, {});
const result = options.callbacks.redirect({
url: "https://malicious.example/auth/signin?callbackUrl=%2Fdashboard&token=abc",
baseUrl: "https://pedw.example"
});
const parsed = new URL(result);
assert.strictEqual(parsed.origin, "https://welsh.example");
assert.strictEqual(parsed.pathname, "/auth/signin");
assert.strictEqual(parsed.searchParams.get("callbackUrl"), "/dashboard");
assert.strictEqual(parsed.searchParams.get("token"), "abc");
});
const run = async () => {
let passed = 0;
for (const currentTest of tests) {
await currentTest.fn();
passed += 1;
}
console.log(
`Phase 22 auth-redirect tests passed (${passed}/${tests.length}).`
);
};
module.exports = run;
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}
@@ -0,0 +1,130 @@
const assert = require("assert");
const {
createAxiosMock,
createLoggerMock,
createAxiosError,
loadServiceModule,
normalize
} = require("../serviceHarness.cjs");
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
test("case/getPortalModuleDetails uses BASE_URL route and encoded case reference", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
axios.getHandler = async () => ({ data: { value: [] } });
const caseService = loadServiceModule("caseDirectService.js", {
axios,
BASE_URL: "http://example.local",
consoleLogger: logger.consoleLogger
});
const result = await caseService.getPortalModuleDetails(
"appeal",
"REF A/B"
);
assert.deepStrictEqual(normalize(result), { value: [] });
assert.strictEqual(
axios.calls[0].url,
"http://example.local/api/endpoint/getportalmoduledetails_api?appealType=appeal&caseReference=REF%20A/B"
);
});
test("case/getPortalModuleDetailsProxy escapes apostrophes in case reference", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
axios.getHandler = async () => ({ data: { value: [] } });
const caseService = loadServiceModule("caseDirectService.js", {
axios,
BASE_URL: "",
consoleLogger: logger.consoleLogger
});
const result = await caseService.getPortalModuleDetailsProxy(
"appeal",
"REF'O"
);
assert.deepStrictEqual(normalize(result), { value: [] });
assert.strictEqual(
axios.calls[0].url,
"/api/endpoint/getportalmoduledetailsproxy_api?appealType=appeal&caseReference=REF''O"
);
});
test("case/getAppealID composes lookup route and extracts first non-underscore value", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
axios.getHandler = async () => ({
data: {
value: [
{
_internal: "ignore",
pinswg_appeal: "appeal-123"
}
]
}
});
const caseService = loadServiceModule("caseDirectService.js", {
axios,
BASE_URL: "",
consoleLogger: logger.consoleLogger
});
const result = await caseService.getAppealID("REF-1", "pinswg_cases", "id");
assert.strictEqual(result, "appeal-123");
assert.strictEqual(
axios.calls[0].url,
"/api/endpoint/getappealid_api?updateFormCollection=pinswg_cases&primaryAttribute=id&caseReference=REF-1"
);
});
test("case/getAppealPDFDocument logs and returns error.response on failure", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
const error = createAxiosError(404, "Not Found");
axios.getHandler = async () => Promise.reject(error);
const caseService = loadServiceModule("caseDirectService.js", {
axios,
BASE_URL: "http://example.local",
consoleLogger: logger.consoleLogger
});
const result = await caseService.getAppealPDFDocument("inc-123");
assert.strictEqual(result, error.response);
assert.strictEqual(logger.calls.length, 1);
});
const run = async () => {
let passed = 0;
for (const currentTest of tests) {
await currentTest.fn();
passed += 1;
}
console.log(
`Phase 22 case-service tests passed (${passed}/${tests.length}).`
);
};
module.exports = run;
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}
@@ -0,0 +1,236 @@
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const assert = require("assert");
const rootDir = path.resolve(__dirname, "..", "..");
const loadEndpointClientModule = (injected = {}) => {
const filePath = path.join(
rootDir,
"actions",
"clients",
"endpointClient.js"
);
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
source = source.replace(/export const\s+/g, "const ");
source += "\nmodule.exports = { getJson, requestJson };\n";
const context = {
module: { exports: {} },
exports: {},
require,
axios: () => {
throw new Error("axios not injected");
},
...injected
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const loadRelayClientModule = (injected = {}) => {
const filePath = path.join(rootDir, "actions", "clients", "relayClient.js");
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
source = source.replace(/export const\s+/g, "const ");
source += "\nmodule.exports = { buildHashedQueryUrl };\n";
const context = {
module: { exports: {} },
exports: {},
require,
axios: {
get: async () => {
throw new Error("axios.get not injected");
}
},
hashAPIPath: () => "&hash=fallback",
process: { env: {} },
...injected
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const loadFileRouteBuilderModule = (injected = {}) => {
const filePath = path.join(
rootDir,
"actions",
"clients",
"fileRouteBuilder.js"
);
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
source = source.replace(/export const\s+/g, "const ");
source +=
"\nmodule.exports = { buildFileQuery, withBaseUrl, appendQuerySuffix };\n";
const context = {
module: { exports: {} },
exports: {},
require,
encodeURIComponent,
...injected
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
test("clients/endpointClient getJson returns response.data from axios.get", async () => {
const calls = [];
const axios = {
get: async (url, config) => {
calls.push({ url, config });
return { data: { ok: true } };
}
};
const mod = loadEndpointClientModule({ axios });
const result = await mod.getJson("/x", { headers: { a: 1 } });
assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), { ok: true });
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].url, "/x");
assert.strictEqual(calls[0].config.headers.a, 1);
});
test("clients/endpointClient requestJson returns response.data from axios(config)", async () => {
const calls = [];
const axios = async (config) => {
calls.push(config);
return { data: { saved: true } };
};
const mod = loadEndpointClientModule({ axios });
const result = await mod.requestJson({ method: "post", url: "/y" });
assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), { saved: true });
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].method, "post");
});
test("clients/relayClient buildHashedQueryUrl appends browser hash response", async () => {
const calls = [];
const mod = loadRelayClientModule({
axios: {
get: async (url) => {
calls.push(url);
return { data: { hash: "&hash=abc" } };
}
},
process: { env: {} }
});
const result = await mod.buildHashedQueryUrl("/api/file/deleteblob?x=1");
assert.strictEqual(result, "/api/file/deleteblob?x=1&hash=abc");
assert.strictEqual(calls.length, 1);
assert.strictEqual(
calls[0],
"/api/endpoint/gethash_api?path=%2Fapi%2Ffile%2Fdeleteblob%3Fx%3D1"
);
});
test("clients/relayClient falls back to hashAPIPath on server when HASHKEY present", async () => {
const mod = loadRelayClientModule({
axios: {
get: async () => {
throw new Error("network unavailable");
}
},
hashAPIPath: () => "&hash=server-fallback",
process: { env: { HASHKEY: "secret" } }
});
const result = await mod.buildHashedQueryUrl("/api/file/deleteblob?x=1");
assert.strictEqual(result, "/api/file/deleteblob?x=1&hash=server-fallback");
});
test("clients/relayClient rethrows when browser hash call fails and no HASHKEY", async () => {
const expected = new Error("hash service down");
const mod = loadRelayClientModule({
axios: {
get: async () => {
throw expected;
}
},
process: { env: {} }
});
await assert.rejects(
() => mod.buildHashedQueryUrl("/api/file/deleteblob?x=1"),
(error) => error === expected
);
});
test("clients/fileRouteBuilder builds query with optional encoding and suffix helpers", async () => {
const mod = loadFileRouteBuilderModule();
const unencoded = mod.buildFileQuery("/api/file/getbloblist", {
container: "abc",
casefolderID: "x/y"
});
const encoded = mod.buildFileQuery(
"/api/file/deleteblob",
{
container: "abc",
casefolderID: "x/y",
blobname: "doc one.pdf"
},
{
encode: true
}
);
assert.strictEqual(
unencoded,
"/api/file/getbloblist?container=abc&casefolderID=x/y"
);
assert.strictEqual(
encoded,
"/api/file/deleteblob?container=abc&casefolderID=x%2Fy&blobname=doc%20one.pdf"
);
assert.strictEqual(
mod.withBaseUrl("http://example.local", unencoded),
"http://example.local/api/file/getbloblist?container=abc&casefolderID=x/y"
);
assert.strictEqual(
mod.appendQuerySuffix(unencoded, "&hash=123"),
"/api/file/getbloblist?container=abc&casefolderID=x/y&hash=123"
);
});
const run = async () => {
let passed = 0;
for (const currentTest of tests) {
await currentTest.fn();
passed += 1;
}
console.log(
`Phase 22 client-utils tests passed (${passed}/${tests.length}).`
);
};
module.exports = run;
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}
+114
View File
@@ -0,0 +1,114 @@
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const assert = require("assert");
const rootDir = path.resolve(__dirname, "..", "..");
const loadTokenModule = (injected = {}) => {
const filePath = path.join(rootDir, "actions", "core", "token.js");
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
source = source.replace(/export const\s+/g, "const ");
source += "\nmodule.exports = { getToken };\n";
const context = {
module: { exports: {} },
exports: {},
require,
process: {
env: {
GRANT_TYPE: "client_credentials",
CLIENT_ID: "client-id",
CLIENT_SECRET: "client-secret",
RELAYURI: "relay.local"
}
},
ACCESS_TOKEN_ENDPOINT: "https://login.example/",
TENANT_ID: "tenant-123",
consoleLogger: () => {},
requestJson: async () => {
throw new Error("requestJson not injected");
},
...injected
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
test("core/getToken returns token payload and calls requestJson with expected config", async () => {
const calls = [];
const mod = loadTokenModule({
requestJson: async (config) => {
calls.push(config);
return { access_token: "abc", expires_in: 3600 };
}
});
const result = await mod.getToken();
assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), {
access_token: "abc",
expires_in: 3600
});
assert.strictEqual(calls.length, 1);
assert.strictEqual(
calls[0].url,
"https://login.example/tenant-123/oauth2/v2.0/token"
);
assert.strictEqual(calls[0].method, "post");
assert.strictEqual(
calls[0].data,
"grant_type=client_credentials&client_id=client-id&client_secret=client-secret&scope=https://relay.local/.default"
);
assert.strictEqual(
calls[0].headers["Content-Type"],
"application/x-www-form-urlencoded"
);
});
test("core/getToken logs and returns error object when requestJson throws", async () => {
const loggerCalls = [];
const error = new Error("token failed");
const mod = loadTokenModule({
consoleLogger: (err) => loggerCalls.push(err),
requestJson: async () => {
throw error;
}
});
const result = await mod.getToken();
assert.strictEqual(result, error);
assert.strictEqual(loggerCalls.length, 1);
assert.strictEqual(loggerCalls[0], error);
});
const run = async () => {
let passed = 0;
for (const currentTest of tests) {
await currentTest.fn();
passed += 1;
}
console.log(
`Phase 22 core-token tests passed (${passed}/${tests.length}).`
);
};
module.exports = run;
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}
@@ -0,0 +1,171 @@
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const assert = require("assert");
const rootDir = path.resolve(__dirname, "..", "..");
const loadFileClientModule = (injected = {}) => {
const filePath = path.join(rootDir, "actions", "clients", "fileClient.js");
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
source = source.replace(/export const\s+/g, "const ");
source +=
"\nmodule.exports = { getFileJson, getSignedFileJson, downloadFileBlob, postSignedFileJson };\n";
const context = {
module: { exports: {} },
exports: {},
require,
getJson: async () => {
throw new Error("getJson not injected");
},
requestJson: async () => {
throw new Error("requestJson not injected");
},
buildHashedQueryUrl: async () => {
throw new Error("buildHashedQueryUrl not injected");
},
...injected
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
test("clients/fileClient getFileJson delegates to getJson", async () => {
const calls = [];
const mod = loadFileClientModule({
getJson: async (url) => {
calls.push(url);
return { ok: true };
}
});
const result = await mod.getFileJson("/api/file/getbloblistproxy?x=1");
assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), { ok: true });
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0], "/api/file/getbloblistproxy?x=1");
});
test("clients/fileClient getSignedFileJson signs url and requests json", async () => {
const signedCalls = [];
const requestCalls = [];
const mod = loadFileClientModule({
buildHashedQueryUrl: async (queryUrl) => {
signedCalls.push(queryUrl);
return queryUrl + "&hash=signed";
},
requestJson: async (config) => {
requestCalls.push(config);
return { deleted: true };
}
});
const result = await mod.getSignedFileJson(
"/api/file/deleteblobcase?container=a&casefolderID=b"
);
assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), {
deleted: true
});
assert.strictEqual(signedCalls.length, 1);
assert.strictEqual(requestCalls.length, 1);
assert.strictEqual(requestCalls[0].method, "get");
assert.strictEqual(
requestCalls[0].url,
"/api/file/deleteblobcase?container=a&casefolderID=b&hash=signed"
);
});
test("clients/fileClient downloadFileBlob requests blob response", async () => {
const requestCalls = [];
const mod = loadFileClientModule({
requestJson: async (config) => {
requestCalls.push(config);
return "blob-data";
}
});
const result = await mod.downloadFileBlob("/api/file/downloadblob?x=1");
assert.strictEqual(result, "blob-data");
assert.strictEqual(requestCalls.length, 1);
assert.strictEqual(requestCalls[0].method, "get");
assert.strictEqual(requestCalls[0].responseType, "blob");
assert.strictEqual(requestCalls[0].url, "/api/file/downloadblob?x=1");
});
test("clients/fileClient postSignedFileJson signs url and posts payload", async () => {
const signedCalls = [];
const requestCalls = [];
const mod = loadFileClientModule({
buildHashedQueryUrl: async (queryUrl) => {
signedCalls.push(queryUrl);
return queryUrl + "&hash=signed-post";
},
requestJson: async (config) => {
requestCalls.push(config);
return { uploaded: true };
}
});
const payload = { name: "doc" };
const result = await mod.postSignedFileJson(
"/api/file/uploadsinglefile",
payload,
{
headers: { "content-type": "multipart/form-data" }
}
);
assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), {
uploaded: true
});
assert.strictEqual(signedCalls.length, 1);
assert.strictEqual(requestCalls.length, 1);
assert.strictEqual(requestCalls[0].method, "post");
assert.strictEqual(
requestCalls[0].url,
"/api/file/uploadsinglefile&hash=signed-post"
);
assert.deepStrictEqual(
JSON.parse(JSON.stringify(requestCalls[0].data)),
payload
);
assert.strictEqual(
requestCalls[0].headers["content-type"],
"multipart/form-data"
);
});
const run = async () => {
let passed = 0;
for (const currentTest of tests) {
await currentTest.fn();
passed += 1;
}
console.log(
`Phase 22 file-client tests passed (${passed}/${tests.length}).`
);
};
module.exports = run;
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}
+65
View File
@@ -0,0 +1,65 @@
const assert = require("assert");
const path = require("path");
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
const loadRewrites = async () => {
const configPath = path.join(__dirname, "..", "..", "next.config.js");
// eslint-disable-next-line global-require, import/no-dynamic-require
const nextConfig = require(configPath);
return nextConfig.rewrites();
};
test("i18n/rewrites include required CY aliases for auth and policy routes", async () => {
const rewrites = await loadRewrites();
const requiredPairs = [
{ source: "/awd/mewngofnodi", destination: "/auth/signin" },
{ source: "/awd/mewngofnodi/ebost", destination: "/auth/signin/email" },
{ source: "/awd/gwall", destination: "/auth/error" },
{ source: "/awd/gwirio-cais", destination: "/auth/verify-request" },
{ source: "/preifatrwydd", destination: "/privacy" },
{ source: "/hygyrchedd", destination: "/accessibility" },
{
source: "/telerau-ac-amodau",
destination: "/terms-and-conditions"
}
];
for (const pair of requiredPairs) {
const found = rewrites.some(
(entry) =>
entry.source === pair.source &&
entry.destination === pair.destination
);
assert.strictEqual(
found,
true,
`Missing required CY rewrite ${pair.source} -> ${pair.destination}`
);
}
});
const run = async () => {
let passed = 0;
for (const currentTest of tests) {
await currentTest.fn();
passed += 1;
}
console.log(
`Phase 22 i18n-route tests passed (${passed}/${tests.length}).`
);
};
module.exports = run;
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}
+27
View File
@@ -0,0 +1,27 @@
const runCoreTokenTests = require("./core-token-behaviour.test.cjs");
const runClientUtilsTests = require("./client-utils-behaviour.test.cjs");
const runFileClientTests = require("./file-client-behaviour.test.cjs");
const runCaseServiceTests = require("./case-service-behaviour.test.cjs");
const runPortalServiceTests = require("./portal-service-behaviour.test.cjs");
const runAuthRedirectSafetyTests = require("./auth-redirect-safety.test.cjs");
const runI18nRouteParityTests = require("./i18n-route-parity.test.cjs");
const run = async () => {
await runCoreTokenTests();
await runClientUtilsTests();
await runFileClientTests();
await runCaseServiceTests();
await runPortalServiceTests();
await runAuthRedirectSafetyTests();
await runI18nRouteParityTests();
console.log("Phase 22 combined suite passed.");
};
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}
module.exports = run;
@@ -0,0 +1,157 @@
const assert = require("assert");
const {
createAxiosMock,
createLoggerMock,
createAxiosError,
loadServiceModule,
normalize
} = require("../serviceHarness.cjs");
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
test("portal/sendCaseCompleteMessage appends signed hash suffix to inv query", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
const requestCalls = [];
const requestJson = async (config) => {
requestCalls.push(config);
return { status: "ok" };
};
const portal = loadServiceModule("portalDirectService.js", {
axios,
BASE_URL: "",
consoleLogger: logger.consoleLogger,
buildHashedQueryUrl: async (queryUrl) => `${queryUrl}&hash=abc123`,
requestJson
});
const result = await portal.sendCaseCompleteMessage(
"container1",
"CASE-1",
"yes"
);
assert.deepStrictEqual(normalize(result), { status: "ok" });
assert.strictEqual(requestCalls.length, 1);
assert.strictEqual(requestCalls[0].method, "get");
assert.strictEqual(
requestCalls[0].url,
"/api/file/createappealcompletemessage_api?container=container1&tempcaseref=CASE-1&inv=yes&hash=abc123"
);
});
test("portal/deleteMyRepresentations uses signed delete request with headers", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
const requestCalls = [];
const requestJson = async (config) => {
requestCalls.push(config);
return { deleted: true };
};
const portal = loadServiceModule("portalDirectService.js", {
axios,
BASE_URL: "",
consoleLogger: logger.consoleLogger,
buildHashedQueryUrl: async (queryUrl) => `${queryUrl}&hash=xyz`,
requestJson
});
const result = await portal.deleteMyRepresentations("rep-1");
assert.deepStrictEqual(normalize(result), { deleted: true });
assert.strictEqual(requestCalls.length, 1);
assert.strictEqual(requestCalls[0].method, "delete");
assert.strictEqual(
requestCalls[0].url,
"/api/endpoint/deletemyrepresentations_api?myRepresentationsID=rep-1&hash=xyz"
);
assert.strictEqual(
requestCalls[0].headers["Content-Type"],
"application/json"
);
});
test("portal/createWatchedCases posts to harmonized route helper URL", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
const requestCalls = [];
const requestJson = async (config) => {
requestCalls.push(config);
return { created: true };
};
const portal = loadServiceModule("portalDirectService.js", {
axios,
BASE_URL: "",
consoleLogger: logger.consoleLogger,
requestJson
});
const payload = { foo: "bar" };
const result = await portal.createWatchedCases(payload);
assert.deepStrictEqual(normalize(result), { created: true });
assert.strictEqual(requestCalls.length, 1);
assert.strictEqual(requestCalls[0].method, "post");
assert.strictEqual(
requestCalls[0].url,
"/api/endpoint/createwatchedcases_api"
);
assert.deepStrictEqual(normalize(requestCalls[0].data), payload);
});
test("portal/sendRepCompleteMessage rejects when hash signing fails before request stage", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
const error = createAxiosError(500, "Failed");
const portal = loadServiceModule("portalDirectService.js", {
axios,
BASE_URL: "",
consoleLogger: logger.consoleLogger,
buildHashedQueryUrl: async () => {
throw error;
},
requestJson: async () => {
throw new Error("should not be called");
}
});
await assert.rejects(
() => portal.sendRepCompleteMessage("c", "r", "f"),
(caught) => {
assert.strictEqual(caught, error);
return true;
}
);
assert.strictEqual(logger.calls.length, 0);
});
const run = async () => {
let passed = 0;
for (const currentTest of tests) {
await currentTest.fn();
passed += 1;
}
console.log(
`Phase 22 portal-service tests passed (${passed}/${tests.length}).`
);
};
module.exports = run;
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}
+31 -94
View File
@@ -1,96 +1,11 @@
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const assert = require("assert"); const assert = require("assert");
const {
const rootDir = path.resolve(__dirname, "..", ".."); createAxiosMock,
const servicesDir = path.join(rootDir, "actions", "services"); createLoggerMock,
createAxiosError,
const createAxiosMock = () => { loadServiceModule,
const axios = (config) => axios.request(config); normalize
} = require("../serviceHarness.cjs");
axios.calls = [];
axios.requestHandler = async () => {
throw new Error("No axios.request handler configured");
};
axios.getHandler = async () => {
throw new Error("No axios.get handler configured");
};
axios.postHandler = async () => {
throw new Error("No axios.post handler configured");
};
axios.request = (config) => {
axios.calls.push({ type: "request", config });
return axios.requestHandler(config);
};
axios.get = (url, config) => {
axios.calls.push({ type: "get", url, config });
return axios.getHandler(url, config);
};
axios.post = (url, data, config) => {
axios.calls.push({ type: "post", url, data, config });
return axios.postHandler(url, data, config);
};
return axios;
};
const createLoggerMock = () => {
const calls = [];
const consoleLogger = (error) => {
calls.push(error);
};
return { consoleLogger, calls };
};
const createAxiosError = (status = 500, statusText = "Server Error") => {
return {
response: {
status,
statusText,
data: {}
},
config: {
url: "/mock-url"
}
};
};
const loadServiceModule = (fileName, injected = {}) => {
const filePath = path.join(servicesDir, fileName);
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
const exportNames = Array.from(
source.matchAll(/export const\s+(\w+)\s*=/g)
).map((match) => match[1]);
source = source.replace(/export const\s+/g, "const ");
source += `\nmodule.exports = { ${exportNames.join(", ")} };\n`;
const context = {
module: { exports: {} },
exports: {},
require,
URLSearchParams,
encodeURIComponent,
FormData: global.FormData,
console: {
log: () => {},
info: () => {},
warn: () => {},
error: () => {}
},
...injected
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const tests = []; const tests = [];
@@ -98,8 +13,6 @@ const test = (name, fn) => {
tests.push({ name, fn }); tests.push({ name, fn });
}; };
const normalize = (value) => JSON.parse(JSON.stringify(value));
test("search/getBasicSearch returns res.data on success", async () => { test("search/getBasicSearch returns res.data on success", async () => {
const axios = createAxiosMock(); const axios = createAxiosMock();
const logger = createLoggerMock(); const logger = createLoggerMock();
@@ -225,6 +138,30 @@ test("case/getCaseMessage returns error.response on failure", async () => {
assert.strictEqual(logger.calls.length, 1); assert.strictEqual(logger.calls.length, 1);
}); });
test("case/getPortalModuleDetails composes BASE_URL route with encoded case reference", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
axios.getHandler = async () => ({ data: { value: [] } });
const caseService = loadServiceModule("caseDirectService.js", {
axios,
BASE_URL: "http://example.local",
consoleLogger: logger.consoleLogger
});
const result = await caseService.getPortalModuleDetails(
"appeal",
"REF A/B"
);
assert.deepStrictEqual(normalize(result), { value: [] });
assert.strictEqual(
axios.calls[0].url,
"http://example.local/api/endpoint/getportalmoduledetails_api?appealType=appeal&caseReference=REF%20A/B"
);
});
test("admin/getNewAppealsPage returns res.data on success", async () => { test("admin/getNewAppealsPage returns res.data on success", async () => {
const axios = createAxiosMock(); const axios = createAxiosMock();
const logger = createLoggerMock(); const logger = createLoggerMock();
+42 -98
View File
@@ -1,96 +1,11 @@
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const assert = require("assert"); const assert = require("assert");
const {
const rootDir = path.resolve(__dirname, "..", ".."); createAxiosMock,
const servicesDir = path.join(rootDir, "actions", "services"); createLoggerMock,
createAxiosError,
const createAxiosMock = () => { loadServiceModule,
const axios = (config) => axios.request(config); normalize
} = require("../serviceHarness.cjs");
axios.calls = [];
axios.requestHandler = async () => {
throw new Error("No axios.request handler configured");
};
axios.getHandler = async () => {
throw new Error("No axios.get handler configured");
};
axios.postHandler = async () => {
throw new Error("No axios.post handler configured");
};
axios.request = (config) => {
axios.calls.push({ type: "request", config });
return axios.requestHandler(config);
};
axios.get = (url, config) => {
axios.calls.push({ type: "get", url, config });
return axios.getHandler(url, config);
};
axios.post = (url, data, config) => {
axios.calls.push({ type: "post", url, data, config });
return axios.postHandler(url, data, config);
};
return axios;
};
const createLoggerMock = () => {
const calls = [];
const consoleLogger = (error) => {
calls.push(error);
};
return { consoleLogger, calls };
};
const createAxiosError = (status = 500, statusText = "Server Error") => {
return {
response: {
status,
statusText,
data: {}
},
config: {
url: "/mock-url"
}
};
};
const loadServiceModule = (fileName, injected = {}) => {
const filePath = path.join(servicesDir, fileName);
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
const exportNames = Array.from(
source.matchAll(/export const\s+(\w+)\s*=/g)
).map((match) => match[1]);
source = source.replace(/export const\s+/g, "const ");
source += `\nmodule.exports = { ${exportNames.join(", ")} };\n`;
const context = {
module: { exports: {} },
exports: {},
require,
URLSearchParams,
encodeURIComponent,
FormData: global.FormData,
console: {
log: () => {},
info: () => {},
warn: () => {},
error: () => {}
},
...injected
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const tests = []; const tests = [];
@@ -98,8 +13,6 @@ const test = (name, fn) => {
tests.push({ name, fn }); tests.push({ name, fn });
}; };
const normalize = (value) => JSON.parse(JSON.stringify(value));
test("document/getAwaitingSubmissionFromBlob includes hash token and returns data", async () => { test("document/getAwaitingSubmissionFromBlob includes hash token and returns data", async () => {
const axios = createAxiosMock(); const axios = createAxiosMock();
const logger = createLoggerMock(); const logger = createLoggerMock();
@@ -302,6 +215,36 @@ test("document delete blob flows use signer hash", async () => {
); );
}); });
test("document/downloadBlob delegates blob request config via file client helper", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
axios.requestHandler = async (config) => {
return { data: { type: "blob", url: config.url } };
};
const document = loadServiceModule("documentDirectService.js", {
axios,
BASE_URL: "http://example.local",
consoleLogger: logger.consoleLogger,
hashAPIPath: () => ""
});
const result = await document.downloadBlob(
"MyContainer",
"folder/file.pdf"
);
assert.deepStrictEqual(normalize(result), {
type: "blob",
url: "http://example.local/api/file/downloadblob?container=mycontainer&blobname=folder/file.pdf"
});
assert.strictEqual(axios.calls.length, 1);
assert.strictEqual(axios.calls[0].type, "request");
assert.strictEqual(axios.calls[0].config.method, "get");
assert.strictEqual(axios.calls[0].config.responseType, "blob");
});
test("account/getPortalLogin appends hash and returns res.data", async () => { test("account/getPortalLogin appends hash and returns res.data", async () => {
const axios = createAxiosMock(); const axios = createAxiosMock();
const logger = createLoggerMock(); const logger = createLoggerMock();
@@ -367,7 +310,7 @@ test("notify/sendEmail posts payload and returns response data", async () => {
const axios = createAxiosMock(); const axios = createAxiosMock();
const logger = createLoggerMock(); const logger = createLoggerMock();
axios.postHandler = async () => ({ data: { id: "msg-1" } }); axios.requestHandler = async () => ({ data: { id: "msg-1" } });
const notify = loadServiceModule("notifyDirectService.js", { const notify = loadServiceModule("notifyDirectService.js", {
axios, axios,
@@ -382,8 +325,9 @@ test("notify/sendEmail posts payload and returns response data", async () => {
); );
assert.deepStrictEqual(normalize(result), { id: "msg-1" }); assert.deepStrictEqual(normalize(result), { id: "msg-1" });
assert.strictEqual(axios.calls[0].url, "/api/email/notify"); assert.strictEqual(axios.calls[0].config.url, "/api/email/notify");
assert.deepStrictEqual(normalize(axios.calls[0].data), { assert.strictEqual(axios.calls[0].config.method, "post");
assert.deepStrictEqual(normalize(axios.calls[0].config.data), {
templateId: "template-1", templateId: "template-1",
emailAddress: "person@example.com", emailAddress: "person@example.com",
reference: "ref-123", reference: "ref-123",
@@ -396,7 +340,7 @@ test("notify/sendEmail logs and rethrows on failure", async () => {
const logger = createLoggerMock(); const logger = createLoggerMock();
const error = createAxiosError(429, "Too Many Requests"); const error = createAxiosError(429, "Too Many Requests");
axios.postHandler = async () => Promise.reject(error); axios.requestHandler = async () => Promise.reject(error);
const notify = loadServiceModule("notifyDirectService.js", { const notify = loadServiceModule("notifyDirectService.js", {
axios, axios,
+202
View File
@@ -0,0 +1,202 @@
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const rootDir = path.resolve(__dirname, "..");
const servicesDir = path.join(rootDir, "actions", "services");
const createAxiosMock = () => {
const axios = (config) => axios.request(config);
axios.calls = [];
axios.requestHandler = async () => {
throw new Error("No axios.request handler configured");
};
axios.getHandler = async () => {
throw new Error("No axios.get handler configured");
};
axios.postHandler = async () => {
throw new Error("No axios.post handler configured");
};
axios.request = (config) => {
axios.calls.push({ type: "request", config });
return axios.requestHandler(config);
};
axios.get = (url, config) => {
axios.calls.push({ type: "get", url, config });
return axios.getHandler(url, config);
};
axios.post = (url, data, config) => {
axios.calls.push({ type: "post", url, data, config });
return axios.postHandler(url, data, config);
};
return axios;
};
const createLoggerMock = () => {
const calls = [];
const consoleLogger = (error) => {
calls.push(error);
};
return { consoleLogger, calls };
};
const createAxiosError = (status = 500, statusText = "Server Error") => {
return {
response: {
status,
statusText,
data: {}
},
config: {
url: "/mock-url"
}
};
};
const loadServiceModule = (fileName, injected = {}) => {
const filePath = path.join(servicesDir, fileName);
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
const exportNames = Array.from(
source.matchAll(/export const\s+(\w+)\s*=/g)
).map((match) => match[1]);
source = source.replace(/export const\s+/g, "const ");
source += `\nmodule.exports = { ${exportNames.join(", ")} };\n`;
const defaultGetJson = (url, config) => {
if (!injected.axios || !injected.axios.get) {
throw new Error("Missing axios.get for default getJson mock");
}
return injected.axios
.get(url, config)
.then((response) => response.data);
};
const defaultRequestJson = (config) => {
if (!injected.axios) {
throw new Error("Missing axios for default requestJson mock");
}
return injected.axios(config).then((response) => response.data);
};
const defaultGetFileJson = (url) => {
return defaultGetJson(url);
};
const defaultBuildHashedQueryUrl = async (queryUrl) => {
if (!injected.axios || !injected.axios.get) {
throw new Error(
"Missing axios.get for default buildHashedQueryUrl mock"
);
}
const hashResponse = await injected.axios.get(
"/api/endpoint/gethash_api?path=" + encodeURIComponent(queryUrl)
);
return queryUrl + hashResponse.data.hash;
};
const defaultGetSignedFileJson = async (queryUrl) => {
const hashedUrl = await (
injected.buildHashedQueryUrl || defaultBuildHashedQueryUrl
)(queryUrl);
return (injected.requestJson || defaultRequestJson)({
method: "get",
url: hashedUrl
});
};
const defaultDownloadFileBlob = (url) => {
return (injected.requestJson || defaultRequestJson)({
method: "get",
url,
responseType: "blob"
});
};
const defaultBuildFileQuery = (pathValue, params = {}, options = {}) => {
const { encode = false } = options;
const entries = Object.entries(params).filter(([, value]) => {
return value !== undefined && value !== null;
});
if (entries.length === 0) {
return pathValue;
}
const query = entries
.map(([key, value]) => {
if (!encode) {
return `${key}=${String(value)}`;
}
return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`;
})
.join("&");
return `${pathValue}?${query}`;
};
const defaultWithBaseUrl = (baseUrl, route) => `${baseUrl}${route}`;
const defaultAppendQuerySuffix = (route, suffix = "") =>
`${route}${suffix}`;
const context = {
module: { exports: {} },
exports: {},
require,
URLSearchParams,
encodeURIComponent,
FormData: global.FormData,
console: {
log: () => {},
info: () => {},
warn: () => {},
error: () => {}
},
getJson: injected.getJson || defaultGetJson,
requestJson: injected.requestJson || defaultRequestJson,
getFileJson: injected.getFileJson || defaultGetFileJson,
getSignedFileJson:
injected.getSignedFileJson || defaultGetSignedFileJson,
downloadFileBlob: injected.downloadFileBlob || defaultDownloadFileBlob,
buildHashedQueryUrl:
injected.buildHashedQueryUrl || defaultBuildHashedQueryUrl,
buildFileQuery: injected.buildFileQuery || defaultBuildFileQuery,
withBaseUrl: injected.withBaseUrl || defaultWithBaseUrl,
appendQuerySuffix:
injected.appendQuerySuffix || defaultAppendQuerySuffix,
...injected
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const normalize = (value) => {
if (value === undefined || value === null) {
return value;
}
return JSON.parse(JSON.stringify(value));
};
module.exports = {
createAxiosMock,
createLoggerMock,
createAxiosError,
loadServiceModule,
normalize
};