Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 BREAKING-CHANGE t.b.d

### Changed

- Cloud storage SDKs (`@aws-sdk/client-s3`, `@aws-sdk/lib-storage`, `@azure/storage-blob`, `@google-cloud/storage`) are now optional peer dependencies. Install only the SDK(s) for the provider you use (e.g. `npm install @aws-sdk/client-s3 @aws-sdk/lib-storage` for AWS S3). A clear error message with the exact install command is shown if a required SDK is missing at runtime.

## Version 3.12.2

### Fixed
Expand Down
44 changes: 37 additions & 7 deletions lib/mtx/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -619,11 +619,21 @@
* @param {string} tenant - Tenant ID
*/
const _cleanupAWSS3Objects = async (creds, tenant) => {
const {
S3Client,
paginateListObjectsV2,
DeleteObjectsCommand,
} = require("@aws-sdk/client-s3")
let S3Client, paginateListObjectsV2, DeleteObjectsCommand
try {
;({
S3Client,
paginateListObjectsV2,
DeleteObjectsCommand,
} = require("@aws-sdk/client-s3"))
} catch (e) {
if (e.code === "MODULE_NOT_FOUND")
throw new Error(

Check failure on line 631 in lib/mtx/server.js

View workflow job for this annotation

GitHub Actions / lint

There is no `cause` attached to the symptom error being thrown
'Cleanup of AWS S3 objects requires "@aws-sdk/client-s3" to be installed.\n' +
"Please run: npm install @aws-sdk/client-s3 @aws-sdk/lib-storage",
)
throw e
}
const client = new S3Client({
region: creds.region,
credentials: {
Expand Down Expand Up @@ -682,7 +692,17 @@
* @param {string} tenant - Tenant ID
*/
const _cleanupAzureBlobObjects = async (creds, tenant) => {
const { BlobServiceClient } = require("@azure/storage-blob")
let BlobServiceClient
try {
;({ BlobServiceClient } = require("@azure/storage-blob"))
} catch (e) {
if (e.code === "MODULE_NOT_FOUND")
throw new Error(

Check failure on line 700 in lib/mtx/server.js

View workflow job for this annotation

GitHub Actions / lint

There is no `cause` attached to the symptom error being thrown
'Cleanup of Azure Blob objects requires "@azure/storage-blob" to be installed.\n' +
"Please run: npm install @azure/storage-blob",
)
throw e
}
const blobServiceClient = new BlobServiceClient(
`${creds.container_uri}?${creds.sas_token}`,
)
Expand Down Expand Up @@ -721,7 +741,17 @@
* @param {string} tenant - Tenant ID
*/
const _cleanupGoogleCloudObjects = async (creds, tenant) => {
const { Storage } = require("@google-cloud/storage")
let Storage
try {
;({ Storage } = require("@google-cloud/storage"))
} catch (e) {
if (e.code === "MODULE_NOT_FOUND")
throw new Error(

Check failure on line 749 in lib/mtx/server.js

View workflow job for this annotation

GitHub Actions / lint

There is no `cause` attached to the symptom error being thrown
'Cleanup of Google Cloud Storage objects requires "@google-cloud/storage" to be installed.\n' +
"Please run: npm install @google-cloud/storage",
)
throw e
}
const storageClient = new Storage({
projectId: creds.project_id,
credentials: creds.service_account,
Expand Down
24 changes: 21 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
"prepare": "husky"
},
"dependencies": {
"axios": "^1.13.5"
},
"devDependencies": {
"@aws-sdk/client-s3": "^3.993.0",
"@aws-sdk/lib-storage": "^3.993.0",
"@azure/storage-blob": "^12.31.0",
"@google-cloud/storage": "^7.19.0",
"axios": "^1.13.5"
},
"devDependencies": {
"@cap-js/cds-test": ">=0",
"@cap-js/cds-types": "^0.16.0",
"@cap-js/hana": "^2.7.0",
Expand All @@ -39,8 +39,26 @@
"husky": "^9.1.7"
},
"peerDependencies": {
"@aws-sdk/client-s3": "^3",
"@aws-sdk/lib-storage": "^3",
"@azure/storage-blob": "^12",
"@google-cloud/storage": "^7",
"@sap/cds": ">=8"
},
"peerDependenciesMeta": {
"@aws-sdk/client-s3": {
"optional": true
},
"@aws-sdk/lib-storage": {
"optional": true
},
"@azure/storage-blob": {
"optional": true
},
"@google-cloud/storage": {
"optional": true
}
},
"engines": {
"node": ">=18.0.0"
},
Expand Down
23 changes: 19 additions & 4 deletions srv/attachments/aws-s3.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
const {
S3Client,
let S3Client,
GetObjectCommand,
DeleteObjectCommand,
HeadObjectCommand,
CopyObjectCommand,
} = require("@aws-sdk/client-s3")
const { Upload } = require("@aws-sdk/lib-storage")
Upload
try {
;({
S3Client,
GetObjectCommand,
DeleteObjectCommand,
HeadObjectCommand,
CopyObjectCommand,
} = require("@aws-sdk/client-s3"))
;({ Upload } = require("@aws-sdk/lib-storage"))
} catch (e) {
if (e.code === "MODULE_NOT_FOUND")
throw new Error(

Check failure on line 18 in srv/attachments/aws-s3.js

View workflow job for this annotation

GitHub Actions / lint

There is no `cause` attached to the symptom error being thrown
'The AWS S3 storage provider requires "@aws-sdk/client-s3" and "@aws-sdk/lib-storage" to be installed.\n' +
"Please run: npm install @aws-sdk/client-s3 @aws-sdk/lib-storage",
)
throw e
}
const cds = require("@sap/cds")
const LOG = cds.log("attachments")
const utils = require("../../lib/helper")
Expand Down
12 changes: 11 additions & 1 deletion srv/attachments/azure-blob-storage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
const { BlobServiceClient } = require("@azure/storage-blob")
let BlobServiceClient
try {
;({ BlobServiceClient } = require("@azure/storage-blob"))
} catch (e) {
if (e.code === "MODULE_NOT_FOUND")
throw new Error(

Check failure on line 6 in srv/attachments/azure-blob-storage.js

View workflow job for this annotation

GitHub Actions / lint

There is no `cause` attached to the symptom error being thrown
'The Azure Blob Storage provider requires "@azure/storage-blob" to be installed.\n' +
"Please run: npm install @azure/storage-blob",
)
throw e
}
const { AbortController } = require("abort-controller")
const cds = require("@sap/cds")
const LOG = cds.log("attachments")
Expand Down
12 changes: 11 additions & 1 deletion srv/attachments/gcp.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
const { Storage } = require("@google-cloud/storage")
let Storage
try {
;({ Storage } = require("@google-cloud/storage"))
} catch (e) {
if (e.code === "MODULE_NOT_FOUND")
throw new Error(

Check failure on line 6 in srv/attachments/gcp.js

View workflow job for this annotation

GitHub Actions / lint

There is no `cause` attached to the symptom error being thrown
'The Google Cloud Platform storage provider requires "@google-cloud/storage" to be installed.\n' +
"Please run: npm install @google-cloud/storage",
)
throw e
}
const cds = require("@sap/cds")
const LOG = cds.log("attachments")
const utils = require("../../lib/helper")
Expand Down
75 changes: 75 additions & 0 deletions tests/unit/optionalDeps.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
describe("optional peer dependency errors", () => {
const makeNotFound = (pkg) => {
const err = new Error(`Cannot find module '${pkg}'`)
err.code = "MODULE_NOT_FOUND"
return err
}

test("aws-s3 throws helpful error when @aws-sdk/client-s3 is missing", () => {
jest.isolateModules(() => {
jest.doMock("@aws-sdk/client-s3", () => {
throw makeNotFound("@aws-sdk/client-s3")
})
expect(() => require("../../srv/attachments/aws-s3")).toThrow(
"npm install @aws-sdk/client-s3 @aws-sdk/lib-storage",
)
})
Comment thread
stefanrudi marked this conversation as resolved.
})

test("aws-s3 throws helpful error when @aws-sdk/lib-storage is missing", () => {
jest.isolateModules(() => {
jest.doMock("@aws-sdk/lib-storage", () => {
throw makeNotFound("@aws-sdk/lib-storage")
})
expect(() => require("../../srv/attachments/aws-s3")).toThrow(
"npm install @aws-sdk/client-s3 @aws-sdk/lib-storage",
)
})
})
Comment thread
stefanrudi marked this conversation as resolved.

test("azure-blob-storage throws helpful error when @azure/storage-blob is missing", () => {
jest.isolateModules(() => {
jest.doMock("@azure/storage-blob", () => {
throw makeNotFound("@azure/storage-blob")
})
expect(() => require("../../srv/attachments/azure-blob-storage")).toThrow(
"npm install @azure/storage-blob",
)
})
})
Comment thread
stefanrudi marked this conversation as resolved.

test("gcp throws helpful error when @google-cloud/storage is missing", () => {
jest.isolateModules(() => {
jest.doMock("@google-cloud/storage", () => {
throw makeNotFound("@google-cloud/storage")
})
expect(() => require("../../srv/attachments/gcp")).toThrow(
"npm install @google-cloud/storage",
)
})
})

test("aws-s3 loads successfully when SDKs are present", () => {
jest.isolateModules(() => {
jest.dontMock("@aws-sdk/client-s3")
jest.dontMock("@aws-sdk/lib-storage")
expect(() => require("../../srv/attachments/aws-s3")).not.toThrow()
})
})

test("azure-blob-storage loads successfully when SDK is present", () => {
jest.isolateModules(() => {
jest.dontMock("@azure/storage-blob")
expect(() =>
require("../../srv/attachments/azure-blob-storage"),
).not.toThrow()
})
})

test("gcp loads successfully when SDK is present", () => {
jest.isolateModules(() => {
jest.dontMock("@google-cloud/storage")
expect(() => require("../../srv/attachments/gcp")).not.toThrow()
})
})
})
Loading