diff --git a/.cdsrc.json b/.cdsrc.json deleted file mode 100644 index 0967ef4..0000000 --- a/.cdsrc.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/README.md b/README.md index bdb3a58..9d0105b 100644 --- a/README.md +++ b/README.md @@ -6,124 +6,121 @@ This plugin can be consumed by the CAP application deployed on BTP to store thei ### Table of Contents - [Setup](#setup) -- [Use `attachments-sdm`](#use-attachments-sdm) -- [Test-drive Hybrid](#test-drive-hybrid) -- [Contributing](#contributing) +- [Use `sdm`](#use-sdm) +- [Testing the application locally](#testing-the-application-locally) +- [Support, Feedback, Contributing](#support-feedback-contributing) - [Code of Conduct](#code-of-conduct) - [Licensing](#licensing) ## Setup -To enable attachments-sdm, simply add this self-configuring plugin package to your project: +In this guide, we use the [Incidents Management reference sample app](https://github.com/cap-js/incidents-app) as the base application, to add `Attachments` type to the CDS model. + +> **Note:** This plugin is yet to be released. After the release, to enable SDM, simply add this self-configuring plugin package to your project using the following command: +> +> ```sh +> npm add @cap-js/sdm +> ``` + +Prior to release follow below steps to enable sdm plugin + +1. Clone the incidents-app repository: ```sh - npm add @cap-js/attachments-sdm + git clone https://github.com/cap-js/incidents-app.git ``` -In this guide, we use the [Incidents Management reference sample app](https://github.com/cap-js/incidents-app) as the base application, to add `Attachments` type to the CDS model. +2. Clone the sdm repository: + +```sh + git clone https://github.com/cap-js/sdm.git +``` + +3. Open terminal, navigate to sdm root folder and generate tarball: + +```sh + npm pack -> [!Note] -> To be able to use the Fiori _uploadTable_ feature, you must ensure ^1.121.0 SAPUI5 version is updated in the application's _index.html_ + This will generate a file with name cap-js-sdm-1.0.0.tgz +``` + +4. Copy the path of .tgz file generated in step 3 and in terminal navigate to incidents-app root folder and execute: + +```sh + npm install <path-to-.tgz file> +``` -## Use Attachments-sdm +## Use sdm -**To use Attachments-sdm, create an element with an `Attachments` type.** Following the [best practice of separation of concerns](https://cap.cloud.sap/docs/guides/domain-modeling#separation-of-concerns), we do so in a separate file _db/attachments.cds_: +**To use sdm plugin in incidents-app, create an element with an `Attachments` type.** Following the [best practice of separation of concerns](https://cap.cloud.sap/docs/guides/domain-modeling#separation-of-concerns), create a separate file _db/attachments.cds_ and paste the below content in it: ``` using { sap.capire.incidents as my } from './schema'; -using { Attachments } from '@cap-js/attachments'; +using { Attachments } from '@cap-js/sdm'; extend my.Incidents with { attachments: Composition of many Attachments } ``` -**Create a SAP Document Management Service instance and key. Using the contents from key onboard a repository and configure the onboarded repositoryId under cds.requires in package.json** +**Create a SAP Document Management Service instance and key. Using credentials from key [onboard a repository](https://help.sap.com/docs/document-management-service/sap-document-management-service/onboarding-repository) and configure the onboarded repositoryId under cds.requires in package.json** ``` -"attachments-sdm": { +"sdm": { "settings": { "repositoryId": "<repository-Id>" } } ``` -## Test-drive Hybrid +## Testing the application locally For using SAP Document Management Service to store attachments, use the instance-name and service-key values of SAP Document Management Service Integration Option in the below setup. -1. Log in to Cloud Foundry space: +1. Install cds-dk globally + + ```sh + npm i @sap/cds-dk -g + ``` + +2. Log in to Cloud Foundry space: ```sh cf login -a <CF-API> -o <ORG-NAME> -s <SPACE-NAME> ``` -2. To bind to the service continue with the steps below. +3. To bind to the service continue with the steps below. In the project directory, you can generate a new file \_.cdsrc-private.json by running: ```sh - cds bind attachments -2 <INSTANCE-NAME>:<SERVICE-KEY> --kind sdm + cds bind sdm -2 <INSTANCE-NAME>:<SERVICE-KEY> --kind sdm ``` -3. **Start the server**: +4. **Start the server**: - _Default_ scenario (In memory database): ```sh cds watch --profile hybrid ``` -4. **Navigate to the object page** of the incident `Solar panel broken`: - - Go to [Object page for incident **Solar panel broken**](<http://localhost:4004/incidents/app/#/Incidents(ID=3583f982-d7df-4aad-ab26-301d4a157cd7,IsActiveEntity=true)>) - -5. The `Attachments` type has generated an out-of-the-box Attachments table (see 1) at the bottom of the Object page: - <img width="1300" alt="Attachments Table" style="border-radius:0.5rem;" src="etc/facet.png"> - -6. **Upload a file** by going into Edit mode and either using the **Upload** button on the Attachments table or by drag/drop. Then click the **Save** button to have that file stored in SAP Document Management Integration Option. We demonstrate this by uploading the PDF file from [_xmpl/db/content/Solar Panel Report.pdf_](./xmpl/db/content/Solar%20Panel%20Report.pdf): - <img width="1300" alt="Upload an attachment" style="border-radius:0.5rem;" src="etc/upload.gif"> - -7. **Delete a file** by going into Edit mode and selecting the file(s) and by using the **Delete** button on the Attachments table. Then click the **Save** button to have that file deleted from the resource (SAP Document Management Integration Option). We demonstrate this by deleting the previously uploaded PDF file: `Solar Panel Report.pdf` - <img width="1300" alt="Delete an attachment" style="border-radius:0.5rem;" src="etc/delete.gif"> - -## Test-drive Cloud - -1. Log in to Cloud Foundry space: +5. **Navigate to the object page** of the incident `Solar panel broken`: ```sh - cf login -a <CF-API> -o <ORG-NAME> -s <SPACE-NAME> + * Open http://localhost:4004 in a browser. + * If prompted for sign-in, enter alice in username field and click on Sign In button. + * Click on /incidents/webapp under Web Applications. + * Click on incident with title Solar panel broken. ``` -2. Bind your CAP application to SAP Document Management Service Integration Option instance or continue with the steps below. - - In mta.yml of your CAP application add **sdm** as a dependency under srv module: + Or, directly navigate to [Object page for incident **Solar panel broken.**](<http://localhost:4004/incidents/webapp/index.html#/Incidents(ID=3583f982-d7df-4aad-ab26-301d4a157cd7,IsActiveEntity=true)>) - ```sh - modules: - - name: cap-srv - type: nodejs - path: gen/srv - requires: - - name: sdm - ``` - - Under resources of mta.yml add below lines: - - ```sh - resources: - - name: sdm - type: org.cloudfoundry.managed-service - parameters: - service: sdm - service-plan: free - ``` - -3. Build & Deploy the project: +6. The `Attachments` type has generated an out-of-the-box Attachments table (see 1) at the bottom of the Object page: + <img width="1300" alt="Attachments Table" style="border-radius:0.5rem;" src="etc/facet.png"> - ```sh - mbt build - cf deploy <path-to-mtar file> - ``` +7. **Upload a file** by going into Edit mode and either using the **Upload** button on the Attachments table or by drag/drop. Then click the **Save** button to have that file stored in SAP Document Management Integration Option. We demonstrate this by uploading the PDF file from [_xmpl/db/content/Solar Panel Report.pdf_](./xmpl/db/content/Solar%20Panel%20Report.pdf): + <img width="1300" alt="Upload an attachment" style="border-radius:0.5rem;" src="etc/upload.gif"> -4. Once the deployment is successfull open the HTML5 application and follow steps (4 - 7) mentioned in [Test-drive Hybrid](#test-drive-hybrid) section of this README. +8. **Delete a file** by going into Edit mode and selecting the file(s) and by using the **Delete** button on the Attachments table. Then click the **Save** button to have that file deleted from the resource (SAP Document Management Integration Option). We demonstrate this by deleting the previously uploaded PDF file: `Solar Panel Report.pdf` + <img width="1300" alt="Delete an attachment" style="border-radius:0.5rem;" src="etc/delete.gif"> ## Support, Feedback, Contributing diff --git a/lib/util/index.js b/lib/util/index.js index 1048ddd..0ed908a 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -31,7 +31,7 @@ function fetchAccessToken(credentials) { } function getConfigurations() { - return cds.env.requires?.["attachments-sdm"]?.settings || {}; + return cds.env.requires?.["sdm"]?.settings || {}; } module.exports = { diff --git a/manifest.yml b/manifest.yml deleted file mode 100644 index d6a32a8..0000000 --- a/manifest.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Generated manifest.yml based on template version 0.1.0 -# appName = document-management -# language=nodejs -# multitenancy=false ---- -applications: -# ----------------------------------------------------------------------------------- -# Backend Service -# ----------------------------------------------------------------------------------- -- name: document-management-srv - random-route: true # for development only - path: gen/srv - memory: 256M - buildpack: nodejs_buildpack - services: - - sdm-di - diff --git a/mta.yaml b/mta.yaml deleted file mode 100644 index b9343d0..0000000 --- a/mta.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -_schema-version: '3.1' -ID: cap-js.document-management -version: 1.0.0 -description: "CAP plugin for effortless integration of CAP applications with SAP - Document Management Service." -parameters: - enable-parallel-deployments: true -build-parameters: - before-all: - - builder: custom - commands: - - npx cds build --production -modules: - - name: document-management-srv - type: nodejs - path: gen/srv - parameters: - buildpack: nodejs_buildpack - build-parameters: - builder: npm - provides: - - name: srv-api # required by consumers of CAP services (e.g. approuter) - properties: - srv-url: ${default-url} - requires: [] diff --git a/package.json b/package.json index 9191764..feecb6d 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@cap-js/attachments-sdm", + "name": "@cap-js/sdm", "version": "1.0.0", "description": "CAP plugin for effortless integration of CAP applications with SAP Document Management Service.", "main": "cds-plugin.js", @@ -39,8 +39,8 @@ "cds": { "requires": { "kinds": { - "attachments-sdm": { - "impl": "@cap-js/attachments-sdm/lib/sdm" + "sdm": { + "impl": "@cap-js/sdm/lib/sdm" } }, "[development]": { diff --git a/test/integration/attachments-sdm.test.js b/test/integration/attachments-sdm.test.js index 6000596..9715480 100644 --- a/test/integration/attachments-sdm.test.js +++ b/test/integration/attachments-sdm.test.js @@ -1,18 +1,18 @@ -const axios = require('axios'); -const fs = require('fs'); -const FormData = require('form-data'); -const credentials = require('./credentials.json'); +const axios = require("axios"); +const fs = require("fs"); +const FormData = require("form-data"); +const credentials = require("./credentials.json"); -describe('Attachments Integration Tests', () => { +describe("Attachments Integration Tests", () => { let token; - let sampleAttachmentID - let appUrl = credentials.appUrl + let sampleAttachmentID; + let appUrl = credentials.appUrl; let incidentID = credentials.incidentID; - let serviceName = 'processor'; - let entityName = 'Incidents'; - let srvpath = 'ProcessorService'; + let serviceName = "processor"; + let entityName = "Incidents"; + let srvpath = "ProcessorService"; - //Generating the CAP Application token + //Generating the CAP Application token beforeAll(async () => { try { const authRes = await axios.post( @@ -21,25 +21,24 @@ describe('Attachments Integration Tests', () => { { auth: { username: credentials.clientID, - password: credentials.clientSecret - } + password: credentials.clientSecret, + }, } ); token = authRes.data.access_token; - } catch (error) { expect(error).toBeUndefined; } }); - it('should create an attachment and check if it has been created', async () => { + it("should create an attachment and check if it has been created", async () => { try { const config = { - headers: { 'Authorization': "Bearer " + token } + headers: { Authorization: "Bearer " + token }, }; - - //Setting draft mode and uploading the attachment + + //Setting draft mode and uploading the attachment await axios.post( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=true)/${srvpath}.draftEdit`, { @@ -52,47 +51,52 @@ describe('Attachments Integration Tests', () => { up__ID: incidentID, filename: "sample.pdf", mimeType: "application/pdf", - createdAt: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString(), - createdBy: "integration.test@sap.com", - modifiedBy: "integraion.test@sap.com" + createdAt: new Date( + Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000 + ).toISOString(), + createdBy: "test@test.com", + modifiedBy: "test@test.com", }; response = await axios.post( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=false)/attachments`, postData, config - ) + ); if (response.data && response.data.ID) { const formDataPut = new FormData(); - const pdfStream = fs.createReadStream("./sample.pdf"); - pdfStream.on('error', (error) => console.log('Error reading file:', error)); - formDataPut.append('content', pdfStream); - sampleAttachmentID = response.data.ID - - //Uploading the actual content into the created attachment + const pdfStream = fs.createReadStream("./sample.pdf"); + pdfStream.on("error", (error) => + console.log("Error reading file:", error) + ); + formDataPut.append("content", pdfStream); + sampleAttachmentID = response.data.ID; + + //Uploading the actual content into the created attachment await axios.put( `https://${appUrl}/odata/v4/${serviceName}/Incidents_attachments(up__ID=${incidentID},ID=${sampleAttachmentID},IsActiveEntity=false)/content`, formDataPut, - config, - ); + config + ); } await axios.post( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=false)/${srvpath}.draftPrepare`, { - SideEffectsQualifier: "" + SideEffectsQualifier: "", }, config ); - config.headers['Content-Type'] = 'application/json'; - response = await axios.post(` + config.headers["Content-Type"] = "application/json"; + response = await axios.post( + ` https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=false)/${srvpath}.draftActivate`, {}, config - ); - + ); + //Checking to see whether the attachment was created response = await axios.get( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=true)/attachments(up__ID=${incidentID},ID=${sampleAttachmentID},IsActiveEntity=true)/content`, @@ -100,59 +104,57 @@ describe('Attachments Integration Tests', () => { ); expect(response.status).toBe(200); expect(response.data).toNotBeUndefined; - expect(response.headers['content-type']).toBe('application/pdf'); - } catch (error) { - expect(error).toBeUndefined; - } + expect(response.headers["content-type"]).toBe("application/pdf"); + } catch (error) { + expect(error).toBeUndefined; + } }); - it('should read the created attachment', async () => { + it("should read the created attachment", async () => { try { const config = { - headers: { 'Authorization': "Bearer " + token } - } + headers: { Authorization: "Bearer " + token }, + }; const response = await axios.get( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=true)/attachments(up__ID=${incidentID},ID=${sampleAttachmentID},IsActiveEntity=true)/content`, config ); - + expect(response.status).toBe(200); - expect(response.headers['content-type']).toBe('application/pdf'); + expect(response.headers["content-type"]).toBe("application/pdf"); } catch (error) { console.error(error); } }); - it('should respond with 400 when the attachment is not found', async () => { - let nonExistentincidentID = 'Incorrect Incident ID'; - let nonExistentID = 'Incorrect attachment ID'; - + it("should respond with 400 when the attachment is not found", async () => { + let nonExistentincidentID = "Incorrect Incident ID"; + let nonExistentID = "Incorrect attachment ID"; + try { const config = { - headers: { 'Authorization': "Bearer " + token } + headers: { Authorization: "Bearer " + token }, }; - + await axios.get( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${nonExistentincidentID},IsActiveEntity=true)/attachments(up__ID=${nonExistentincidentID},ID=${nonExistentID},IsActiveEntity=true)/content`, config ); - - throw new Error('Expected request to fail but it succeeded'); // This line will be executed if the previous line does not throw an error. - - } catch (error) { - expect(error.response.status).toBe(400); - } + + throw new Error("Expected request to fail but it succeeded"); // This line will be executed if the previous line does not throw an error. + } catch (error) { + expect(error.response.status).toBe(400); + } }); - it('should delete an attachment and make sure it doesnt exist', async () => { + it("should delete an attachment and make sure it doesnt exist", async () => { const config = { - headers: { 'Authorization': "Bearer " + token } + headers: { Authorization: "Bearer " + token }, }; - try { - - //Setting draft mode and uploading the attachment + try { + //Setting draft mode and uploading the attachment await axios.post( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=true)/${srvpath}.draftEdit`, { @@ -169,29 +171,26 @@ describe('Attachments Integration Tests', () => { await axios.post( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=false)/${srvpath}.draftPrepare`, { - SideEffectsQualifier: "" + SideEffectsQualifier: "", }, config ); - config.headers['Content-Type'] = 'application/json'; + config.headers["Content-Type"] = "application/json"; await axios.post( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=false)/${srvpath}.draftActivate`, {}, config - ); + ); - //Making sure the attachment doesn't exist + //Making sure the attachment doesn't exist response = await axios.get( `https://${appUrl}/odata/v4/${serviceName}/${entityName}(ID=${incidentID},IsActiveEntity=true)/attachments(up__ID=${incidentID},ID=${sampleAttachmentID},IsActiveEntity=true)/content`, config ); - } catch (error) { - expect(error.response.status).toBe(404); - } - + } catch (error) { + expect(error.response.status).toBe(404); + } }); - - -}); \ No newline at end of file +}); diff --git a/test/lib/util/index.test.js b/test/lib/util/index.test.js index 90f5ed1..cddbb59 100644 --- a/test/lib/util/index.test.js +++ b/test/lib/util/index.test.js @@ -76,7 +76,7 @@ describe("util", () => { it("should return attachments settings if exists", () => { cds.env = { requires: { - "attachments-sdm": { + sdm: { settings: { param1: "value1", param2: "value2",