From fcce6a397820b397df33a8af8fe3ec53309b8713 Mon Sep 17 00:00:00 2001 From: robbond Date: Wed, 25 Mar 2026 14:21:09 +0000 Subject: [PATCH] TASK22269: normalize azurestorage hash query path builders --- actions/azurestorage.js | 150 ++++++++++++++++++++++++-------------- memory-bank/change-log.md | 37 ++++++++++ 2 files changed, 134 insertions(+), 53 deletions(-) diff --git a/actions/azurestorage.js b/actions/azurestorage.js index f75d6000..b2b89887 100644 --- a/actions/azurestorage.js +++ b/actions/azurestorage.js @@ -31,6 +31,59 @@ const QUEUE_PATH = process.env.AZURE_PEDW_QUEUE_ENDPOINT; const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME; +const buildDownloadBlobQueryPath = ({ + containerName, + casefolderID, + blobname, + encodeCasefolderID = true, + encodeBlobname = true +}) => { + const casefolderPart = encodeCasefolderID + ? encodeURIComponent(casefolderID) + : casefolderID; + const blobPart = encodeBlobname ? encodeURIComponent(blobname) : blobname; + + return ( + "/api/file/downloadblob?container=" + + containerName + + "&casefolderID=" + + casefolderPart + + "&blobname=" + + blobPart + ); +}; + +const buildDeleteBlobQueryPath = ({ + containerName, + casefolderID, + blobname, + encodeCasefolderID = true, + encodeBlobname = true +}) => { + const casefolderPart = encodeCasefolderID + ? encodeURIComponent(casefolderID) + : casefolderID; + const blobPart = encodeBlobname ? encodeURIComponent(blobname) : blobname; + + return ( + "/api/file/deleteblob?container=" + + containerName + + "&casefolderID=" + + casefolderPart + + "&blobname=" + + blobPart + ); +}; + +const buildGetBlobListQueryPath = ({ containerName, casefolderID }) => { + return ( + "/api/file/getbloblist?container=" + + containerName + + "&casefolderID=" + + casefolderID + ); +}; + export const createContainerSas = async (containerName) => { // Get environment variables @@ -214,12 +267,11 @@ export const getBlobs = async (containerName, casefolderID) => { "&blobname=" + encodeURIComponent(blob.name), "hashedfilepath": hashAPIPath( - "/api/file/downloadblob?container=" + - containerName + - "&casefolderID=" + - encodeURIComponent(casefolderID) + - "&blobname=" + - encodeURIComponent(blob.name.split("/")[2]) + buildDownloadBlobQueryPath({ + containerName, + casefolderID, + blobname: blob.name.split("/")[2] + }) ), "deletepath": "/api/file/deleteblob?container=" + @@ -229,18 +281,17 @@ export const getBlobs = async (containerName, casefolderID) => { "&blobname=" + encodeURIComponent(blob.name.split("/")[2]), "hasheddeletepath": hashAPIPath( - "/api/file/deleteblob?container=" + - containerName + - "&casefolderID=" + - encodeURIComponent(casefolderID) + - "&blobname=" + - encodeURIComponent(blob.name.split("/")[2]) + buildDeleteBlobQueryPath({ + containerName, + casefolderID, + blobname: blob.name.split("/")[2] + }) ), "hashgetblobs": hashAPIPath( - "/api/file/getbloblist?container=" + - containerName + - "&casefolderID=" + + buildGetBlobListQueryPath({ + containerName, casefolderID + }) ) }); } @@ -1246,26 +1297,24 @@ export const getProgressBlobs = async (containerName, caseReference) => { "contentType": blob.contentType, "lastModified": blob.properties.lastModified, "hashedfilepath": hashAPIPath( - "/api/file/downloadblob?container=" + - containerName + - "&casefolderID=" + - encodeURIComponent(caseReference) + - "&blobname=" + - encodeURIComponent(blob.name.split("/")[1]) + buildDownloadBlobQueryPath({ + containerName, + casefolderID: caseReference, + blobname: blob.name.split("/")[1] + }) ), "hasheddeletepath": hashAPIPath( - "/api/file/deleteblob?container=" + - containerName + - "&casefolderID=" + - encodeURIComponent(caseReference) + - "&blobname=" + - encodeURIComponent(blob.name.split("/")[1]) + buildDeleteBlobQueryPath({ + containerName, + casefolderID: caseReference, + blobname: blob.name.split("/")[1] + }) ), "hashgetblobs": hashAPIPath( - "/api/file/getbloblist?container=" + - containerName + - "&casefolderID=" + - caseReference + buildGetBlobListQueryPath({ + containerName, + casefolderID: caseReference + }) ) }); } @@ -1455,14 +1504,12 @@ export const getRepsFilesBlobs = async ( "&blobname=" + encodeURIComponent(blob.name.split("/")[3]), "hashedfilepath": hashAPIPath( - "/api/file/downloadblob?container=" + - containerName + - "&casefolderID=" + - encodeURIComponent( - blob.name.split("/")[0] + "/" + blob.name.split("/")[1] - ) + - "&blobname=" + - encodeURIComponent(blob.name.split("/")[3]) + buildDownloadBlobQueryPath({ + containerName, + casefolderID: + blob.name.split("/")[0] + "/" + blob.name.split("/")[1], + blobname: blob.name.split("/")[3] + }) ), "deletepath": "/api/file/deleteblob?container=" + @@ -1474,22 +1521,19 @@ export const getRepsFilesBlobs = async ( "&blobname=" + blob.name.split("/")[3], "hasheddeletepath": hashAPIPath( - "/api/file/deleteblob?container=" + - containerName + - "&casefolderID=" + - encodeURIComponent( - blob.name.split("/")[0] + "/" + blob.name.split("/")[1] - ) + - "&blobname=" + - encodeURIComponent(blob.name.split("/")[3]) + buildDeleteBlobQueryPath({ + containerName, + casefolderID: + blob.name.split("/")[0] + "/" + blob.name.split("/")[1], + blobname: blob.name.split("/")[3] + }) ), "hashgetblobs": hashAPIPath( - "/api/file/getbloblist?container=" + - containerName + - "&casefolderID=" + - blob.name.split("/")[0] + - "/" + - blob.name.split("/")[1] + buildGetBlobListQueryPath({ + containerName, + casefolderID: + blob.name.split("/")[0] + "/" + blob.name.split("/")[1] + }) ) }); } diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index a8714674..7f54c220 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -2905,3 +2905,40 @@ Validation: Follow-ups: - Remaining non-service candidate for this stream is `actions/azurestorage.js` direct `hashAPIPath` metadata assembly (separate bounded slice if desired). + +--- + +### CL-081: TASK22269 Slice B1.6 — azurestorage hash-query metadata builder normalization + +date: 2026-03-25 +author: Cline +scope: `actions/azurestorage.js` +type: change +rationale: Continue requested follow-on slice by reducing repeated hash-query path string composition in azure storage metadata builders behind local helper functions. +impact: Lowers duplication/drift risk in hashed metadata path generation while preserving existing route and encoding behavior. +status: completed + +Summary: + +- Added internal helper builders in `actions/azurestorage.js`: + - `buildDownloadBlobQueryPath(...)` + - `buildDeleteBlobQueryPath(...)` + - `buildGetBlobListQueryPath(...)` +- Replaced repeated inline hash path literals with helper usage in targeted metadata object builders: + - `getBlobs` + - `getProgressBlobs` + - `getRepsFilesBlobs` +- Preserved existing behavior semantics for hash path construction: + - encoded `casefolderID`/`blobname` where previously encoded + - unchanged `containerName` and `casefolderID` value sourcing + - unchanged returned object field names and shape + +Validation: + +- `npm run lint` -> pass with warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) +- `node tests/phase22/index.test.cjs` -> pass +- `node tests/phase7/service-behaviour.test.cjs` -> pass (13/13) + +Follow-ups: + +- Remaining potential cleanups in `actions/azurestorage.js` are broader non-slice refactors (legacy logging verbosity, large function decomposition) and should be handled separately to keep risk bounded.