-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
68 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,108 +1,113 @@ | ||
const cds = require("@sap/cds/lib"); | ||
const LOG = cds.log("attachments"); | ||
const { extname } = require("path"); | ||
const DEBUG = LOG._debug ? LOG.debug : undefined; | ||
const attachmentIDRegex = /\/\w+\(.*ID=([0-9a-fA-F-]{36})/; | ||
|
||
cds.on("loaded", function unfoldModel(csn) { | ||
if (!("Attachments" in csn.definitions)) return; | ||
const csnCopy = structuredClone(csn) | ||
cds.linked(csnCopy).forall("Composition", (comp) => { | ||
const cds = require("@sap/cds/lib") | ||
const LOG = cds.log("attachments") | ||
const { extname } = require("path") | ||
const DEBUG = LOG._debug ? LOG.debug : undefined | ||
const attachmentIDRegex = /\/\w+\(.*ID=([0-9a-fA-F-]{36})/ | ||
|
||
cds.on("compile.to.edmx", unfoldModel) | ||
cds.on("loaded", unfoldModel) | ||
|
||
function unfoldModel (csn) { | ||
const meta = csn.meta ??= {} | ||
if (!("Attachments" in csn.definitions)) return | ||
if (meta._enhanced_for_attachments) return | ||
// const csnCopy = structuredClone(csn) // REVISIT: Why did we add this cloning? | ||
cds.linked(csn).forall("Composition", (comp) => { | ||
if (comp._target && comp._target["@_is_media_data"] && comp.parent && comp.is2many) { | ||
const parentDefinition = comp.parent.name | ||
let facets = csn.definitions[parentDefinition]["@UI.Facets"]; | ||
if (!facets) return; | ||
DEBUG?.("Adding @UI.Facet to:", comp.parent.name); | ||
let facets = comp.parent["@UI.Facets"] | ||
if (!facets) return | ||
DEBUG?.("Adding @UI.Facet to:", comp.parent.name) | ||
facets.push({ | ||
$Type: "UI.ReferenceFacet", | ||
Target: `${comp.name}/@UI.LineItem`, | ||
Label: "{i18n>Attachments}", | ||
}); | ||
}) | ||
} | ||
}); | ||
}); | ||
}) | ||
meta._enhanced_for_attachments = true | ||
} | ||
|
||
cds.once("served", async function registerPluginHandlers() { | ||
if (!("Attachments" in cds.model.definitions)) return; | ||
cds.once("served", async function registerPluginHandlers () { | ||
if (!("Attachments" in cds.model.definitions)) return | ||
|
||
const AttachmentsSrv = await cds.connect.to("attachments"); | ||
const AttachmentsSrv = await cds.connect.to("attachments") | ||
|
||
// Searching all associations to attachments to add respective handlers | ||
for (let srv of cds.services) { | ||
if (srv instanceof cds.ApplicationService) { | ||
Object.values(srv.entities).forEach((entity) => { | ||
|
||
for (let elementName in entity.elements) { | ||
if (elementName === "SiblingEntity") continue; // REVISIT: Why do we have this? | ||
const element = entity.elements[elementName], target = element._target; | ||
if (elementName === "SiblingEntity") continue // REVISIT: Why do we have this? | ||
const element = entity.elements[elementName], target = element._target | ||
if (target?.["@_is_media_data"] && target?.drafts) { | ||
DEBUG?.("serving attachments for:", target.name); | ||
srv.before("READ", [target, target.drafts], validateAttachment); | ||
DEBUG?.("serving attachments for:", target.name) | ||
|
||
srv.before("READ", [target, target.drafts], validateAttachment) | ||
|
||
srv.after("READ", [target, target.drafts], readAttachment); | ||
srv.after("READ", [target, target.drafts], readAttachment) | ||
|
||
srv.before("PUT", target.drafts, (req) => validateAttachmentSize(req) ); | ||
srv.before("PUT", target.drafts, (req) => validateAttachmentSize(req)) | ||
|
||
AttachmentsSrv.registerUpdateHandlers(srv, entity, target) | ||
|
||
AttachmentsSrv.registerUpdateHandlers(srv, entity, target); | ||
|
||
srv.before('NEW', target.drafts, req => { | ||
req.data.url = cds.utils.uuid(); | ||
req.data.ID = cds.utils.uuid(); | ||
let ext = extname(req.data.filename).toLowerCase().slice(1); | ||
req.data.mimeType = Ext2MimeTyes[ext] || "application/octet-stream"; | ||
}); | ||
req.data.url = cds.utils.uuid() | ||
req.data.ID = cds.utils.uuid() | ||
let ext = extname(req.data.filename).toLowerCase().slice(1) | ||
req.data.mimeType = Ext2MimeTyes[ext] || "application/octet-stream" | ||
}) | ||
} | ||
} | ||
}); | ||
}) | ||
} | ||
} | ||
|
||
async function validateAttachment(req) { | ||
async function validateAttachment (req) { | ||
|
||
/* removing case condition for mediaType annotation as in our case binary value and metadata is stored in different database */ | ||
|
||
req?.query?.SELECT?.columns?.forEach((element) => { | ||
if(element.as === '[email protected]' && element.xpr){ | ||
delete element.xpr; | ||
element.ref = ['mimeType']; | ||
if (element.as === '[email protected]' && element.xpr) { | ||
delete element.xpr | ||
element.ref = ['mimeType'] | ||
} | ||
}); | ||
}) | ||
|
||
if(req?.req?.url?.endsWith("/content")) { | ||
const attachmentID = req.req.url.match(attachmentIDRegex)[1]; | ||
const status = await AttachmentsSrv.getStatus(req.target, { ID : attachmentID }); | ||
if (req?.req?.url?.endsWith("/content")) { | ||
const attachmentID = req.req.url.match(attachmentIDRegex)[1] | ||
const status = await AttachmentsSrv.getStatus(req.target, { ID: attachmentID }) | ||
const scanEnabled = cds.env.requires?.attachments?.scan ?? true | ||
if(scanEnabled && status !== 'Clean') { | ||
req.reject(403, 'Unable to download the attachment as scan status is not clean.'); | ||
if (scanEnabled && status !== 'Clean') { | ||
req.reject(403, 'Unable to download the attachment as scan status is not clean.') | ||
} | ||
} | ||
} | ||
|
||
async function readAttachment([attachment], req) { | ||
if (!req?.req?.url?.endsWith("/content") || !attachment || attachment?.content) return; | ||
let keys = { ID : req.req.url.match(attachmentIDRegex)[1]}; | ||
let { target } = req; | ||
attachment.content = await AttachmentsSrv.get(target, keys, req); //Dependency -> sending req object for usage in SDM plugin | ||
async function readAttachment ([attachment], req) { | ||
if (!req?.req?.url?.endsWith("/content") || !attachment || attachment?.content) return | ||
let keys = { ID: req.req.url.match(attachmentIDRegex)[1] } | ||
let { target } = req | ||
attachment.content = await AttachmentsSrv.get(target, keys, req) //Dependency -> sending req object for usage in SDM plugin | ||
} | ||
}); | ||
}) | ||
|
||
function validateAttachmentSize(req) { | ||
const contentLengthHeader = req.headers["content-length"]; | ||
let fileSizeInBytes; | ||
function validateAttachmentSize (req) { | ||
const contentLengthHeader = req.headers["content-length"] | ||
let fileSizeInBytes | ||
|
||
if (contentLengthHeader) { | ||
fileSizeInBytes = Number(contentLengthHeader); | ||
const MAX_FILE_SIZE = 419430400; //400 MB in bytes | ||
fileSizeInBytes = Number(contentLengthHeader) | ||
const MAX_FILE_SIZE = 419430400 //400 MB in bytes | ||
if (fileSizeInBytes > MAX_FILE_SIZE) { | ||
return req.reject(403, "File Size limit exceeded beyond 400 MB."); | ||
return req.reject(403, "File Size limit exceeded beyond 400 MB.") | ||
} | ||
} else { | ||
return req.reject(403, "Invalid Content Size"); | ||
return req.reject(403, "Invalid Content Size") | ||
} | ||
} | ||
|
||
module.exports = { validateAttachmentSize }; | ||
module.exports = { validateAttachmentSize } | ||
|
||
const Ext2MimeTyes = { | ||
aac: "audio/aac", | ||
|
@@ -164,4 +169,4 @@ const Ext2MimeTyes = { | |
zip: "application/zip", | ||
txt: "application/txt", | ||
lst: "application/txt" | ||
}; | ||
} |