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
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.
Client-level extraction (`relayClient`, `endpointClient`, `fileClient`, `notifyClient`) is planned for the next increment.
Current extracted clients:
- `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 { consoleLogger } from "./logger";
@@ -23,20 +23,19 @@ const tokenConfig = {
}
};
export const getToken = () => {
return axios
.post(
`${ACCESS_TOKEN_ENDPOINT}${TENANT_ID}/oauth2/v2.0/token`,
tokenBody,
tokenConfig
)
.then((res) => res.data)
.then((data) => {
cache.tokenResponse = data;
return data;
})
.catch((error) => {
consoleLogger(error);
return error;
export const getToken = async () => {
try {
const data = await requestJson({
method: "post",
url: `${ACCESS_TOKEN_ENDPOINT}${TENANT_ID}/oauth2/v2.0/token`,
data: tokenBody,
...tokenConfig
});
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/guards";
export * from "./clients";
export * from "./services";
+45 -100
View File
@@ -1,55 +1,25 @@
import axios from "axios";
import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger";
import { hashAPIPath } from "../core/hash";
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;
}
};
import { buildHashedQueryUrl } from "../clients/relayClient";
import { getJson, requestJson } from "../clients/endpointClient";
export const getPersonalAccount = (contactid) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getpersonalaccount_api?contactid=" +
contactid
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return getJson(
BASE_URL + "/api/endpoint/getpersonalaccount_api?contactid=" + contactid
).catch((error) => {
consoleLogger(error);
});
};
export const getLogin = (emailAddress, pwd) => {
return axios
.get(
"/api/endpoint/getlogin_api?emailAddress=" +
emailAddress +
"&pwd=" +
pwd
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
return getJson(
"/api/endpoint/getlogin_api?emailAddress=" +
emailAddress +
"&pwd=" +
pwd
).catch((error) => {
consoleLogger(error);
});
};
export const updatePassword = (contactId, newpassword) => {
@@ -61,13 +31,9 @@ export const updatePassword = (contactId, newpassword) => {
data: data
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const updateAccount = async (contactId, updateBody, ssr) => {
@@ -81,13 +47,9 @@ export const updateAccount = async (contactId, updateBody, ssr) => {
url: queryUrl,
data: data
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const createAccount = (formValues) => {
@@ -99,67 +61,50 @@ export const createAccount = (formValues) => {
data: data
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const getEmailAccountCheck = (emailAddress) => {
return axios
.get(
"/api/endpoint/getemailaccountcheck_api?emailAddress=" +
emailAddress
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
return getJson(
"/api/endpoint/getemailaccountcheck_api?emailAddress=" + emailAddress
).catch((error) => {
consoleLogger(error);
});
};
export const getPortalLogin = async (emailAddress) => {
var queryUrl =
"/api/endpoint/getportallogin_api?emailAddress=" + emailAddress;
return axios
.get(BASE_URL + (await buildHashedQueryUrl(queryUrl)))
.then((res) => res.data)
.catch((error) => {
return getJson(BASE_URL + (await buildHashedQueryUrl(queryUrl))).catch(
(error) => {
consoleLogger(error);
return JSON.stringify(error);
});
}
);
};
export const getPortalLoginProxy = async (emailAddress) => {
var queryUrl =
"/api/endpoint/getportalloginproxy_api?emailAddress=" + emailAddress;
return axios
.get(queryUrl)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
return getJson(queryUrl).catch((error) => {
consoleLogger(error);
return JSON.stringify(error);
});
return JSON.stringify(error);
});
};
export const getPreferredLanguage = async (email) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getpreferredlanguage_api?emailAddress=" +
email
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
return error.response;
});
return getJson(
BASE_URL +
"/api/endpoint/getpreferredlanguage_api?emailAddress=" +
email
).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 { logAndReturnResponse } from "./httpServiceUtils";
import { getJson } from "../clients/endpointClient";
export const getNewAppeals = async (searchString) => {
try {
const res = await axios.get(BASE_URL + "/api/admin/getnewappeals_api");
return res.data;
return await getJson(BASE_URL + "/api/admin/getnewappeals_api");
} catch (error) {
return logAndReturnResponse(error);
}
@@ -18,8 +17,8 @@ export const getNewAppealsPage = async (
fieldSort,
showNumberOfRecords
) => {
return axios
.get(
try {
return await getJson(
"/api/admin/getnewappeals_api?searchString=" +
searchString +
"&pageNumber=" +
@@ -30,11 +29,10 @@ export const getNewAppealsPage = async (
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
);
} catch (error) {
return logAndReturnResponse(error);
}
};
export const getNewDocumentsPaged = async (
@@ -46,8 +44,8 @@ export const getNewDocumentsPaged = async (
documentOrigin,
selectedWeeks
) => {
return axios
.get(
try {
return await getJson(
"/api/admin/getlatestdocuments_api?pageNumber=" +
pageNumber +
"&orderby=" +
@@ -62,9 +60,8 @@ export const getNewDocumentsPaged = async (
selectedWeeks +
"&documentOrigin=" +
documentOrigin
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
);
} catch (error) {
return logAndReturnResponse(error);
}
};
+126 -194
View File
@@ -1,70 +1,55 @@
import axios from "axios";
import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger";
import { logAndReturnResponse } from "./httpServiceUtils";
import { getJson, requestJson } from "../clients/endpointClient";
import { buildFileQuery, withBaseUrl } from "../clients/fileRouteBuilder";
export const getCaseMessage = (searchString) => {
return axios
.get(BASE_URL + "/api/endpoint/getcasemessage_api?id=" + searchString)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
const route = buildFileQuery("/api/endpoint/getcasemessage_api", {
id: searchString
});
return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
};
export const getIncidentbyID = (searchString) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getincidentbyid_api?searchString=" +
searchString
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
const route = buildFileQuery("/api/endpoint/getincidentbyid_api", {
searchString
});
return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
};
export const getIsPublishedbyID = (searchString) => {
return axios
.get(
"/api/endpoint/getispublishedbyid_api?searchString=" + searchString
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
const route = buildFileQuery("/api/endpoint/getispublishedbyid_api", {
searchString
});
return getJson(route).catch(logAndReturnResponse);
};
export const getPartSavedAppeal = (searchString) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getpartsavedappeal_api?searchString=" +
searchString
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
const route = buildFileQuery("/api/endpoint/getpartsavedappeal_api", {
searchString
});
return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
};
export const getSIPSEvents = async (caseid) => {
return axios
.get(BASE_URL + "/api/endpoint/getsipsevents_api?caseid=" + caseid)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
const route = buildFileQuery("/api/endpoint/getsipsevents_api", {
caseid
});
return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
};
export const getSIPSMedia = async (caseid) => {
return axios
.get(BASE_URL + "/api/endpoint/getsipsmedia_api?caseid=" + caseid)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
const route = buildFileQuery("/api/endpoint/getsipsmedia_api", {
caseid
});
return getJson(withBaseUrl(BASE_URL, route)).catch(logAndReturnResponse);
};
export const getAppealID = (
@@ -72,17 +57,15 @@ export const getAppealID = (
updateFormCollection,
primaryAttribute
) => {
return axios
.get(
"/api/endpoint/getappealid_api?updateFormCollection=" +
updateFormCollection +
"&primaryAttribute=" +
primaryAttribute +
"&caseReference=" +
caseReference
)
.then((res) => {
const result = Object.entries(res.data.value[0]).filter(
const route = buildFileQuery("/api/endpoint/getappealid_api", {
updateFormCollection,
primaryAttribute,
caseReference
});
return getJson(route)
.then((data) => {
const result = Object.entries(data.value[0]).filter(
([key]) => !key.startsWith("_")
)[0][1];
var appealID = result;
@@ -104,29 +87,22 @@ export const createNewCase = (
var data = createBody;
var queryUrl =
"/api/endpoint/createcase_api?appealTypeId=" +
appealTypeId +
"&lpaID=" +
lpaID +
"&contactid=" +
contactid +
"&containername=" +
containerName;
var queryUrl = buildFileQuery("/api/endpoint/createcase_api", {
appealTypeId,
lpaID,
contactid,
containername: containerName
});
var config = {
method: "post",
url: BASE_URL + queryUrl,
url: withBaseUrl(BASE_URL, queryUrl),
data: data
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const createNewCaseBlob = (
@@ -144,15 +120,12 @@ export const createNewCaseBlob = (
data.pinswg_lpaname = lpaName;
data.createdon = new Date();
var queryUrl =
"/api/file/createcase_api?appealTypeId=" +
appealTypeId +
"&lpaID=" +
lpaID +
"&contactid=" +
contactid +
"&containername=" +
containerName;
var queryUrl = buildFileQuery("/api/file/createcase_api", {
appealTypeId,
lpaID,
contactid,
containername: containerName
});
var config = {
method: "post",
@@ -160,13 +133,9 @@ export const createNewCaseBlob = (
data: data
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const updateCase = async (
@@ -184,11 +153,10 @@ export const updateCase = async (
primaryAttribute
);
var queryUrl =
"/api/endpoint/updatecase_api?updateFormCollection=" +
updateFormCollection +
"&appealObj=" +
appealObj;
var queryUrl = buildFileQuery("/api/endpoint/updatecase_api", {
updateFormCollection,
appealObj
});
var config = {
method: "post",
@@ -196,13 +164,9 @@ export const updateCase = async (
data: updateBody
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const updateCaseBlob = async (
@@ -220,11 +184,10 @@ export const updateCaseBlob = async (
primaryAttribute
);
var queryUrl =
"/api/file/updatecase_api?updateFormCollection=" +
updateFormCollection +
"&appealObj=" +
appealObj;
var queryUrl = buildFileQuery("/api/file/updatecase_api", {
updateFormCollection,
appealObj
});
var config = {
method: "post",
@@ -232,113 +195,82 @@ export const updateCaseBlob = async (
data: updateBody
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const patchCase = async (incidentid) => {
var queryUrl = "/api/endpoint/patchcase_api?incidentid=" + incidentid;
var config = {
method: "get",
url: queryUrl
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
//console.log("this error:", error);
});
var queryUrl = buildFileQuery("/api/endpoint/patchcase_api", {
incidentid
});
return getJson(queryUrl).catch((error) => {
//console.log("this error:", error);
});
};
export const getCase = (incidentID) => {
return axios
.get(BASE_URL + "/api/endpoint/getcase_api?incidentID=" + incidentID)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getcase_api", {
incidentID
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
});
};
export const getCaseByID = (incidentID) => {
return axios
.get(
BASE_URL + "/api/endpoint/getcasebyid_api?incidentID=" + incidentID
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getcasebyid_api", {
incidentID
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
});
};
export const getAppealPDFDocs = (incidentID) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getappealpdfdocuments_api?incidentid=" +
incidentID
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getappealpdfdocuments_api", {
incidentid: incidentID
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
});
};
export const getAppealPDFDocument = async (incidentid) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getappealpdfdocuments_api?incidentid=" +
incidentid
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
return error.response;
});
const route = buildFileQuery("/api/endpoint/getappealpdfdocuments_api", {
incidentid
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
return error.response;
});
};
export const getPortalModuleDetails = async (appealType, caseReference) => {
var config = {
method: "get",
url:
BASE_URL +
"/api/endpoint/getportalmoduledetails_api?appealType=" +
appealType +
"&caseReference=" +
encodeURI(caseReference)
};
const route = buildFileQuery("/api/endpoint/getportalmoduledetails_api", {
appealType,
caseReference: encodeURI(caseReference)
});
try {
const res = await axios(config);
return res.data;
} catch (error) {
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
}
});
};
export const getPortalModuleDetailsProxy = (appealType, caseReference) => {
return axios
.get(
"/api/endpoint/getportalmoduledetailsproxy_api?appealType=" +
appealType +
"&caseReference=" +
caseReference.replace(/\'/g, "''")
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery(
"/api/endpoint/getportalmoduledetailsproxy_api",
{
appealType,
caseReference: caseReference.replace(/\'/g, "''")
}
);
return getJson(route).catch((error) => {
consoleLogger(error);
});
};
+170 -282
View File
@@ -1,110 +1,88 @@
import axios from "axios";
import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger";
import { hashAPIPath } from "../core/hash";
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;
}
};
import {
buildFileQuery,
withBaseUrl,
appendQuerySuffix
} from "../clients/fileRouteBuilder";
import {
getFileJson,
getSignedFileJson,
downloadFileBlob,
postSignedFileJson
} from "../clients/fileClient";
export const getAwaitingSubmissionFromBlob = (containerName) => {
return axios
.get(
BASE_URL +
"/api/file/getawaitingsubmissionfromblob?container=" +
containerName +
hashAPIPath(
"/api/file/getawaitingsubmissionfromblob?container=" +
containerName
)
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/file/getawaitingsubmissionfromblob", {
container: containerName
});
return getFileJson(
withBaseUrl(BASE_URL, appendQuerySuffix(route, hashAPIPath(route)))
).catch((error) => {
consoleLogger(error);
});
};
export const getRepsFromBlob = (containerName) => {
return axios
.get(
BASE_URL +
"/api/file/getrepsblob?container=" +
containerName +
hashAPIPath("/api/file/getrepsblob?container=" + containerName)
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/file/getrepsblob", {
container: containerName
});
return getFileJson(
withBaseUrl(BASE_URL, appendQuerySuffix(route, hashAPIPath(route)))
).catch((error) => {
consoleLogger(error);
});
};
export const getRepsFromBlobProxy = async (containerName) => {
try {
const res = await axios.get(
"/api/file/getrepsblobproxy?container=" + containerName
);
return res.data;
} catch (error) {
const route = buildFileQuery("/api/file/getrepsblobproxy", {
container: containerName
});
return getFileJson(route).catch((error) => {
consoleLogger(error);
}
});
};
export const getAwaitingSubmissionFromBlobProxy = async (containerName) => {
try {
const res = await axios.get(
BASE_URL +
"/api/file/getawaitingsubmissionfromblobproxy?container=" +
containerName
);
return res.data;
} catch (error) {
const route = buildFileQuery(
"/api/file/getawaitingsubmissionfromblobproxy",
{
container: containerName
}
);
return getFileJson(withBaseUrl(BASE_URL, route)).catch((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 = (
containerID,
casefolderID
) => {
var queryUrl =
"/api/file/deleteblobcase?container=" +
containerID +
"&casefolderID=" +
casefolderID;
var queryUrl = buildFileQuery("/api/file/deleteblobcase", {
container: containerID,
casefolderID
});
return buildHashedQueryUrl(queryUrl)
.then((signedUrl) =>
axios({
method: "get",
url: signedUrl
})
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return getSignedFileJson(queryUrl).catch((error) => {
consoleLogger(error);
});
};
export const deleteMyRepresentationsFromBlob = (
@@ -112,27 +90,15 @@ export const deleteMyRepresentationsFromBlob = (
casefolderID,
repfile
) => {
var queryUrl =
"/api/file/deleteblobrep?container=" +
containerID +
"&casefolderID=" +
casefolderID +
"&repfile=" +
repfile;
var queryUrl = buildFileQuery("/api/file/deleteblobrep", {
container: containerID,
casefolderID,
repfile
});
return buildHashedQueryUrl(queryUrl)
.then((signedUrl) =>
axios({
method: "get",
url: signedUrl
})
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return getSignedFileJson(queryUrl).catch((error) => {
consoleLogger(error);
});
};
export const uploadFiles = async (
@@ -156,18 +122,10 @@ export const uploadFiles = async (
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 {
const res = await axios(config);
return res.data;
return await postSignedFileJson(queryUrl, formData, {
headers: { "content-type": "multipart/form-data" }
});
} catch (error) {
consoleLogger(error);
}
@@ -186,18 +144,10 @@ export const uploadSingleFile = async (filesObj, containerID, casefolderID) => {
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 {
const res = await axios(config);
return res.data;
return await postSignedFileJson(queryUrl, formData, {
headers: { "content-type": "multipart/form-data" }
});
} catch (error) {
consoleLogger(error);
}
@@ -217,18 +167,10 @@ export const uploadRepFiles = async (
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 {
const res = await axios(config);
return res.data;
return await postSignedFileJson(queryUrl, formData, {
headers: { "content-type": "multipart/form-data" }
});
} catch (error) {
consoleLogger(error);
}
@@ -243,18 +185,10 @@ export const generateRepPDF = async (
var queryUrl =
"/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 {
const res = await axios(config);
return res.data;
return await postSignedFileJson(queryUrl, formValues, {
...(options.download ? { responseType: "blob" } : {})
});
} catch (error) {
consoleLogger(error);
}
@@ -277,60 +211,26 @@ export const generateAppealPDF = async (
appealType +
(options.download ? "&download=true" : "");
const hashedUrl = await buildHashedQueryUrl(queryUrl);
const config = {
method: "post",
url: hashedUrl,
data: formValues,
...(options.download ? { responseType: "blob" } : {})
};
try {
const res = await axios(config);
return res.data;
return await postSignedFileJson(queryUrl, formValues, {
...(options.download ? { responseType: "blob" } : {})
});
} catch (error) {
consoleLogger(error);
}
};
export const getFilesFromBlob = (containerName, casefolderID) => {
return axios
.get(
BASE_URL +
"/api/file/getbloblist?container=" +
containerName +
"&casefolderID=" +
casefolderID +
hashAPIPath(
"/api/file/getbloblist?container=" +
containerName +
"&casefolderID=" +
casefolderID
)
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
};
const route = buildFileQuery("/api/file/getbloblist", {
container: containerName,
casefolderID
});
export const getFilesFromBlobproxy = (containerName, casefolderID) => {
return axios
.get(
"/api/file/getbloblistproxy?container=" +
containerName +
"&casefolderID=" +
casefolderID
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return getFileJson(
withBaseUrl(BASE_URL, appendQuerySuffix(route, hashAPIPath(route)))
).catch((error) => {
consoleLogger(error);
});
};
export const getFilesFromBlobHashed = (
@@ -338,20 +238,16 @@ export const getFilesFromBlobHashed = (
getblobshash,
casefolderID
) => {
return axios
.get(
"/api/file/getbloblist?container=" +
containerName +
"&casefolderID=" +
casefolderID +
getblobshash
)
.then((res) => {
return res.data;
})
.catch((error) => {
const route = buildFileQuery("/api/file/getbloblist", {
container: containerName,
casefolderID
});
return getFileJson(appendQuerySuffix(route, getblobshash)).catch(
(error) => {
consoleLogger(error);
});
}
);
};
export const deleteBlob = async (
@@ -360,20 +256,23 @@ export const deleteBlob = async (
deleteblobhash,
casefolderID
) => {
try {
const res = await axios.get(
"/api/file/deleteblob?container=" +
containerName +
"&casefolderID=" +
encodeURIComponent(casefolderID) +
"&blobname=" +
encodeURIComponent(blobName) +
deleteblobhash
);
return res.data;
} catch (error) {
consoleLogger(error);
}
const route = buildFileQuery(
"/api/file/deleteblob",
{
container: containerName,
casefolderID,
blobname: blobName
},
{
encode: true
}
);
return getFileJson(appendQuerySuffix(route, deleteblobhash)).catch(
(error) => {
consoleLogger(error);
}
);
};
export const deleteRepBlob = async (
@@ -383,78 +282,67 @@ export const deleteRepBlob = async (
casefolderID,
filenamePrefix
) => {
try {
const res = await axios.get(
"/api/file/deleteblob?container=" +
containerName +
"&casefolderID=" +
encodeURIComponent(casefolderID + "/" + filenamePrefix) +
"&blobname=" +
encodeURIComponent(blobName) +
deleteblobhash
);
return res.data;
} catch (error) {
consoleLogger(error);
}
const route = buildFileQuery(
"/api/file/deleteblob",
{
container: containerName,
casefolderID: casefolderID + "/" + filenamePrefix,
blobname: blobName
},
{
encode: true
}
);
return getFileJson(appendQuerySuffix(route, deleteblobhash)).catch(
(error) => {
consoleLogger(error);
}
);
};
export const downloadBlob = (containerName, blobName) => {
return axios
.get(
BASE_URL +
"/api/file/downloadblob?container=" +
containerName.toLowerCase() +
"&blobname=" +
blobName,
{ responseType: "blob" }
)
.then((response) => {
res.setHeader(
"content-disposition",
"attachment; filename=" + blobName
);
return res.status(200).send(response.data);
const queryUrl = withBaseUrl(
BASE_URL,
buildFileQuery("/api/file/downloadblob", {
container: containerName.toLowerCase(),
blobname: blobName
})
.catch((error) => {
consoleLogger(error);
});
);
return downloadFileBlob(queryUrl).catch((error) => {
consoleLogger(error);
});
};
export const getProgressFromBlob = async (containerName, casereference) => {
try {
const res = await axios.get(
BASE_URL +
"/api/file/getprogressobjblob?container=" +
encodeURIComponent(containerName) +
"&casefolderID=" +
encodeURIComponent(casereference) +
hashAPIPath(
"/api/file/getprogressobjblob?container=" +
encodeURIComponent(containerName) +
"&casefolderID=" +
encodeURIComponent(casereference)
)
);
return res.data;
} catch (error) {
const route = buildFileQuery(
"/api/file/getprogressobjblob",
{
container: containerName,
casefolderID: casereference
},
{
encode: true
}
);
return getFileJson(
withBaseUrl(BASE_URL, appendQuerySuffix(route, hashAPIPath(route)))
).catch((error) => {
consoleLogger(error);
}
});
};
export const createContainerProxy = (containerName) => {
var queryUrl =
"/api/file/setupcontainer?ident=" +
containerName +
hashAPIPath("/api/file/setupcontainer?ident=" + containerName);
var route = buildFileQuery("/api/file/setupcontainer", {
ident: containerName
});
var queryUrl = appendQuerySuffix(route, hashAPIPath(route));
return axios
.get(BASE_URL + queryUrl)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
return getFileJson(withBaseUrl(BASE_URL, queryUrl)).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 { requestJson } from "../clients/endpointClient";
export const createCRMTask = async (formValues) => {
var queryUrl = "/api/endpoint/createcrmtask_api";
@@ -12,8 +12,7 @@ export const createCRMTask = async (formValues) => {
};
try {
const res = await axios(config);
return res.data;
return await requestJson(config);
} catch (error) {
consoleLogger(error);
}
+6 -3
View File
@@ -1,5 +1,5 @@
import axios from "axios";
import { consoleLogger } from "../core/logger";
import { requestJson } from "../clients/endpointClient";
export const sendEmail = async (
templateId,
@@ -15,8 +15,11 @@ export const sendEmail = async (
};
try {
const response = await axios.post("/api/email/notify", mailData);
return response.data;
return await requestJson({
method: "post",
url: "/api/email/notify",
data: mailData
});
} catch (error) {
consoleLogger(error);
throw error;
+152 -201
View File
@@ -1,167 +1,132 @@
import axios from "axios";
import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger";
import { hashAPIPath } from "../core/hash";
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;
}
};
import { buildHashedQueryUrl } from "../clients/relayClient";
import { getJson, requestJson } from "../clients/endpointClient";
import {
buildFileQuery,
withBaseUrl,
appendQuerySuffix
} from "../clients/fileRouteBuilder";
export const getMyCases = (loggedInUserId) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getmycases_api?loggedInUserId=" +
loggedInUserId
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getmycases_api", {
loggedInUserId
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
});
};
export const getMyInvolvements = async (loggedInUserId) => {
try {
const res = await axios.get(
BASE_URL +
"/api/endpoint/getmyinvolvements_api?loggedInUserId=" +
loggedInUserId
);
return res.data;
} catch (error) {
const route = buildFileQuery("/api/endpoint/getmyinvolvements_api", {
loggedInUserId
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
}
});
};
export const getMyLPACases = (lpaid) => {
return axios
.get(BASE_URL + "/api/endpoint/getmylpacases_api?lpaid=" + lpaid)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getmylpacases_api", {
lpaid
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
});
};
export const getMyRepresentations = (loggedInUserId) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getmyrepresentations_api?loggedInUserId=" +
loggedInUserId
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getmyrepresentations_api", {
loggedInUserId
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
});
};
export const getMyRepresentationsProxy = (loggedInUserId) => {
return axios
.get(
"/api/endpoint/getmyrepresentationsproxy_api?loggedInUserId=" +
loggedInUserId
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery(
"/api/endpoint/getmyrepresentationsproxy_api",
{
loggedInUserId
}
);
return getJson(route).catch((error) => {
consoleLogger(error);
});
};
export const getRepresentations = (incidentID) => {
return axios
.get("/api/endpoint/getrepresentations_api?incidentID=" + incidentID)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getrepresentations_api", {
incidentID
});
return getJson(route).catch((error) => {
consoleLogger(error);
});
};
export const getRepresentationsProxy = (incidentID) => {
return axios
.get(
"/api/endpoint/getrepresentationsproxy_api?incidentID=" + incidentID
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getrepresentationsproxy_api", {
incidentID
});
return getJson(route).catch((error) => {
consoleLogger(error);
});
};
export const getWatchedCases = (loggedInUserId) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getwatchedcases_api?loggedInUserId=" +
loggedInUserId
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getwatchedcases_api", {
loggedInUserId
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
});
};
export const getWatchedCasesProxy = (loggedInUserId) => {
return axios
.get(
"/api/endpoint/getwatchedcasesproxy_api?loggedInUserId=" +
loggedInUserId
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getwatchedcasesproxy_api", {
loggedInUserId
});
return getJson(route).catch((error) => {
consoleLogger(error);
});
};
export const getAwaitingSubmissionProxy = (loggedInUserId) => {
return axios
.get(
"/api/endpoint/getawaitingsubmissionproxy_api?loggedInUserId=" +
loggedInUserId
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery(
"/api/endpoint/getawaitingsubmissionproxy_api",
{
loggedInUserId
}
);
return getJson(route).catch((error) => {
consoleLogger(error);
});
};
export const getAwaitingSubmission = (loggedInUserId) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getawaitingsubmission_api?loggedInUserId=" +
loggedInUserId
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
const route = buildFileQuery("/api/endpoint/getawaitingsubmission_api", {
loggedInUserId
});
return getJson(withBaseUrl(BASE_URL, route)).catch((error) => {
consoleLogger(error);
});
};
export const createWatchedCases = async (formValues) => {
var data = formValues;
var queryUrl = "/api/endpoint/createwatchedcases_api";
var queryUrl = buildFileQuery("/api/endpoint/createwatchedcases_api");
var config = {
method: "post",
@@ -170,21 +135,20 @@ export const createWatchedCases = async (formValues) => {
};
try {
const res = await axios(config);
return res.data;
return await requestJson(config);
} catch (error) {
consoleLogger(error);
}
};
export const deleteMyRepresentations = (myRepresentationsID) => {
var queryUrl =
"/api/endpoint/deletemyrepresentations_api?myRepresentationsID=" +
myRepresentationsID;
var queryUrl = buildFileQuery("/api/endpoint/deletemyrepresentations_api", {
myRepresentationsID
});
return buildHashedQueryUrl(queryUrl)
.then((signedUrl) =>
axios({
requestJson({
method: "delete",
url: signedUrl,
headers: {
@@ -197,46 +161,41 @@ export const deleteMyRepresentations = (myRepresentationsID) => {
}
})
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
};
export const deleteAwaitingSubmissions = (incidentID) => {
var queryUrl =
"/api/endpoint/deleteawaitingsubmissions_api?incidentID=" + incidentID;
var queryUrl = buildFileQuery(
"/api/endpoint/deleteawaitingsubmissions_api",
{
incidentID
}
);
var config = {
method: "delete",
url: queryUrl
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const deleteWatchedCases = async (watchedCaseID) => {
var queryUrl =
"/api/endpoint/deletewatchedcases_api?watchedCaseID=" + watchedCaseID;
var queryUrl = buildFileQuery("/api/endpoint/deletewatchedcases_api", {
watchedCaseID
});
return buildHashedQueryUrl(queryUrl)
.then((signedUrl) =>
axios({
requestJson({
method: "delete",
url: signedUrl
})
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
@@ -247,60 +206,57 @@ export const sendCaseCompleteMessage = async (
caseReference,
inv
) => {
var hashQueryPath =
"/api/file/createappealcompletemessage_api?container=" +
containerID +
"&tempcaseref=" +
caseReference;
var hashQueryPath = buildFileQuery(
"/api/file/createappealcompletemessage_api",
{
container: containerID,
tempcaseref: caseReference
}
);
var queryUrl =
"/api/file/createappealcompletemessage_api?container=" +
containerID +
"&tempcaseref=" +
caseReference +
"&inv=" +
inv;
var queryUrl = buildFileQuery("/api/file/createappealcompletemessage_api", {
container: containerID,
tempcaseref: caseReference,
inv
});
var signedQueryUrl = await buildHashedQueryUrl(hashQueryPath);
queryUrl = queryUrl + signedQueryUrl.replace(hashQueryPath, "");
queryUrl = appendQuerySuffix(
queryUrl,
signedQueryUrl.replace(hashQueryPath, "")
);
var config = {
method: "get",
url: queryUrl
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const sendCaseCompleteMessageProxy = async (
containerID,
caseReference
) => {
var queryUrl =
"/api/file/createappealcompletemessageproxy_api?container=" +
containerID +
"&tempcaseref=" +
caseReference;
var queryUrl = buildFileQuery(
"/api/file/createappealcompletemessageproxy_api",
{
container: containerID,
tempcaseref: caseReference
}
);
var config = {
method: "get",
url: queryUrl
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
export const sendRepCompleteMessage = async (
@@ -308,13 +264,14 @@ export const sendRepCompleteMessage = async (
caseReference,
fileName
) => {
var hashQueryPath =
"/api/file/createrepcompletemessage_api?container=" +
containerID +
"&tempcaseref=" +
caseReference +
"&repid=" +
fileName;
var hashQueryPath = buildFileQuery(
"/api/file/createrepcompletemessage_api",
{
container: containerID,
tempcaseref: caseReference,
repid: fileName
}
);
var queryUrl = await buildHashedQueryUrl(hashQueryPath);
@@ -323,17 +280,13 @@ export const sendRepCompleteMessage = async (
url: queryUrl
};
return axios(config)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return requestJson(config).catch((error) => {
consoleLogger(error);
});
};
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 config = {
@@ -343,15 +296,14 @@ export const setRepInvolvment = async (caseid, contactid) => {
};
try {
const res = await axios(config);
return res.data;
return await requestJson(config);
} catch (error) {
consoleLogger(error);
}
};
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 config = {
@@ -361,8 +313,7 @@ export const setCaseInvolvment = async (caseid, contactid) => {
};
try {
const res = await axios(config);
return res.data;
return await requestJson(config);
} catch (error) {
consoleLogger(error);
}
+31 -49
View File
@@ -1,76 +1,58 @@
import axios from "axios";
import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger";
import { logAndReturnEmptyValueErrorResponse } from "./httpServiceUtils";
import { getJson } from "../clients/endpointClient";
export const getAppealsTypes = () => {
return axios
.get(BASE_URL + "/api/endpoint/getappealtypes_api")
.then((res) => res.data)
.catch(logAndReturnEmptyValueErrorResponse);
return getJson(BASE_URL + "/api/endpoint/getappealtypes_api").catch(
logAndReturnEmptyValueErrorResponse
);
};
export const getProjectTypes = () => {
return axios
.get(BASE_URL + "/api/endpoint/getprojecttypes_api")
.then((res) => res.data)
.catch(logAndReturnEmptyValueErrorResponse);
return getJson(BASE_URL + "/api/endpoint/getprojecttypes_api").catch(
logAndReturnEmptyValueErrorResponse
);
};
export const getAppealsTypesForNewAppeal = () => {
return axios
.get(BASE_URL + "/api/endpoint/getappealtypesfornewappeal_api")
.then((res) => res.data)
.catch(logAndReturnEmptyValueErrorResponse);
return getJson(
BASE_URL + "/api/endpoint/getappealtypesfornewappeal_api"
).catch(logAndReturnEmptyValueErrorResponse);
};
export const getLPA = () => {
return axios
.get(BASE_URL + "/api/endpoint/getlpa_api")
.then((res) => {
return res.data;
})
.catch(logAndReturnEmptyValueErrorResponse);
return getJson(BASE_URL + "/api/endpoint/getlpa_api").catch(
logAndReturnEmptyValueErrorResponse
);
};
export const getFormData = (whichForm) => {
return axios
.get(BASE_URL + "/api/endpoint/getformdata_api?whichForm=" + whichForm)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
return getJson(
BASE_URL + "/api/endpoint/getformdata_api?whichForm=" + whichForm
).catch((error) => {
consoleLogger(error);
});
};
export const getMandatoryFields = (whichForm) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getmandatoryfields_api?whichForm=" +
whichForm
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
return getJson(
BASE_URL + "/api/endpoint/getmandatoryfields_api?whichForm=" + whichForm
).catch((error) => {
consoleLogger(error);
});
};
export const getPickLists = (whichForm) => {
return axios
.get(BASE_URL + "/api/endpoint/getpicklists_api?whichForm=" + whichForm)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
return getJson(
BASE_URL + "/api/endpoint/getpicklists_api?whichForm=" + whichForm
).catch((error) => {
consoleLogger(error);
});
};
export const getNotice = () => {
return axios
.get(BASE_URL + "/api/notices")
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return getJson(BASE_URL + "/api/notices").catch((error) => {
consoleLogger(error);
});
};
+128 -193
View File
@@ -1,35 +1,25 @@
import axios from "axios";
import { BASE_URL } from "../core/env";
import { consoleLogger } from "../core/logger";
import { getJson } from "../clients/endpointClient";
import {
logAndReturnResponse,
logAndReturnEmptyValueErrorResponse
} from "./httpServiceUtils";
export const getBasicSearch = (searchString) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getbasicsearch_api?searchString=" +
searchString
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
return getJson(
BASE_URL +
"/api/endpoint/getbasicsearch_api?searchString=" +
searchString
).catch(logAndReturnResponse);
};
export const getBasicDNSURLSearch = (searchString) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getbasicdnsurlsearch_api?searchString=" +
searchString
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
return getJson(
BASE_URL +
"/api/endpoint/getbasicdnsurlsearch_api?searchString=" +
searchString
).catch(logAndReturnResponse);
};
export const getAddressSearchPaged = (
@@ -39,23 +29,18 @@ export const getAddressSearchPaged = (
fieldSort,
showNumberOfRecords
) => {
return axios
.get(
"/api/endpoint/getbasicsearch_by_address_paged_api?searchString=" +
searchString +
"&pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
return getJson(
"/api/endpoint/getbasicsearch_by_address_paged_api?searchString=" +
searchString +
"&pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords
).catch(logAndReturnResponse);
};
export const getBasicSearchPaged = (
@@ -65,46 +50,36 @@ export const getBasicSearchPaged = (
fieldSort,
showNumberOfRecords
) => {
return axios
.get(
"/api/endpoint/getbasicsearchpaged_api?searchString=" +
searchString +
"&pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords
)
.then((res) => {
return res.data;
})
.catch(logAndReturnResponse);
return getJson(
"/api/endpoint/getbasicsearchpaged_api?searchString=" +
searchString +
"&pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords
).catch(logAndReturnResponse);
};
export const getDNSCoords = () => {
return axios
.get(BASE_URL + "/api/endpoint/getdnscoords_api")
.then((res) => {
return res.data;
})
.catch(logAndReturnEmptyValueErrorResponse);
return getJson(BASE_URL + "/api/endpoint/getdnscoords_api").catch(
logAndReturnEmptyValueErrorResponse
);
};
export const getDNSList = (searchString) => {
return axios
.get(BASE_URL + "/api/endpoint/getdnslist_api")
.then((res) => res.data)
.catch(logAndReturnResponse);
return getJson(BASE_URL + "/api/endpoint/getdnslist_api").catch(
logAndReturnResponse
);
};
export const getBasicDNSSearch = (searchString) => {
return axios
.get(BASE_URL + "/api/endpoint/getbasicdnssearch_api")
.then((res) => res.data)
.catch(logAndReturnResponse);
return getJson(BASE_URL + "/api/endpoint/getbasicdnssearch_api").catch(
logAndReturnResponse
);
};
export const getBasicDNSSearchPaged = (
@@ -113,30 +88,24 @@ export const getBasicDNSSearchPaged = (
fieldSort,
showNumberOfRecords
) => {
return axios
.get(
"/api/endpoint/getbasicdnssearchpaged_api?pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords
)
.then((res) => res.data)
.catch(logAndReturnResponse);
return getJson(
"/api/endpoint/getbasicdnssearchpaged_api?pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords
).catch(logAndReturnResponse);
};
export const getAdvancedSearch = (searchString) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getadvancedsearch_api?searchstring=" +
JSON.stringify(searchString)
)
.then((res) => res.data)
.catch(logAndReturnEmptyValueErrorResponse);
return getJson(
BASE_URL +
"/api/endpoint/getadvancedsearch_api?searchstring=" +
JSON.stringify(searchString)
).catch(logAndReturnEmptyValueErrorResponse);
};
export const getAdvancedSearchPaged = (
@@ -146,21 +115,18 @@ export const getAdvancedSearchPaged = (
fieldSort,
showNumberOfRecords
) => {
return axios
.get(
"/api/endpoint/getadvancedsearchpaged_api?searchstring=" +
JSON.stringify(searchString) +
"&pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords
)
.then((res) => res.data)
.catch(logAndReturnEmptyValueErrorResponse);
return getJson(
"/api/endpoint/getadvancedsearchpaged_api?searchstring=" +
JSON.stringify(searchString) +
"&pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords
).catch(logAndReturnEmptyValueErrorResponse);
};
export const getBasicSearchDetails = async (
@@ -169,20 +135,17 @@ export const getBasicSearchDetails = async (
primaryIdAttribute,
incidentID
) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getbasicsearchdetails_api?appealTypeName=" +
appealTypeName +
"&primaryIdAttribute=" +
primaryIdAttribute +
"&incidentID=" +
incidentID
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
return getJson(
BASE_URL +
"/api/endpoint/getbasicsearchdetails_api?appealTypeName=" +
appealTypeName +
"&primaryIdAttribute=" +
primaryIdAttribute +
"&incidentID=" +
incidentID
).catch((error) => {
consoleLogger(error);
});
};
export const getBasicPartSavedDetails = (
@@ -191,20 +154,17 @@ export const getBasicPartSavedDetails = (
primaryIdAttribute,
incidentID
) => {
return axios
.get(
BASE_URL +
"/api/endpoint/getbasicpartsaveddetails_api?appealTypeName=" +
appealTypeName +
"&primaryIdAttribute=" +
primaryIdAttribute +
"&incidentID=" +
incidentID
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
return getJson(
BASE_URL +
"/api/endpoint/getbasicpartsaveddetails_api?appealTypeName=" +
appealTypeName +
"&primaryIdAttribute=" +
primaryIdAttribute +
"&incidentID=" +
incidentID
).catch((error) => {
consoleLogger(error);
});
};
export const getBasicSearchDetailsPaged = async (
@@ -231,8 +191,8 @@ export const getBasicSearchDetailsPaged = async (
)}`;
try {
const res = await axios.get(url);
return res.data.value || [];
const data = await getJson(url);
return data.value || [];
} catch (error) {
consoleLogger(error);
return [];
@@ -240,27 +200,20 @@ export const getBasicSearchDetailsPaged = async (
};
export const getSearchDocumentDetails = (incidentID) => {
return axios
.get(
"/api/endpoint/getsearchdocumentdetails_api?incidentid=" +
incidentID
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
throw error;
});
return getJson(
"/api/endpoint/getsearchdocumentdetails_api?incidentid=" + incidentID
).catch((error) => {
consoleLogger(error);
throw error;
});
};
export const getSearchDocumentTypes = (incidentID) => {
return axios
.get(
"/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
return getJson(
"/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID
).catch((error) => {
consoleLogger(error);
});
};
export const getSearchDocumentDetailsPaged = (
@@ -271,39 +224,30 @@ export const getSearchDocumentDetailsPaged = (
showNumberOfRecords,
documentType
) => {
return axios
.get(
"/api/endpoint/getsearchdocumentdetailspaged_api?incidentid=" +
incidentID +
"&pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords +
"&documentType=" +
documentType
)
.then((res) => res.data)
.catch((error) => {
consoleLogger(error);
});
return getJson(
"/api/endpoint/getsearchdocumentdetailspaged_api?incidentid=" +
incidentID +
"&pageNumber=" +
pageNumber +
"&orderby=" +
orderBy +
"&fieldSort=" +
fieldSort +
"&showNumberOfRecords=" +
showNumberOfRecords +
"&documentType=" +
documentType
).catch((error) => {
consoleLogger(error);
});
};
export const getLinkedCases = (parentIncidentid) => {
return axios
.get(
"/api/endpoint/getlinkedcases_api?parentincidentid=" +
parentIncidentid
)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
});
return getJson(
"/api/endpoint/getlinkedcases_api?parentincidentid=" + parentIncidentid
).catch((error) => {
consoleLogger(error);
});
};
export const getAddressSearch = async (searchString) => {
@@ -313,14 +257,5 @@ export const getAddressSearch = async (searchString) => {
var queryUrl =
BASE_URL + "/api/endpoint/getbasicsearch_by_address_api?" + str;
var config = {
method: "get",
url: queryUrl
};
return axios(config)
.then((res) => {
return res.data;
})
.catch(logAndReturnEmptyValueErrorResponse);
return getJson(queryUrl).catch(logAndReturnEmptyValueErrorResponse);
};
+55
View File
@@ -75,3 +75,58 @@ Guidance:
- 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`.
## 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 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 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 {
createAxiosMock,
createLoggerMock,
createAxiosError,
loadServiceModule,
normalize
} = require("../serviceHarness.cjs");
const tests = [];
@@ -98,8 +13,6 @@ const test = (name, fn) => {
tests.push({ name, fn });
};
const normalize = (value) => JSON.parse(JSON.stringify(value));
test("search/getBasicSearch returns res.data on success", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
@@ -225,6 +138,30 @@ test("case/getCaseMessage returns error.response on failure", async () => {
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 () => {
const axios = createAxiosMock();
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 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 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 {
createAxiosMock,
createLoggerMock,
createAxiosError,
loadServiceModule,
normalize
} = require("../serviceHarness.cjs");
const tests = [];
@@ -98,8 +13,6 @@ const test = (name, fn) => {
tests.push({ name, fn });
};
const normalize = (value) => JSON.parse(JSON.stringify(value));
test("document/getAwaitingSubmissionFromBlob includes hash token and returns data", async () => {
const axios = createAxiosMock();
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 () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
@@ -367,7 +310,7 @@ test("notify/sendEmail posts payload and returns response data", async () => {
const axios = createAxiosMock();
const logger = createLoggerMock();
axios.postHandler = async () => ({ data: { id: "msg-1" } });
axios.requestHandler = async () => ({ data: { id: "msg-1" } });
const notify = loadServiceModule("notifyDirectService.js", {
axios,
@@ -382,8 +325,9 @@ test("notify/sendEmail posts payload and returns response data", async () => {
);
assert.deepStrictEqual(normalize(result), { id: "msg-1" });
assert.strictEqual(axios.calls[0].url, "/api/email/notify");
assert.deepStrictEqual(normalize(axios.calls[0].data), {
assert.strictEqual(axios.calls[0].config.url, "/api/email/notify");
assert.strictEqual(axios.calls[0].config.method, "post");
assert.deepStrictEqual(normalize(axios.calls[0].config.data), {
templateId: "template-1",
emailAddress: "person@example.com",
reference: "ref-123",
@@ -396,7 +340,7 @@ test("notify/sendEmail logs and rethrows on failure", async () => {
const logger = createLoggerMock();
const error = createAxiosError(429, "Too Many Requests");
axios.postHandler = async () => Promise.reject(error);
axios.requestHandler = async () => Promise.reject(error);
const notify = loadServiceModule("notifyDirectService.js", {
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
};