diff --git a/CHANGELOG.md b/CHANGELOG.md index 81299a560..5f130f77e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). The format is based on [Keep a Changelog](http://keepachangelog.com/). +## Version 4.0.0 + +### Changed + +- **Breaking:** The `outbox` configuration key under `cds.requires.attachments` has been renamed to `queue`. Projects that explicitly set `attachments.outbox: true` in their own CDS configuration must rename the key to `queue`. A deprecation warning is logged at startup when the old key is detected. + ## Version 3.13.1 ### Fixed diff --git a/lib/generic-handlers.js b/lib/generic-handlers.js index 064daa7fb..865d7f046 100644 --- a/lib/generic-handlers.js +++ b/lib/generic-handlers.js @@ -1,12 +1,7 @@ const cds = require("@sap/cds") const LOG = cds.log("attachments") const { extname } = require("path") -const { - MAX_FILE_SIZE, - sizeInBytes, - checkMimeTypeMatch, - inferTargetCAP8, -} = require("./helper") +const { MAX_FILE_SIZE, sizeInBytes, checkMimeTypeMatch } = require("./helper") const { getMime } = require("./mime") /** @@ -40,13 +35,7 @@ async function finalizePrepareAttachment(data, req, prefix) { // Only try to populate parent keys if there is a parent reference if (parentRef && parentRef.length > 0) { let target - if (cds.infer?.target) { - // CAP 9+: Use cds.infer.target - target = cds.infer.target({ SELECT: { from: { ref: parentRef } } }) - } else { - // CAP 8 fallback: Use inferTargetCAP8 helper - target = inferTargetCAP8(req, parentRef) - } + target = cds.infer.target({ SELECT: { from: { ref: parentRef } } }) if (target?.keys) { LOG.info(`Populating parent keys for attachment upload`, target) diff --git a/lib/helper.js b/lib/helper.js index 9debe9fe7..3763f5cab 100644 --- a/lib/helper.js +++ b/lib/helper.js @@ -439,40 +439,6 @@ async function computeHash(input) { return hash.digest("hex") } -/** - * Resolves the target entity definition from a subject ref in CAP 8 (fallback for CAP 9+ cds.infer). - * Walks the ref path through the model and returns the draft entity if draft-enabled. - * @param {import('@sap/cds').Request} req - The current request (unused, kept for API symmetry with cds.infer) - * @param {Array} ref - The subject ref array from req.subject - * @returns {import('@sap/cds').entity|null} The resolved entity definition, or null if not found - */ -function inferTargetCAP8(req, ref) { - const model = cds.context?.model || cds.model - - // Extract entity/navigation names from ref array - // Handle both simple strings and objects with .id property - const names = ref - .map((part) => { - if (typeof part === "string") return part - if (typeof part === "object" && part.id) return part.id - return null - }) - .filter(Boolean) - - const name = names.join(".") - - let target = model.definitions[name] - if (!target) return null - - // draft fallback - if (target["@odata.draft.enabled"]) { - const draft = model.definitions[`${name}.drafts`] - if (draft) target = draft - } - - return target -} - const multipliers = {} multipliers.B = 1 multipliers.KB = multipliers.B * 1024 @@ -907,7 +873,6 @@ module.exports = { traverseEntity, buildBackAssocChain, MAX_FILE_SIZE, - inferTargetCAP8, getAttachmentKind, handleDuplicates, createSizeCheckHandler, diff --git a/lib/plugin.js b/lib/plugin.js index 2e34e7ccb..a3f9f6d19 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -7,15 +7,11 @@ const { validateAttachmentMimeType, validateAndInsertAttachmentFromDBHandler, } = require("./generic-handlers") -const { - inferTargetCAP8, - getAttachmentKind, - handleDuplicates, -} = require("./helper") +const { getAttachmentKind, handleDuplicates } = require("./helper") require("./csn-runtime-extension") const LOG = cds.log("attachments") -cds.on(cds.version >= "8.6.0" ? "compile.to.edmx" : "loaded", unfoldModel) +cds.on("compile.to.edmx", unfoldModel) // Register the db handler ONCE (not per-service) to intercept attachment INSERT // and handle it through the attachments service instead of native DB insert @@ -24,6 +20,11 @@ cds.on(cds.version >= "8.6.0" ? "compile.to.edmx" : "loaded", unfoldModel) // NOTE: Must use db.prepend() to ensure handlers run before existing ones cds.once("served", () => { if (!cds.env.requires.attachments) return + if (cds.env.requires.attachments?.outbox !== undefined) { + LOG.warn( + "`cds.requires.attachments.outbox` is deprecated; use `queue` instead.", + ) + } const { db } = cds.services db.prepend(() => { @@ -838,8 +839,7 @@ cds.ApplicationService.handle_attachments = cds.service.impl(async function () { if (req.query?.INSERT?.into.ref.length > 1) { const ref = req.query.INSERT.into.ref.slice(0, -1) const parentQuery = { SELECT: { from: { ref: ref }, one: true } } - const parent = - cds.infer?.target?.(parentQuery) || inferTargetCAP8(req, ref) + const parent = cds.infer.target(parentQuery) if (!parent) { LOG.warn( `Could not determine parent target. Ref: ${JSON.stringify(ref)}`, @@ -920,8 +920,7 @@ cds.ApplicationService.handle_attachments = cds.service.impl(async function () { if (req.query?.DELETE?.from?.ref.length > 1) { const ref = req.query.DELETE.from.ref.slice(0, -1) const parentQuery = { SELECT: { from: { ref: ref }, one: true } } - const parent = - cds.infer?.target?.(parentQuery) || inferTargetCAP8(req, ref) + const parent = cds.infer.target(parentQuery) if (!parent) { LOG.warn( `Could not determine parent target. Ref: ${JSON.stringify(ref)}`, diff --git a/package.json b/package.json index 7bbd54b35..65d4495e4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@cap-js/attachments", "description": "CAP cds-plugin providing image and attachment storing out-of-the-box.", - "version": "3.13.1", + "version": "4.0.0", "repository": "cap-js/attachments", "author": "SAP SE (https://www.sap.com)", "homepage": "https://cap.cloud.sap/", @@ -39,7 +39,7 @@ "husky": "^9.1.7" }, "peerDependencies": { - "@sap/cds": ">=8" + "@sap/cds": ">=9" }, "engines": { "node": ">=18.0.0" @@ -94,7 +94,7 @@ } }, "attachments": { - "outbox": true, + "outboxed": true, "scan": true, "deduplicateFileNames": true, "scanExpiryMs": 259200000, diff --git a/srv/attachments/gcp.js b/srv/attachments/gcp.js index 689affddc..ff761f67f 100644 --- a/srv/attachments/gcp.js +++ b/srv/attachments/gcp.js @@ -403,17 +403,8 @@ module.exports = class GoogleAttachmentsService extends ( ) const file = bucket.file(blobName) - let response - try { - response = await file.delete() - } catch (error) { - if (error.statusCode === 404) { - response = error - } else { - throw error - } - } - if (response?.[0]?.statusCode !== 204) { + const [response] = await file.delete({ ignoreNotFound: true }) + if (response?.statusCode !== 204) { LOG.warn("File has not been deleted from Google Cloud Storage", { blobName, bucketName: bucket.name, diff --git a/tests/incidents-app/package.json b/tests/incidents-app/package.json index cf715cfbc..e668d4e3e 100644 --- a/tests/incidents-app/package.json +++ b/tests/incidents-app/package.json @@ -4,7 +4,7 @@ "dependencies": { "@cap-js/attachments": "file:../../.", "@cap-js/audit-logging": "^1.2.0", - "@cap-js/hana": "2.6.0", + "@cap-js/hana": ">=2.6.0", "@cap-js/postgres": "^2.2.0" }, "devDependencies": { diff --git a/tests/integration/attachments-non-draft.test.js b/tests/integration/attachments-non-draft.test.js index 11719cdf8..5cf41e8b8 100644 --- a/tests/integration/attachments-non-draft.test.js +++ b/tests/integration/attachments-non-draft.test.js @@ -872,7 +872,7 @@ describe("Testing max and min amounts of attachments", () => { }) }) - it("custom error message can be specified targeting composition property", async () => { + it("Custom error message can be specified targeting composition property", async () => { await POST(`odata/v4/validation-test-non-draft/Incidents`, { customer_ID: "1004155", title: "ABC", @@ -894,7 +894,7 @@ describe("Testing max and min amounts of attachments", () => { }) }) - it("custom error message can be specified for entity", async () => { + it("Custom error message can be specified for entity", async () => { await POST(`odata/v4/validation-test-non-draft/Incidents`, { customer_ID: "1004155", title: "ABC",