TASK22224: harden getbloblist and getprogressobjblob parity
This commit is contained in:
@@ -40,10 +40,9 @@ const ApiProxy = nextConnect();
|
|||||||
ApiProxy.use(middleware);
|
ApiProxy.use(middleware);
|
||||||
|
|
||||||
ApiProxy.get(async (req, res) => {
|
ApiProxy.get(async (req, res) => {
|
||||||
var containerName = req.query.container;
|
const containerName = req.query.container;
|
||||||
var casefolderID = req.query.casefolderID;
|
const casefolderID = req.query.casefolderID;
|
||||||
|
const checkHash = req.query.hash;
|
||||||
var checkHash = req.query.hash;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof containerName === "undefined" ||
|
typeof containerName === "undefined" ||
|
||||||
@@ -60,13 +59,24 @@ ApiProxy.get(async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkquerypath =
|
const casefolderIDTrimmed = casefolderID.trim();
|
||||||
|
|
||||||
|
const hashCandidatePaths = [
|
||||||
"/api/file/getbloblist?container=" +
|
"/api/file/getbloblist?container=" +
|
||||||
containerName +
|
containerName +
|
||||||
"&casefolderID=" +
|
"&casefolderID=" +
|
||||||
casefolderID;
|
casefolderIDTrimmed,
|
||||||
|
"/api/file/getbloblist?container=" +
|
||||||
|
containerName +
|
||||||
|
"&casefolderID=" +
|
||||||
|
encodeURIComponent(casefolderIDTrimmed)
|
||||||
|
];
|
||||||
|
|
||||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
const isHashValid = hashCandidatePaths.some(
|
||||||
|
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isHashValid) {
|
||||||
return respondError(res, {
|
return respondError(res, {
|
||||||
status: 400,
|
status: 400,
|
||||||
code: "INVALID_HASH",
|
code: "INVALID_HASH",
|
||||||
@@ -74,16 +84,24 @@ ApiProxy.get(async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const data =
|
const data =
|
||||||
casefolderID.split("/").length > 1
|
casefolderIDTrimmed.split("/").length > 1
|
||||||
? await getRepsFilesBlobs(
|
? await getRepsFilesBlobs(
|
||||||
containerName,
|
containerName,
|
||||||
casefolderID.split("/")[0],
|
casefolderIDTrimmed.split("/")[0],
|
||||||
casefolderID.split("/")[1]
|
casefolderIDTrimmed.split("/")[1]
|
||||||
)
|
)
|
||||||
: await getBlobs(containerName, casefolderID);
|
: await getBlobs(containerName, casefolderIDTrimmed);
|
||||||
|
|
||||||
return respondSuccess(res, { "value": [data] });
|
return respondSuccess(res, { "value": [data] });
|
||||||
|
} catch (error) {
|
||||||
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "GET_BLOB_LIST_FAILED",
|
||||||
|
message: "Failed to retrieve blob list"
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
|||||||
@@ -12,10 +12,9 @@ const ApiProxy = nextConnect();
|
|||||||
ApiProxy.use(middleware);
|
ApiProxy.use(middleware);
|
||||||
|
|
||||||
ApiProxy.get(async (req, res) => {
|
ApiProxy.get(async (req, res) => {
|
||||||
var containerName = req.query.container;
|
const containerName = req.query.container;
|
||||||
var casefolderID = req.query.casefolderID;
|
const casefolderID = req.query.casefolderID;
|
||||||
|
const checkHash = req.query.hash;
|
||||||
var checkHash = req.query.hash;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof containerName === "undefined" ||
|
typeof containerName === "undefined" ||
|
||||||
@@ -32,13 +31,24 @@ ApiProxy.get(async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkquerypath =
|
const casefolderIDTrimmed = casefolderID.trim();
|
||||||
|
|
||||||
|
const hashCandidatePaths = [
|
||||||
"/api/file/getprogressobjblob?container=" +
|
"/api/file/getprogressobjblob?container=" +
|
||||||
containerName +
|
containerName +
|
||||||
"&casefolderID=" +
|
"&casefolderID=" +
|
||||||
casefolderID;
|
casefolderIDTrimmed,
|
||||||
|
"/api/file/getprogressobjblob?container=" +
|
||||||
|
containerName +
|
||||||
|
"&casefolderID=" +
|
||||||
|
encodeURIComponent(casefolderIDTrimmed)
|
||||||
|
];
|
||||||
|
|
||||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
const isHashValid = hashCandidatePaths.some(
|
||||||
|
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isHashValid) {
|
||||||
return respondError(res, {
|
return respondError(res, {
|
||||||
status: 400,
|
status: 400,
|
||||||
code: "INVALID_HASH",
|
code: "INVALID_HASH",
|
||||||
@@ -46,13 +56,24 @@ ApiProxy.get(async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await getProgressBlobs(containerName, casefolderID)
|
try {
|
||||||
.then((data) => {
|
const progressBlob = await getProgressBlobs(
|
||||||
return downloadProgressFile(containerName, data.path, casefolderID);
|
containerName,
|
||||||
})
|
casefolderIDTrimmed
|
||||||
.then((data) => {
|
);
|
||||||
|
const data = await downloadProgressFile(
|
||||||
|
containerName,
|
||||||
|
progressBlob.path,
|
||||||
|
casefolderIDTrimmed
|
||||||
|
);
|
||||||
return respondSuccess(res, data);
|
return respondSuccess(res, data);
|
||||||
|
} catch (error) {
|
||||||
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "GET_PROGRESS_OBJ_BLOB_FAILED",
|
||||||
|
message: "Failed to retrieve progress blob"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
|||||||
@@ -377,6 +377,138 @@ test("getbloblist handler returns INVALID_HASH for mismatch", async () => {
|
|||||||
assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH");
|
assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("getbloblist handler accepts encoded casefolder hash variant", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/getbloblist.js", {
|
||||||
|
nextConnect: createNextConnectMock(),
|
||||||
|
middleware: () => {},
|
||||||
|
hashAPIPath: (queryPath) =>
|
||||||
|
queryPath.includes("casefolderID=CASE%2FSUB")
|
||||||
|
? "&hash=expected"
|
||||||
|
: "&hash=other",
|
||||||
|
respondError: respondErrorMock,
|
||||||
|
respondSuccess: respondSuccessMock,
|
||||||
|
getBlobs: async () => ({ file: "a.pdf" }),
|
||||||
|
getRepsFilesBlobs: async () => ({ file: "rep.pdf" })
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
query: {
|
||||||
|
container: "c1",
|
||||||
|
casefolderID: "CASE/SUB",
|
||||||
|
hash: "expected"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default.handler(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 200);
|
||||||
|
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
|
||||||
|
value: [{ file: "rep.pdf" }]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getbloblist handler dependency failure returns GET_BLOB_LIST_FAILED", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/getbloblist.js", {
|
||||||
|
nextConnect: createNextConnectMock(),
|
||||||
|
middleware: () => {},
|
||||||
|
hashAPIPath: () => "&hash=expected",
|
||||||
|
respondError: respondErrorMock,
|
||||||
|
respondSuccess: respondSuccessMock,
|
||||||
|
getBlobs: async () => {
|
||||||
|
throw new Error("blob lookup failed");
|
||||||
|
},
|
||||||
|
getRepsFilesBlobs: async () => ({ file: "rep.pdf" })
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
query: { container: "c1", casefolderID: "f1", hash: "expected" }
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default.handler(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
assert.strictEqual(res.state.jsonBody.error.code, "GET_BLOB_LIST_FAILED");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getprogressobjblob handler success returns payload", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/getprogressobjblob.js", {
|
||||||
|
nextConnect: createNextConnectMock(),
|
||||||
|
middleware: () => {},
|
||||||
|
hashAPIPath: () => "&hash=expected",
|
||||||
|
respondError: respondErrorMock,
|
||||||
|
respondSuccess: respondSuccessMock,
|
||||||
|
getProgressBlobs: async () => ({ path: "CASE1/CASE1_appeal.json" }),
|
||||||
|
downloadProgressFile: async () => ({ id: "progress-1" })
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
query: { container: "c1", casefolderID: "CASE1", hash: "expected" }
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default.handler(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 200);
|
||||||
|
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
|
||||||
|
id: "progress-1"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getprogressobjblob handler accepts encoded casefolder hash variant", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/getprogressobjblob.js", {
|
||||||
|
nextConnect: createNextConnectMock(),
|
||||||
|
middleware: () => {},
|
||||||
|
hashAPIPath: (queryPath) =>
|
||||||
|
queryPath.includes("casefolderID=CASE%2FSUB")
|
||||||
|
? "&hash=expected"
|
||||||
|
: "&hash=other",
|
||||||
|
respondError: respondErrorMock,
|
||||||
|
respondSuccess: respondSuccessMock,
|
||||||
|
getProgressBlobs: async () => ({ path: "CASE/SUB/CASE_appeal.json" }),
|
||||||
|
downloadProgressFile: async () => ({ id: "progress-2" })
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
query: {
|
||||||
|
container: "c1",
|
||||||
|
casefolderID: "CASE/SUB",
|
||||||
|
hash: "expected"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default.handler(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 200);
|
||||||
|
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
|
||||||
|
id: "progress-2"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getprogressobjblob handler dependency failure returns GET_PROGRESS_OBJ_BLOB_FAILED", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/getprogressobjblob.js", {
|
||||||
|
nextConnect: createNextConnectMock(),
|
||||||
|
middleware: () => {},
|
||||||
|
hashAPIPath: () => "&hash=expected",
|
||||||
|
respondError: respondErrorMock,
|
||||||
|
respondSuccess: respondSuccessMock,
|
||||||
|
getProgressBlobs: async () => {
|
||||||
|
throw new Error("progress lookup failed");
|
||||||
|
},
|
||||||
|
downloadProgressFile: async () => ({})
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
query: { container: "c1", casefolderID: "CASE1", hash: "expected" }
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default.handler(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
assert.strictEqual(
|
||||||
|
res.state.jsonBody.error.code,
|
||||||
|
"GET_PROGRESS_OBJ_BLOB_FAILED"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("setupcontainer handler returns MISSING_REQUIRED_QUERY when ident missing", async () => {
|
test("setupcontainer handler returns MISSING_REQUIRED_QUERY when ident missing", async () => {
|
||||||
const mod = loadModule("pages/api/file/setupcontainer.js", {
|
const mod = loadModule("pages/api/file/setupcontainer.js", {
|
||||||
nextConnect: createNextConnectMock(),
|
nextConnect: createNextConnectMock(),
|
||||||
|
|||||||
Reference in New Issue
Block a user