TASK22102: slice6 blob crud and retrieval contract cleanup
This commit is contained in:
@@ -1472,3 +1472,39 @@ Validation:
|
||||
Follow-ups:
|
||||
|
||||
- Continue next larger slice on remaining file handlers with empty 400 responses to complete contract-consistency rollout.
|
||||
|
||||
---
|
||||
|
||||
### CL-040: TASK22102 API contract consistency — Slice 6 (CRUD/blob retrieval group)
|
||||
|
||||
date: 2026-03-17
|
||||
author: Cline
|
||||
scope: `pages/api/file/{deleteblob,deleteblobcase,deleteblobrep,deleteawaitingsubmissionfromblob,getprogressobjblob,getawaitingsubmissionfromblob,getrepsblob}.js`
|
||||
type: change
|
||||
rationale: Deliver the first of two remaining larger slices by standardizing response contracts across blob CRUD and retrieval handlers still using direct/empty response patterns.
|
||||
impact: Improves consistency and safety of error responses in selected blob handlers while preserving existing success payload behavior and cache-control semantics.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- Migrated 7 blob CRUD/retrieval handlers to `respondSuccess`/`respondError`:
|
||||
- `deleteblob`
|
||||
- `deleteblobcase`
|
||||
- `deleteblobrep`
|
||||
- `deleteawaitingsubmissionfromblob`
|
||||
- `getprogressobjblob`
|
||||
- `getawaitingsubmissionfromblob`
|
||||
- `getrepsblob`
|
||||
- Replaced empty `400` and direct status responses with structured error envelopes including explicit codes/messages.
|
||||
- Removed redundant post-hash equality branches where equivalent logic was already guaranteed after early guard checks.
|
||||
- Preserved response behavior:
|
||||
- existing success data payload shapes retained (`{ data: ... }` or direct data payloads)
|
||||
- `getrepsblob` retains `Cache-Control: no-store` header.
|
||||
|
||||
Validation:
|
||||
|
||||
- `npx next lint --file pages/api/file/deleteblob.js --file pages/api/file/deleteblobcase.js --file pages/api/file/deleteblobrep.js --file pages/api/file/deleteawaitingsubmissionfromblob.js --file pages/api/file/getprogressobjblob.js --file pages/api/file/getawaitingsubmissionfromblob.js --file pages/api/file/getrepsblob.js` -> pass (no warnings/errors)
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Execute final Slice 7 for remaining complex handlers (`upload`, `getbloblist`, `downloadblob`, `generateappealpdf`) to complete the 2-slice finish plan.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { deleteBlob } from "../../../actions/azurestorage";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
@@ -23,7 +24,11 @@ ApiProxy.get(async (req, res) => {
|
||||
typeof checkHash === "undefined" ||
|
||||
checkHash.length === 0
|
||||
) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
message: "container, casefolderID, blobname and hash are required"
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
@@ -35,12 +40,16 @@ ApiProxy.get(async (req, res) => {
|
||||
blobName;
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
message: "Invalid hash"
|
||||
});
|
||||
}
|
||||
|
||||
await deleteBlob(containerName, casefolderID + "/files/" + blobName).then(
|
||||
(data) => {
|
||||
return res.status(200).json({ data: data });
|
||||
return respondSuccess(res, { data: data });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { deleteBlob } from "../../../actions/azurestorage";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
@@ -23,7 +24,11 @@ ApiProxy.get(async (req, res) => {
|
||||
typeof checkHash === "undefined" ||
|
||||
checkHash.length === 0
|
||||
) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
message: "container, casefolderID, blobname and hash are required"
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
@@ -35,12 +40,16 @@ ApiProxy.get(async (req, res) => {
|
||||
encodeURIComponent(blobName);
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
message: "Invalid hash"
|
||||
});
|
||||
}
|
||||
|
||||
await deleteBlob(containerName, casefolderID + "/files/" + blobName).then(
|
||||
(data) => {
|
||||
return res.status(200).json({ data: data });
|
||||
return respondSuccess(res, { data: data });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { deleteBlobCase } from "../../../actions/azurestorage";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
@@ -20,7 +21,11 @@ ApiProxy.get(async (req, res) => {
|
||||
typeof checkHash === "undefined" ||
|
||||
checkHash.length === 0
|
||||
) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
message: "container, casefolderID and hash are required"
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
@@ -30,14 +35,16 @@ ApiProxy.get(async (req, res) => {
|
||||
casefolderID;
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
return res.status(400).json();
|
||||
}
|
||||
|
||||
if (hashAPIPath(checkquerypath) == "&hash=" + checkHash) {
|
||||
await deleteBlobCase(containerName, casefolderID).then((data) => {
|
||||
return res.status(200).json({ data: data });
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
message: "Invalid hash"
|
||||
});
|
||||
}
|
||||
|
||||
await deleteBlobCase(containerName, casefolderID).then((data) => {
|
||||
return respondSuccess(res, { data: data });
|
||||
});
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { deleteBlobRep } from "../../../actions/azurestorage";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
@@ -23,7 +24,11 @@ ApiProxy.get(async (req, res) => {
|
||||
typeof checkHash === "undefined" ||
|
||||
checkHash.length === 0
|
||||
) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
message: "container, casefolderID, repfile and hash are required"
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
@@ -35,16 +40,18 @@ ApiProxy.get(async (req, res) => {
|
||||
repfile;
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
message: "Invalid hash"
|
||||
});
|
||||
}
|
||||
|
||||
casefolderID = casefolderID + "/" + repfile;
|
||||
|
||||
if (hashAPIPath(checkquerypath) == "&hash=" + checkHash) {
|
||||
await deleteBlobRep(containerName, casefolderID).then((data) => {
|
||||
return res.status(200).json({ data: data });
|
||||
});
|
||||
}
|
||||
await deleteBlobRep(containerName, casefolderID).then((data) => {
|
||||
return respondSuccess(res, { data: data });
|
||||
});
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -6,6 +6,7 @@ import _ from "lodash";
|
||||
import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
@@ -20,25 +21,31 @@ ApiProxy.get(async (req, res) => {
|
||||
typeof checkHash === "undefined" ||
|
||||
checkHash.length === 0
|
||||
) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
message: "container and hash are required"
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
"/api/file/getawaitingsubmissionfromblob?container=" + containerName;
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
message: "Invalid hash"
|
||||
});
|
||||
}
|
||||
|
||||
if (hashAPIPath(checkquerypath) == "&hash=" + checkHash) {
|
||||
const blobObj = await getAllProgressBlobs(containerName)
|
||||
.then((data) => {
|
||||
return downloadAllProgressFiles(containerName, data);
|
||||
})
|
||||
.then((data) => {
|
||||
return res.status(200).json(data);
|
||||
});
|
||||
}
|
||||
const blobObj = await getAllProgressBlobs(containerName)
|
||||
.then((data) => {
|
||||
return downloadAllProgressFiles(containerName, data);
|
||||
})
|
||||
.then((data) => {
|
||||
return respondSuccess(res, data);
|
||||
});
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -6,6 +6,7 @@ import _ from "lodash";
|
||||
import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
@@ -24,7 +25,11 @@ ApiProxy.get(async (req, res) => {
|
||||
typeof checkHash === "undefined" ||
|
||||
checkHash.length === 0
|
||||
) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
message: "container, casefolderID and hash are required"
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
@@ -34,7 +39,11 @@ ApiProxy.get(async (req, res) => {
|
||||
casefolderID;
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
message: "Invalid hash"
|
||||
});
|
||||
}
|
||||
|
||||
await getProgressBlobs(containerName, casefolderID)
|
||||
@@ -42,7 +51,7 @@ ApiProxy.get(async (req, res) => {
|
||||
return downloadProgressFile(containerName, data.path, casefolderID);
|
||||
})
|
||||
.then((data) => {
|
||||
return res.status(200).json(data);
|
||||
return respondSuccess(res, data);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
@@ -54,32 +55,40 @@ ApiProxy.get(async (req, res) => {
|
||||
typeof checkHash === "undefined" ||
|
||||
checkHash.length === 0
|
||||
) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MISSING_REQUIRED_QUERY",
|
||||
message: "container and hash are required"
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath = "/api/file/getrepsblob?container=" + containerName;
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
message: "Invalid hash"
|
||||
});
|
||||
}
|
||||
|
||||
if (hashAPIPath(checkquerypath) == "&hash=" + checkHash) {
|
||||
const blobObj = await getRepsBlobs(containerName)
|
||||
.then(async (data) => {
|
||||
return data;
|
||||
})
|
||||
.then(async (data) => {
|
||||
let result = await downloadAllRepsFiles(containerName, data);
|
||||
|
||||
return res
|
||||
.setHeader("Cache-Control", "no-store")
|
||||
.status(200)
|
||||
.json(result);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
const blobObj = await getRepsBlobs(containerName)
|
||||
.then(async (data) => {
|
||||
return data;
|
||||
})
|
||||
.then(async (data) => {
|
||||
let result = await downloadAllRepsFiles(containerName, data);
|
||||
res.setHeader("Cache-Control", "no-store");
|
||||
return respondSuccess(res, result);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_REPS_BLOB_FAILED",
|
||||
message: "Failed to retrieve representation blobs"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export default ApiProxy;
|
||||
|
||||
Reference in New Issue
Block a user