TASK22102: slice5 larger file handler contract cleanup
This commit is contained in:
@@ -1437,3 +1437,38 @@ Validation:
|
|||||||
Follow-ups:
|
Follow-ups:
|
||||||
|
|
||||||
- Continue next larger slice on remaining non-proxy file handlers still returning raw error payloads.
|
- Continue next larger slice on remaining non-proxy file handlers still returning raw error payloads.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### CL-039: TASK22102 API contract consistency — Slice 5 (larger file-handler batch)
|
||||||
|
|
||||||
|
date: 2026-03-17
|
||||||
|
author: Cline
|
||||||
|
scope: `pages/api/file/{uploadsinglefile,setupcontainer,createrepcompletemessage_api,editRepJson}.js`
|
||||||
|
type: change
|
||||||
|
rationale: Continue with a larger efficiency-focused slice by migrating remaining high-signal file handlers that still returned raw/empty error responses.
|
||||||
|
impact: Improves response-contract consistency and avoids leaking raw error payloads in selected file handlers while preserving success behavior and endpoint semantics.
|
||||||
|
status: completed
|
||||||
|
|
||||||
|
Summary:
|
||||||
|
|
||||||
|
- Migrated additional file handlers to helper-based responses:
|
||||||
|
- `uploadsinglefile`
|
||||||
|
- `setupcontainer`
|
||||||
|
- `createrepcompletemessage_api`
|
||||||
|
- `editRepJson`
|
||||||
|
- Replaced direct `res.status(...).json(...)` error branches (including raw `error` payload and empty responses) with structured:
|
||||||
|
- `respondError(...)`
|
||||||
|
- `respondSuccess(...)`
|
||||||
|
- Preserved existing success semantics:
|
||||||
|
- `uploadsinglefile` still returns uploaded result + `invalidFiles`
|
||||||
|
- `setupcontainer` still returns `{ data: "success", output }`
|
||||||
|
- `editRepJson` still returns success message on completion
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
- `npx next lint --file pages/api/file/uploadsinglefile.js --file pages/api/file/setupcontainer.js --file pages/api/file/createrepcompletemessage_api.js --file pages/api/file/editRepJson.js` -> pass (no warnings/errors)
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- Continue next larger slice on remaining file handlers with empty 400 responses to complete contract-consistency rollout.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
} from "../../../actions/azurestorage";
|
} from "../../../actions/azurestorage";
|
||||||
import { hashAPIPath } from "../../../actions/core/hash";
|
import { hashAPIPath } from "../../../actions/core/hash";
|
||||||
import { consoleLogger } from "../../../actions/core/logger";
|
import { consoleLogger } from "../../../actions/core/logger";
|
||||||
|
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||||
|
|
||||||
import nextConnect from "next-connect";
|
import nextConnect from "next-connect";
|
||||||
import middleware from "../middleware/middleware";
|
import middleware from "../middleware/middleware";
|
||||||
@@ -27,7 +28,11 @@ ApiProxy.get(async (req, res) => {
|
|||||||
typeof checkHash === "undefined" ||
|
typeof checkHash === "undefined" ||
|
||||||
checkHash.length === 0
|
checkHash.length === 0
|
||||||
) {
|
) {
|
||||||
return res.status(400).json();
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "MISSING_REQUIRED_QUERY",
|
||||||
|
message: "container, tempcaseref, repid and hash are required"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkquerypath =
|
var checkquerypath =
|
||||||
@@ -39,16 +44,24 @@ ApiProxy.get(async (req, res) => {
|
|||||||
filename;
|
filename;
|
||||||
|
|
||||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||||
return res.status(400).json();
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "INVALID_HASH",
|
||||||
|
message: "Invalid hash"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await createRepCompleteMessage(containerName, tempCaseRef, filename)
|
await createRepCompleteMessage(containerName, tempCaseRef, filename)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
return res.status(200).json(data);
|
return respondSuccess(res, data);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
consoleLogger(error);
|
consoleLogger(error);
|
||||||
return res.status(400).json();
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "CREATE_REP_COMPLETE_MESSAGE_FAILED",
|
||||||
|
message: "Failed to create representation complete message"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
import { DefaultAzureCredential } from "@azure/identity";
|
import { DefaultAzureCredential } from "@azure/identity";
|
||||||
import { BlobServiceClient } from "@azure/storage-blob";
|
import { BlobServiceClient } from "@azure/storage-blob";
|
||||||
|
import { consoleLogger } from "../../../actions/core/logger";
|
||||||
|
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
if (req.method !== "POST") {
|
if (req.method !== "POST") {
|
||||||
res.status(405).json({ error: "Method not allowed" });
|
return respondError(res, {
|
||||||
return;
|
status: 405,
|
||||||
|
code: "METHOD_NOT_ALLOWED",
|
||||||
|
message: "Method not allowed"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { container, blobName } = req.body;
|
const { container, blobName } = req.body;
|
||||||
|
|
||||||
if (!container || !blobName) {
|
if (!container || !blobName) {
|
||||||
res.status(400).json({ error: "Missing container or blobName" });
|
return respondError(res, {
|
||||||
return;
|
status: 400,
|
||||||
|
code: "MISSING_REQUIRED_BODY",
|
||||||
|
message: "Missing container or blobName"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -60,10 +68,16 @@ export default async function handler(req, res) {
|
|||||||
const withTags = await blockBlobClient.setTags(tags);
|
const withTags = await blockBlobClient.setTags(tags);
|
||||||
const withMeta = await blockBlobClient.setMetadata(tags);
|
const withMeta = await blockBlobClient.setMetadata(tags);
|
||||||
|
|
||||||
res.status(200).json({ message: "repComplete removed successfully" });
|
return respondSuccess(res, {
|
||||||
|
message: "repComplete removed successfully"
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error editing rep.json:", error);
|
consoleLogger(error);
|
||||||
res.status(500).json({ error: "Failed to edit rep.json" });
|
return respondError(res, {
|
||||||
|
status: 500,
|
||||||
|
code: "EDIT_REP_JSON_FAILED",
|
||||||
|
message: "Failed to edit rep.json"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from "../../../actions/azurestorage";
|
} from "../../../actions/azurestorage";
|
||||||
import { hashAPIPath } from "../../../actions/core/hash";
|
import { hashAPIPath } from "../../../actions/core/hash";
|
||||||
import { consoleLogger } from "../../../actions/core/logger";
|
import { consoleLogger } from "../../../actions/core/logger";
|
||||||
|
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||||
|
|
||||||
import middleware from "../middleware/middleware";
|
import middleware from "../middleware/middleware";
|
||||||
import nextConnect from "next-connect";
|
import nextConnect from "next-connect";
|
||||||
@@ -24,22 +25,34 @@ ApiProxy.get(async (req, res) => {
|
|||||||
typeof checkHash === "undefined" ||
|
typeof checkHash === "undefined" ||
|
||||||
checkHash.length === 0
|
checkHash.length === 0
|
||||||
) {
|
) {
|
||||||
return res.status(400).json();
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "MISSING_REQUIRED_QUERY",
|
||||||
|
message: "ident and hash are required"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkquerypath = "/api/file/setupcontainer?ident=" + containerName;
|
var checkquerypath = "/api/file/setupcontainer?ident=" + containerName;
|
||||||
|
|
||||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||||
return res.status(400).json();
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "INVALID_HASH",
|
||||||
|
message: "Invalid hash"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await createContainer(containerName)
|
await createContainer(containerName)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
return res.status(200).json({ data: "success", output: data });
|
return respondSuccess(res, { data: "success", output: data });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
consoleLogger(error);
|
consoleLogger(error);
|
||||||
res.status(400).json(error);
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "SETUP_CONTAINER_FAILED",
|
||||||
|
message: "Failed to setup container"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import nextConnect from "next-connect";
|
|||||||
import middleware from "../middleware/middleware";
|
import middleware from "../middleware/middleware";
|
||||||
import { consoleLogger } from "../../../actions/core/logger";
|
import { consoleLogger } from "../../../actions/core/logger";
|
||||||
import { hashAPIPath } from "../../../actions/core/hash";
|
import { hashAPIPath } from "../../../actions/core/hash";
|
||||||
|
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||||
import { fileTypeFromBuffer } from "file-type";
|
import { fileTypeFromBuffer } from "file-type";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
@@ -39,13 +40,21 @@ ApiProxy.post(async (req, res) => {
|
|||||||
var checkHash = req.query.hash;
|
var checkHash = req.query.hash;
|
||||||
|
|
||||||
if (typeof checkHash === "undefined" || checkHash.length === 0) {
|
if (typeof checkHash === "undefined" || checkHash.length === 0) {
|
||||||
return res.status(400).json();
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "HASH_REQUIRED",
|
||||||
|
message: "hash is required"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkquerypath = "/api/file/uploadsinglefile";
|
var checkquerypath = "/api/file/uploadsinglefile";
|
||||||
|
|
||||||
if (hashAPIPath(checkquerypath) != "?hash=" + checkHash) {
|
if (hashAPIPath(checkquerypath) != "?hash=" + checkHash) {
|
||||||
return res.status(400).json();
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "INVALID_HASH",
|
||||||
|
message: "Invalid hash"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const containerID = req.body?.containerID?.[0];
|
const containerID = req.body?.containerID?.[0];
|
||||||
@@ -57,7 +66,11 @@ ApiProxy.post(async (req, res) => {
|
|||||||
typeof casefolderID === "undefined" ||
|
typeof casefolderID === "undefined" ||
|
||||||
casefolderID.length === 0
|
casefolderID.length === 0
|
||||||
) {
|
) {
|
||||||
return res.status(400).json();
|
return respondError(res, {
|
||||||
|
status: 400,
|
||||||
|
code: "MISSING_REQUIRED_BODY",
|
||||||
|
message: "containerID and casefolderID are required"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadedFiles = req.files || {};
|
const uploadedFiles = req.files || {};
|
||||||
@@ -126,10 +139,14 @@ ApiProxy.post(async (req, res) => {
|
|||||||
containerID,
|
containerID,
|
||||||
casefolderID
|
casefolderID
|
||||||
);
|
);
|
||||||
return res.status(200).json({ data, invalidFiles });
|
return respondSuccess(res, { data, invalidFiles });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
consoleLogger(error);
|
consoleLogger(error);
|
||||||
return res.status(500).json({ error: "Failed to upload files" });
|
return respondError(res, {
|
||||||
|
status: 500,
|
||||||
|
code: "UPLOAD_SINGLE_FILE_FAILED",
|
||||||
|
message: "Failed to upload files"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user