kalai-attach 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +451 -0
  3. package/_i18n/i18n.properties +6 -0
  4. package/_i18n/i18n_ar.properties +6 -0
  5. package/_i18n/i18n_bg.properties +6 -0
  6. package/_i18n/i18n_cs.properties +6 -0
  7. package/_i18n/i18n_da.properties +6 -0
  8. package/_i18n/i18n_de.properties +6 -0
  9. package/_i18n/i18n_el.properties +6 -0
  10. package/_i18n/i18n_en.properties +6 -0
  11. package/_i18n/i18n_en_US_saptrc.properties +6 -0
  12. package/_i18n/i18n_es.properties +6 -0
  13. package/_i18n/i18n_es_MX.properties +6 -0
  14. package/_i18n/i18n_fi.properties +6 -0
  15. package/_i18n/i18n_fr.properties +6 -0
  16. package/_i18n/i18n_he.properties +6 -0
  17. package/_i18n/i18n_hr.properties +6 -0
  18. package/_i18n/i18n_hu.properties +6 -0
  19. package/_i18n/i18n_it.properties +6 -0
  20. package/_i18n/i18n_ja.properties +6 -0
  21. package/_i18n/i18n_kk.properties +6 -0
  22. package/_i18n/i18n_ko.properties +6 -0
  23. package/_i18n/i18n_ms.properties +6 -0
  24. package/_i18n/i18n_nl.properties +6 -0
  25. package/_i18n/i18n_no.properties +6 -0
  26. package/_i18n/i18n_pl.properties +6 -0
  27. package/_i18n/i18n_pt.properties +6 -0
  28. package/_i18n/i18n_ro.properties +6 -0
  29. package/_i18n/i18n_ru.properties +6 -0
  30. package/_i18n/i18n_sh.properties +6 -0
  31. package/_i18n/i18n_sk.properties +6 -0
  32. package/_i18n/i18n_sl.properties +6 -0
  33. package/_i18n/i18n_sv.properties +6 -0
  34. package/_i18n/i18n_th.properties +6 -0
  35. package/_i18n/i18n_tr.properties +6 -0
  36. package/_i18n/i18n_uk.properties +6 -0
  37. package/_i18n/i18n_vi.properties +6 -0
  38. package/_i18n/i18n_zh_CN.properties +6 -0
  39. package/_i18n/i18n_zh_TW.properties +6 -0
  40. package/_i18n/messages.properties +6 -0
  41. package/_i18n/messages_en_US_saptrc.properties +4 -0
  42. package/cds-plugin.js +4 -0
  43. package/db/data/sap.attachments-ScanStates.csv +6 -0
  44. package/db/data/sap.attachments-ScanStates_texts.csv +6 -0
  45. package/db/data/sap.attachments-ScanStates_texts_ar.csv +6 -0
  46. package/db/data/sap.attachments-ScanStates_texts_bg.csv +6 -0
  47. package/db/data/sap.attachments-ScanStates_texts_cs.csv +6 -0
  48. package/db/data/sap.attachments-ScanStates_texts_da.csv +6 -0
  49. package/db/data/sap.attachments-ScanStates_texts_de.csv +6 -0
  50. package/db/data/sap.attachments-ScanStates_texts_el.csv +6 -0
  51. package/db/data/sap.attachments-ScanStates_texts_en.csv +6 -0
  52. package/db/data/sap.attachments-ScanStates_texts_en_US_saptrc.csv +6 -0
  53. package/db/data/sap.attachments-ScanStates_texts_es.csv +6 -0
  54. package/db/data/sap.attachments-ScanStates_texts_es_MX.csv +6 -0
  55. package/db/data/sap.attachments-ScanStates_texts_fi.csv +6 -0
  56. package/db/data/sap.attachments-ScanStates_texts_fr.csv +6 -0
  57. package/db/data/sap.attachments-ScanStates_texts_he.csv +6 -0
  58. package/db/data/sap.attachments-ScanStates_texts_hr.csv +6 -0
  59. package/db/data/sap.attachments-ScanStates_texts_hu.csv +6 -0
  60. package/db/data/sap.attachments-ScanStates_texts_it.csv +6 -0
  61. package/db/data/sap.attachments-ScanStates_texts_ja.csv +6 -0
  62. package/db/data/sap.attachments-ScanStates_texts_kk.csv +6 -0
  63. package/db/data/sap.attachments-ScanStates_texts_ko.csv +6 -0
  64. package/db/data/sap.attachments-ScanStates_texts_ms.csv +6 -0
  65. package/db/data/sap.attachments-ScanStates_texts_nl.csv +6 -0
  66. package/db/data/sap.attachments-ScanStates_texts_no.csv +6 -0
  67. package/db/data/sap.attachments-ScanStates_texts_pl.csv +6 -0
  68. package/db/data/sap.attachments-ScanStates_texts_pt.csv +6 -0
  69. package/db/data/sap.attachments-ScanStates_texts_ro.csv +6 -0
  70. package/db/data/sap.attachments-ScanStates_texts_ru.csv +6 -0
  71. package/db/data/sap.attachments-ScanStates_texts_sh.csv +6 -0
  72. package/db/data/sap.attachments-ScanStates_texts_sk.csv +6 -0
  73. package/db/data/sap.attachments-ScanStates_texts_sl.csv +6 -0
  74. package/db/data/sap.attachments-ScanStates_texts_sv.csv +6 -0
  75. package/db/data/sap.attachments-ScanStates_texts_th.csv +6 -0
  76. package/db/data/sap.attachments-ScanStates_texts_tr.csv +6 -0
  77. package/db/data/sap.attachments-ScanStates_texts_uk.csv +6 -0
  78. package/db/data/sap.attachments-ScanStates_texts_vi.csv +6 -0
  79. package/db/data/sap.attachments-ScanStates_texts_zh_CN.csv +6 -0
  80. package/db/data/sap.attachments-ScanStates_texts_zh_TW.csv +6 -0
  81. package/db/index.cds +85 -0
  82. package/index.cds +1 -0
  83. package/lib/csn-runtime-extension.js +31 -0
  84. package/lib/generic-handlers.js +199 -0
  85. package/lib/helper.js +407 -0
  86. package/lib/mtx/server.js +583 -0
  87. package/lib/plugin.js +112 -0
  88. package/package.json +67 -0
  89. package/srv/aws-s3.js +249 -0
  90. package/srv/azure-blob-storage.js +202 -0
  91. package/srv/basic.js +331 -0
  92. package/srv/gcp.js +226 -0
  93. package/srv/malwareScanner-mocked.cds +3 -0
  94. package/srv/malwareScanner-mocked.js +127 -0
  95. package/srv/malwareScanner.cds +23 -0
  96. package/srv/malwareScanner.js +151 -0
  97. package/srv/object-store.js +44 -0
  98. package/srv/standard.js +3 -0
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "kalai-attach",
3
+ "description": "CAP cds-plugin providing image and attachment storing out-of-the-box.",
4
+ "version": "1.0.0",
5
+ "repository": "capjsattachments-kalai",
6
+ "author": "Kalai",
7
+ "homepage": "https://github.com/Kalaikovan-airdit",
8
+ "license": "Apache-2.0",
9
+ "main": "cds-plugin.js",
10
+ "files": [
11
+ "index.cds",
12
+ "_i18n",
13
+ "lib",
14
+ "db",
15
+ "srv"
16
+ ],
17
+ "scripts": {
18
+ "lint": "npx eslint .",
19
+ "test": "npx jest --silent=true --runInBand"
20
+ },
21
+ "dependencies": {
22
+ "@aws-sdk/client-s3": "^3.918.0",
23
+ "@aws-sdk/lib-storage": "^3.918.0",
24
+ "@azure/storage-blob": "^12.29.1",
25
+ "@google-cloud/storage": "^7.17.3",
26
+ "axios": "^1.4.0"
27
+ },
28
+ "devDependencies": {
29
+ "@cap-js/cds-test": ">=0",
30
+ "@cap-js/sqlite": "^2",
31
+ "eslint": "^9.36.0",
32
+ "express": "^4.18.2",
33
+ "release-it": "^19.2.2"
34
+ },
35
+ "peerDependencies": {
36
+ "@sap/cds": ">=8"
37
+ },
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
41
+ "cds": {
42
+ "requires": {
43
+ "kinds": {
44
+ "attachments-azure": {
45
+ "impl": "./srv/azure-blob-storage"
46
+ },
47
+ "malwareScanner-mocked": {
48
+ "model": "./srv/malwareScanner-mocked"
49
+ }
50
+ },
51
+ "attachments": {
52
+ "kind": "attachments-azure",
53
+ "scan": false,
54
+ "outbox": true
55
+ },
56
+ "[development]": {
57
+ "attachments": { "kind": "attachments-azure" }
58
+ },
59
+ "[production]": {
60
+ "attachments": { "kind": "attachments-azure" }
61
+ }
62
+ }
63
+ },
64
+ "workspaces": [
65
+ "tests/incidents-app/"
66
+ ]
67
+ }
package/srv/aws-s3.js ADDED
@@ -0,0 +1,249 @@
1
+ const { S3Client, GetObjectCommand, DeleteObjectCommand } = require('@aws-sdk/client-s3')
2
+ const { Upload } = require("@aws-sdk/lib-storage")
3
+ const cds = require("@sap/cds")
4
+ const LOG = cds.log('attachments')
5
+ const utils = require('../lib/helper')
6
+
7
+ module.exports = class AWSAttachmentsService extends require("./object-store") {
8
+
9
+ /**
10
+ * Creates or retrieves a cached S3 client for the specified tenant
11
+ * @returns {Promise<{client: import('@aws-sdk/client-s3').S3Client, bucket: string}>}
12
+ */
13
+ async retrieveClient() {
14
+ const tenantID = this.separateObjectStore ? cds.context.tenant : 'shared'
15
+ LOG.debug('Retrieving S3 client for', { tenantID })
16
+ const existingClient = this.clientsCache.get(tenantID)
17
+ if (existingClient) {
18
+ LOG.debug('Using cached S3 client', {
19
+ tenantID,
20
+ bucket: existingClient.bucket
21
+ })
22
+ return existingClient
23
+ }
24
+
25
+ try {
26
+ LOG.debug(`Fetching object store credentials for tenant ${tenantID}. Using ${this.separateObjectStore ? 'shared' : 'tenant-specific'} object store.`)
27
+ const credentials = this.separateObjectStore
28
+ ? (await utils.getObjectStoreCredentials(tenantID))?.credentials
29
+ : cds.env.requires?.objectStore?.credentials
30
+
31
+ if (!credentials) {
32
+ throw new Error("SAP Object Store instance is not bound.")
33
+ }
34
+
35
+ const requiredFields = ['bucket', 'region', 'access_key_id', 'secret_access_key']
36
+ const missingFields = requiredFields.filter(field => !credentials[field])
37
+
38
+ if (missingFields.length > 0) {
39
+ if (credentials.container_name) {
40
+ throw new Error('Azure Blob Storage found where AWS S3 credentials expected, please check your service bindings.')
41
+ } else if (credentials.projectId) {
42
+ throw new Error('Google Cloud Platform credentials found where AWS S3 credentials expected, please check your service bindings.')
43
+ }
44
+ throw new Error(`Missing Object Store credentials: ${missingFields.join(', ')}`)
45
+ }
46
+
47
+ LOG.debug('Creating S3 client', {
48
+ tenantID,
49
+ region: credentials.region,
50
+ bucket: credentials.bucket
51
+ })
52
+
53
+ const s3Client = new S3Client({
54
+ region: credentials.region,
55
+ credentials: {
56
+ accessKeyId: credentials.access_key_id,
57
+ secretAccessKey: credentials.secret_access_key,
58
+ },
59
+ })
60
+
61
+ const newS3Client = {
62
+ client: s3Client,
63
+ bucket: credentials.bucket,
64
+ }
65
+
66
+ this.clientsCache.set(tenantID, newS3Client)
67
+
68
+ LOG.debug('s3 client has been created successfully', {
69
+ tenantID,
70
+ bucket: newS3Client.bucket,
71
+ region: credentials.region
72
+ })
73
+ return newS3Client;
74
+ } catch (error) {
75
+ LOG.error(
76
+ 'Failed to create tenant-specific S3 client', error,
77
+ 'Check Service Manager and Object Store instance configuration',
78
+ { tenantID })
79
+ throw error
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @inheritdoc
85
+ */
86
+ async put(attachments, data) {
87
+ if (Array.isArray(data)) {
88
+ LOG.debug('Processing bulk file upload', {
89
+ fileCount: data.length,
90
+ filenames: data.map(d => d.filename)
91
+ })
92
+ return Promise.all(
93
+ data.map((d) => this.put(attachments, d))
94
+ )
95
+ }
96
+
97
+ const startTime = Date.now()
98
+ LOG.debug('Starting file upload to S3', {
99
+ attachmentEntity: attachments.name,
100
+ tenant: cds.context.tenant
101
+ })
102
+
103
+ const { client, bucket } = await this.retrieveClient()
104
+
105
+ try {
106
+ const { content, ...metadata } = data
107
+ const Key = metadata.url
108
+
109
+ if (!Key) {
110
+ LOG.error(
111
+ 'File key/URL is required for S3 upload', null,
112
+ 'Ensure attachment data includes a valid URL/key',
113
+ { metadata: { ...metadata, content: !!content } })
114
+ return
115
+ }
116
+
117
+ if (!content) {
118
+ LOG.error(
119
+ 'File content is required for S3 upload', null,
120
+ 'Ensure attachment data includes file content',
121
+ { key: Key, hasContent: !!content })
122
+ return
123
+ }
124
+
125
+ const input = {
126
+ Bucket: bucket,
127
+ Key,
128
+ Body: content,
129
+ }
130
+
131
+ LOG.info('Uploading file to S3', {
132
+ bucket: bucket,
133
+ key: Key,
134
+ filename: metadata.filename,
135
+ contentSize: content.length || content.size || 'unknown'
136
+ })
137
+
138
+ const multipartUpload = new Upload({
139
+ client: client,
140
+ params: input,
141
+ })
142
+
143
+ // The file upload has to be done first, so super.put can compute the hash and trigger malware scan
144
+ await multipartUpload.done()
145
+ await super.put(attachments, metadata)
146
+
147
+ const duration = Date.now() - startTime
148
+ LOG.debug('File upload to S3 completed successfully', {
149
+ filename: metadata.filename,
150
+ fileId: metadata.ID,
151
+ bucket: bucket,
152
+ key: Key,
153
+ duration
154
+ })
155
+ } catch (err) {
156
+ const duration = Date.now() - startTime
157
+ LOG.error(
158
+ 'File upload to S3 failed', err,
159
+ 'Check S3 connectivity, credentials, and bucket permissions',
160
+ { filename: data?.filename, fileId: data?.ID, bucket: bucket, key: data?.url, duration })
161
+ throw err
162
+ }
163
+ }
164
+
165
+ /**
166
+ * @inheritdoc
167
+ */
168
+ async get(attachments, keys) {
169
+ const startTime = Date.now()
170
+
171
+ LOG.info('Starting file download from S3', {
172
+ attachmentEntity: attachments.name,
173
+ keys,
174
+ tenant: cds.context.tenant
175
+ })
176
+
177
+ const { client, bucket } = await this.retrieveClient()
178
+
179
+ try {
180
+ LOG.debug('Fetching attachment metadata', { keys })
181
+ const response = await SELECT.from(attachments, keys).columns("url")
182
+
183
+ if (!response?.url) {
184
+ LOG.warn(
185
+ 'File URL not found in database', null,
186
+ 'Check if the attachment exists and has been properly uploaded',
187
+ { keys, hasResponse: !!response })
188
+ return null
189
+ }
190
+
191
+ const Key = response.url
192
+
193
+ LOG.debug('Streaming file from S3', {
194
+ bucket: bucket,
195
+ key: Key
196
+ })
197
+
198
+ const content = await client.send(
199
+ new GetObjectCommand({
200
+ Bucket: bucket,
201
+ Key,
202
+ })
203
+ )
204
+
205
+ const duration = Date.now() - startTime
206
+ LOG.debug('File streamed from S3 successfully', {
207
+ fileId: keys.ID,
208
+ bucket: bucket,
209
+ key: Key,
210
+ duration
211
+ })
212
+
213
+ return content.Body
214
+
215
+ } catch (error) {
216
+ const duration = Date.now() - startTime
217
+ const suggestion = error.name === 'NoSuchKey' ?
218
+ 'File may have been deleted from S3 or URL is incorrect' :
219
+ error.name === 'AccessDenied' ?
220
+ 'Check S3 bucket permissions and credentials' :
221
+ 'Check S3 connectivity and configuration'
222
+
223
+ LOG.error(
224
+ 'File download from S3 failed', error,
225
+ suggestion,
226
+ { fileId: keys?.ID, bucket: bucket, attachmentName: attachments.name, duration })
227
+
228
+ throw error
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Deletes a file from S3 based on the provided key
234
+ * @param {string} Key - The key of the file to delete
235
+ * @returns {Promise} - Promise resolving when deletion is complete
236
+ */
237
+ async delete(Key) {
238
+ const { client, bucket } = await this.retrieveClient()
239
+ LOG.debug(`[AWS S3] Executing delete for file ${Key} in bucket ${bucket}`)
240
+
241
+ const response = await client.send(
242
+ new DeleteObjectCommand({
243
+ Bucket: bucket,
244
+ Key,
245
+ })
246
+ )
247
+ return response.DeleteMarker
248
+ }
249
+ }
@@ -0,0 +1,202 @@
1
+ const { BlobServiceClient } = require('@azure/storage-blob')
2
+ const cds = require("@sap/cds")
3
+ const LOG = cds.log('attachments')
4
+ const utils = require('../lib/helper')
5
+
6
+ module.exports = class AzureAttachmentsService extends require("./object-store") {
7
+
8
+ /**
9
+ * Creates or retrieves a cached Azure Blob Storage client for the given tenant
10
+ * @returns {Promise<{blobServiceClient: import('@azure/storage-blob').BlobServiceClient, containerClient: import('@azure/storage-blob').ContainerClient}>}
11
+ */
12
+ async retrieveClient() {
13
+ try{
14
+ const container_name="aisp"
15
+ const sas_token="sp=rcwdl&st=2025-12-26T10:41:33Z&se=2030-12-25T18:56:33Z&spr=https&sv=2024-11-04&sr=c&sig=i5ENp1nzh0GrnNd%2FCAnkBBK3vCrHI8vCnHS9og%2F8P8I%3D"
16
+ const container_uri="https://aairdoc9262.blob.core.windows.net"
17
+
18
+ const blobServiceClient = new BlobServiceClient(container_uri + "?" + sas_token)
19
+ const containerClient = blobServiceClient.getContainerClient(container_name)
20
+
21
+ const newAzureCredentials = {
22
+ containerClient,
23
+ }
24
+
25
+ this.clientsCache.set(newAzureCredentials)
26
+
27
+ LOG.debug('Azure Blob Storage client has been created successful', {
28
+
29
+ containerName: containerClient.containerName
30
+ })
31
+ return newAzureCredentials;
32
+ } catch (error) {
33
+ LOG.error(
34
+ 'Failed to create tenant-specific Azure Blob Storage client', error,
35
+ 'Check Service Manager and Azure Blob Storage instance configuration',
36
+ { tenantID })
37
+ throw error
38
+ }
39
+ }
40
+
41
+ /**
42
+ * @inheritdoc
43
+ */
44
+ async put(attachments, data) {
45
+ if (Array.isArray(data)) {
46
+ LOG.debug('Processing bulk file upload', {
47
+ fileCount: data.length,
48
+ filenames: data.map(d => d.filename)
49
+ })
50
+ return Promise.all(
51
+ data.map((d) => this.put(attachments, d))
52
+ )
53
+ }
54
+
55
+ const startTime = Date.now()
56
+
57
+ LOG.debug('Starting file upload to Azure Blob Storage', {
58
+ attachmentEntity: attachments.name,
59
+ tenant: cds.context.tenant
60
+ })
61
+ const { containerClient } = await this.retrieveClient()
62
+ try {
63
+ let { content: _content, ...metadata } = data
64
+ const blobName = metadata.url
65
+
66
+ if (!blobName) {
67
+ LOG.error(
68
+ 'File key/URL is required for Azure Blob Storage upload', null,
69
+ 'Ensure attachment data includes a valid URL/key',
70
+ { metadata: { ...metadata, content: !!_content } })
71
+ throw new Error('File key is required for upload')
72
+ }
73
+
74
+ if (!_content) {
75
+ LOG.error(
76
+ 'File content is required for Azure Blob Storage upload', null,
77
+ 'Ensure attachment data includes file content',
78
+ { key: blobName, hasContent: !!_content })
79
+ throw new Error('File content is required for upload')
80
+ }
81
+
82
+ const blobClient = containerClient.getBlockBlobClient(blobName)
83
+
84
+ LOG.debug('Uploading file to Azure Blob Storage', {
85
+ containerName: containerClient.containerName,
86
+ blobName,
87
+ filename: metadata.filename,
88
+ contentSize: _content.length || _content.size || 'unknown'
89
+ })
90
+
91
+ // Handle different content types for update
92
+ let contentLength
93
+ const content = _content
94
+ if (Buffer.isBuffer(content)) {
95
+ contentLength = content.length
96
+ } else if (content && typeof content.length === 'number') {
97
+ contentLength = content.length
98
+ } else if (content && typeof content.size === 'number') {
99
+ contentLength = content.size
100
+ } else {
101
+ // Convert to buffer if needed
102
+ const chunks = []
103
+ for await (const chunk of content) {
104
+ chunks.push(chunk)
105
+ }
106
+ _content = Buffer.concat(chunks)
107
+ contentLength = _content.length
108
+ }
109
+
110
+ // The file upload has to be done first, so super.put can compute the hash and trigger malware scan
111
+ await blobClient.upload(_content, contentLength)
112
+ await super.put(attachments, metadata)
113
+
114
+ const duration = Date.now() - startTime
115
+ LOG.debug('File upload to Azure Blob Storage completed successfully', {
116
+ filename: metadata.filename,
117
+ fileId: metadata.ID,
118
+ containerName: containerClient.containerName,
119
+ blobName,
120
+ duration
121
+ })
122
+ } catch (err) {
123
+ const duration = Date.now() - startTime
124
+ LOG.error(
125
+ 'File upload to Azure Blob Storage failed', err,
126
+ 'Check Azure Blob Storage connectivity, credentials, and container permissions',
127
+ { filename: data?.filename, fileId: data?.ID, containerName: containerClient.containerName, blobName: data?.url, duration })
128
+ throw err
129
+ }
130
+ }
131
+
132
+ /**
133
+ * @inheritdoc
134
+ */
135
+ async get(attachments, keys) {
136
+ const startTime = Date.now()
137
+ LOG.debug('Starting stream from Azure Blob Storage', {
138
+ attachmentEntity: attachments.name,
139
+ keys,
140
+ tenant: cds.context.tenant
141
+ })
142
+ const { containerClient } = await this.retrieveClient()
143
+
144
+ try {
145
+ LOG.debug('Fetching attachment metadata', { keys })
146
+ const response = await SELECT.from(attachments, keys).columns("url")
147
+
148
+ if (!response?.url) {
149
+ LOG.warn(
150
+ 'File URL not found in database', null,
151
+ 'Check if the attachment exists and has been properly uploaded',
152
+ { keys, hasResponse: !!response })
153
+ return null
154
+ }
155
+
156
+ LOG.debug('Streaming file from Azure Blob Storage', {
157
+ containerName: containerClient.containerName,
158
+ fileId: keys.ID,
159
+ blobName: response.url
160
+ })
161
+
162
+ const blobClient = containerClient.getBlockBlobClient(response.url)
163
+ const downloadResponse = await blobClient.download()
164
+
165
+ const duration = Date.now() - startTime
166
+ LOG.debug('File streamed from Azure Blob Storage successfully', {
167
+ fileId: keys.ID,
168
+ duration
169
+ })
170
+
171
+ return downloadResponse.readableStreamBody
172
+ } catch (error) {
173
+ const duration = Date.now() - startTime
174
+ const suggestion = error.code === 'BlobNotFound' ?
175
+ 'File may have been deleted from Azure Blob Storage or URL is incorrect' :
176
+ error.code === 'AuthenticationFailed' ?
177
+ 'Check Azure Blob Storage credentials and SAS token' :
178
+ 'Check Azure Blob Storage connectivity and configuration'
179
+
180
+ LOG.error(
181
+ 'File download from Azure Blob Storage failed', error,
182
+ suggestion,
183
+ { fileId: keys?.ID, containerName: containerClient.containerName, attachmentName: attachments.name, duration })
184
+
185
+ throw error
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Deletes a file from Azure Blob Storage
191
+ * @param {string} Key - The key of the file to delete
192
+ * @returns {Promise} - Promise resolving when deletion is complete
193
+ */
194
+ async delete(blobName) {
195
+ const { containerClient } = await this.retrieveClient()
196
+ LOG.debug(`[Azure] Executing delete for file ${blobName} in bucket ${containerClient.containerName}`)
197
+
198
+ const blobClient = containerClient.getBlockBlobClient(blobName)
199
+ const response = await blobClient.delete()
200
+ return response._response.status === 202
201
+ }
202
+ }