diff --git a/.github/workflows/checkout b/.github/workflows/checkout index a591b78..a200387 100755 --- a/.github/workflows/checkout +++ b/.github/workflows/checkout @@ -1,81 +1,33 @@ #!/usr/bin/env node -/* eslint-disable no-console */ - -const [,,$1] = process.argv -const { exec } = require('child_process') -const path = require('path') -const { readFile, writeFile } = require("fs").promises; -const { join } = require("path"); - -async function remoteService () { - const TARGET_DIR = "xmpls/remote-service" - const levelUP = "../../" - const INCLUDES = ["srv", "test"] - const paths = INCLUDES.map(pattern => join(__dirname, levelUP, TARGET_DIR, pattern)) - const cmd = `cp -r ${paths.join(' ')} ${join(__dirname, levelUP)}` - await run(cmd); - - const packageJson = JSON.parse(await readFile(join(__dirname, levelUP ,"package.json"), "utf-8")); - const delta = JSON.parse(await readFile(join(__dirname, levelUP, TARGET_DIR, "package.json"), "utf-8")); - packageJson.cds.requires["API_BUSINESS_PARTNER"] = delta.cds.requires["API_BUSINESS_PARTNER"] - await writeFile(join(__dirname, levelUP, "package.json"), JSON.stringify(packageJson, null, 2), "utf-8"); - - // dependencies - const deps = [ - "@sap-cloud-sdk/connectivity", - "@sap-cloud-sdk/http-client", - "@sap-cloud-sdk/resilience", - "@sap-cloud-sdk/util" - ] - return await run(`npm add ${deps.join(" ")}`) +const { fs, path, exists, copy, read, write, append=_append } = require("@sap/cds").utils + +async function checkout (xmpl, ...requires) { + // copy content from xmpl to incidents... + await Promise.all (['app','srv','db','test'] .map (async each => { + let src = `xmpls/${xmpl}/${each}` + if (exists(src)) return await copy(src).to(each) + })) + // enhance package.json + let xmpls = await read (`xmpls/${xmpl}/package.json`) + let base = await read ('package.json') + for (let each of requires) base.cds.requires[each] = xmpls.cds.requires[each] + if (append === _append) base = _formatted(base) //> for older cds versions only + await write (base) .to ('package.json') } -async function messaging () { - const TARGET_DIR = "xmpls/messaging" - const levelUP = "../../" - const INCLUDES = ["app", "srv", "test"] - const paths = INCLUDES.map(pattern => join(__dirname, levelUP, TARGET_DIR, pattern)) - const cmd = `cp -r ${paths.join(' ')} ${join(__dirname, levelUP)}` - await run(cmd); +exports['remote-service'] = ()=> + checkout ('remote-service', 'API_BUSINESS_PARTNER') - const packageJson = JSON.parse(await readFile(join(__dirname, levelUP, "package.json"), "utf-8")); - const delta = JSON.parse(await readFile(join(__dirname, levelUP, TARGET_DIR, "package.json"), "utf-8")); - packageJson.cds.requires["API_BUSINESS_PARTNER"] = delta.cds.requires["API_BUSINESS_PARTNER"] - packageJson.cds.requires["messaging"] = delta.cds.requires["messaging"] - await writeFile(join(__dirname, levelUP, "package.json"), JSON.stringify(packageJson, null, 2), "utf-8"); +exports['messaging'] = ()=> Promise.all([ + checkout ('messaging', 'messaging', 'API_BUSINESS_PARTNER'), + append ('\n'+`using from './incidents/field';`+'\n') .to ('app/services.cds') // add email fields to UI +]) - // dependencies - const deps = [ - "@sap-cloud-sdk/connectivity", - "@sap-cloud-sdk/http-client", - "@sap-cloud-sdk/resilience", - "@sap-cloud-sdk/util", - "@sap/xb-msg-amqp-v100" - ] - return await run(`npm add ${deps.join(" ")}`) -} - -function run (cmd, silent) { - if (cmd.raw) return run (String.raw(...arguments)) - if (cmd.endsWith('--silent')) silent = cmd = cmd.slice(0,-9) - if (typeof cmd === 'string') cmd = new Promise ((done,failed) => { - const cp = exec (cmd, (e,stdout) => e ? failed(e) : done(stdout)) - if (!silent) { - cp.stdout.on ('data', d => process.stdout.write(d)) - cp.stderr.on ('data', d => process.stderr.write(d)) - } - }) - return cmd -} +if (!module.parent) exports[process.argv[2]]?.().catch(console.error) || console.log ( + `Usage: ${path.relative(process.cwd(),__filename)} ${Object.keys(exports).join('|')}` +) -switch ($1) { - - case 'remote-service': - remoteService(); - break; - case 'messaging': - messaging(); - break; - default: - console.log('Usage: ./checkout '); -} \ No newline at end of file +// ------------------------------------------------------------------------ +// fixes for missing features in older cds versions +function _append (str) { return { to: file => fs.promises.appendFile(path.join(cds.root,file), str) }} +function _formatted (json) { return JSON.stringify(json,null,2) } diff --git a/.github/workflows/create-test-branch.yml b/.github/workflows/create-test-branch.yml index fce146a..492833c 100644 --- a/.github/workflows/create-test-branch.yml +++ b/.github/workflows/create-test-branch.yml @@ -17,12 +17,12 @@ jobs: run: | npm run add-all-xmpls rm -rf xmpls - + - name: Test run: | npm i - npm test - + npm test -- --colors + - name: Extract last commit author email and name id: extract_commit_info run: | diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 3393473..f84b4da 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -26,7 +26,7 @@ jobs: with: node-version: ${{ matrix.node-version }} - run: npm i - - run: npm test + - run: npm test -- --colors push: name: Push to CDS Test branch diff --git a/cds-plugin.js b/cds-plugin.js index 4f6582f..f964a1e 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -1,4 +1,4 @@ -const cds = require("@sap/cds"); +const cds = require("@sap/cds") cds.once('bootstrap', (app) => { app.serve('/incidents/app').from(__dirname,'/app/incidents/webapp') }) diff --git a/package.json b/package.json index 89044fe..b28ff31 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,12 @@ "express": "^4" }, "devDependencies": { - "@cap-js/sqlite": "^1.7.3", - "@sap/ux-specification": "^1.120.16", - "axios": "^1.7.3", + "@cap-js/audit-logging": "^0.8.0", + "@cap-js/change-tracking": "^1.0.6", + "@cap-js/attachments": "^1.1.6", + "@cap-js/sqlite": "^1.0.1", + "@sap/ux-specification": "^1.108.4", + "axios": "^1.4.0", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", "chai-subset": "^1.6.0" @@ -27,18 +30,20 @@ "watch": "cds watch", "start": "cds-serve", "test": "npx jest --silent", - "add-attachments": "npm add @cap-js/attachments && cp -r xmpls/attachments.cds ./db && cp xmpls/attachments.test.js ./test", - "add-change-tracking": "npm add @cap-js/change-tracking && cp xmpls/change-tracking.cds ./srv && cp xmpls/change-tracking.test.js ./test", + "add-change-tracking": "npm add @cap-js/change-tracking && cp xmpls/change-tracking.cds ./srv", "add-telemetry": "npm add @cap-js/telemetry", + "add-attachments": "npm add @cap-js/attachments && cp -r xmpls/attachments.cds ./srv", "add-notifications": "npm add @cap-js/notifications && cp xmpls/alert-notifications.js ./srv && cp xmpls/notification-types.json ./srv", - "add-audit-log": "npm add @cap-js/audit-logging && cp xmpls/data-privacy.cds ./srv && cp xmpls/audit-log.test.js ./test", - "add-remote-service": "./.github/workflows/checkout remote-service", - "add-messaging": "./.github/workflows/checkout messaging", + "add-audit-log": "npm add @cap-js/audit-logging && cp xmpls/data-privacy.cds ./srv", + "add-remote-service": ".github/workflows/checkout remote-service", + "add-messaging": ".github/workflows/checkout messaging", "add-all-xmpls": "npm run add-remote-service && npm run add-messaging && npm run add-change-tracking && npm run add-audit-log", "reset": "read -p 'This will irreversibly reset your working directory including ALL files in this git repo. Continue?' -n 1 -r && echo && if [[ $REPLY =~ ^[Yy]$ ]]; then git clean -fd && git reset --hard && npm i; fi" }, "jest": { - "modulePathIgnorePatterns": ["/xmpls/"] + "modulePathIgnorePatterns": [ + "/xmpls/" + ] }, "sapux": [ "app/incidents" @@ -65,4 +70,4 @@ } }, "private": true -} +} \ No newline at end of file diff --git a/test/attachments.test.js b/test/attachments.test.js new file mode 100644 index 0000000..bf50132 --- /dev/null +++ b/test/attachments.test.js @@ -0,0 +1,73 @@ +const cds = require('@sap/cds') + +describe('Test attachments service', () => { + + const { copy, rm, exists, path } = cds.utils; cds.root = path.resolve(__dirname,'..') + beforeAll (()=> copy('xmpls/attachments.cds').to('srv/attachments.cds')) + afterAll (() => rm('srv/attachments.cds')) + + it('should have the srv/attachments.cds file in place', () => { + expect(exists('srv/attachments.cds')).to.be.true + }) + + const { GET, POST, PUT, DELETE , expect, axios} = cds.test() + axios.defaults.auth = { username: 'alice' } + + const Incidents = '/odata/v4/processor/Incidents' + const edit = 'ProcessorService.draftEdit' + const activate = 'ProcessorService.draftActivate' + const active = 'IsActiveEntity=true' + const draft = 'IsActiveEntity=false' + let ID = null, id = null + + it('should create a new incident ', async () => { + const { status, data } = await POST(`${Incidents}`, { + title: 'Urgent attention required !', + status_code: 'N' + }) + expect(status).to.equal(201) + ID = `ID=${data.ID}` //> captures the newly created Incident's ID for subsequent use... + }) + + it('should activate the draft', async () => { + const response = await POST `${Incidents}(${ID},${draft})/${activate}` + expect(response.status).to.eql(201) + }) + + it(`should edit the incident to add an attachment`, async () => { + await POST `${Incidents}(${ID},${active})/${edit}` + + // Add an attachment entry + const created = await POST (`${Incidents}(${ID},${draft})/attachments`, { + up__ID: ID, + filename: "SolarPanelReport.pdf", + mimeType: "application/pdf", + status: "Clean", + }, { headers: { 'Content-Type': 'application/json' }}) + expect(created.status).to.equal(201) + id = `ID=${created.data.ID}` //> captures the newly created Attachments's ID for subsequent use... + + // Upload the file + const uploaded = await PUT (`${Incidents}_attachments(up__${ID},${id},${draft})/content`, + require('fs').createReadStream (cds.root+'/xmpls/SolarPanelReport.pdf'), + { headers: { 'Content-Type': 'application/pdf' }} + ) + expect(uploaded.status).to.equal(204) + + // Activate the draft + const activated = await POST `${Incidents}(${ID},${draft})/${activate}` + expect(activated.status).to.eql(200) + }) + + + it('should check the uploaded file', async () => { + const { status, data} = await GET `${Incidents}(${ID},${active})/attachments(up__${ID},${id})/content` + expect(status).to.equal(200) + expect(data).to.not.be.undefined + }) + + it('should delete the incident', async () => { + const { status } = await DELETE `${Incidents}(${ID},${active})` + expect(status).to.eql(204) + }) +}) diff --git a/test/audit-log.test.js b/test/audit-log.test.js new file mode 100644 index 0000000..d0a3f84 --- /dev/null +++ b/test/audit-log.test.js @@ -0,0 +1,61 @@ +const cds = require("@sap/cds") +const e = require("express") + + +describe("Integration Test for AuditLog", () => { + + const { copy, rm, exists, path } = cds.utils; cds.root = path.resolve(__dirname,'..') + beforeAll (()=> copy('xmpls/data-privacy.cds').to('srv/data-privacy.cds')) + afterAll (() => rm('srv/data-privacy.cds')) + + it('should have the srv/data-privacy.cds file in place', () => { + expect(exists('srv/data-privacy.cds')).to.be.true + }) + + const { GET, POST, PATCH , expect, axios} = cds.test() + axios.defaults.auth = { username: 'alice' } + + let ID + let audit; beforeAll (async () => { + audit = await cds.connect.to('audit-log') + audit.on('PersonalDataModified', req => expect(req.event).to.include('PersonalDataModified')) + audit.on('SensitiveDataRead', req => expect(req.event).to.include("SensitiveDataRead")) + }) + + it("Should return list of Customers", async () => { + const {status} = await GET `/odata/v4/processor/Customers` + expect(status).to.eql(200) + }) + + + it("Should return list of Customers data by explicitly selecting the fields", async () => { + const {status} = await GET `/odata/v4/processor/Customers?$select=name` + expect(status).to.eql(200) + }) + + + it('Creating a customer with personal data', async () => { + const { status, data } = await POST (`/odata/v4/admin/Customers`, { + ID: "{{$guid}}", + firstName: "Bob", + lastName: "Builder", + email: "bob.builder@example.com" + }) + expect(status).to.equal(201) + ID = data.ID + }) + + it('Updating a customer with personal data details', async () => { + const {status} = await PATCH (`/odata/v4/admin/Customers('${ID}')`, { + "addresses": [ + { + "city": "Walldorf", + "postCode": "69190", + "streetAddress": "Dietmar-Hopp-Allee 16" + } + ] + }) + expect(status).to.equal(200) + }) + +}) diff --git a/test/basics.test.js b/test/basics.test.js index 99eaf63..52787da 100644 --- a/test/basics.test.js +++ b/test/basics.test.js @@ -1,23 +1,25 @@ -const cds = require('@sap/cds/lib') -const { GET, expect, axios } = cds.test(__dirname + '/..', '--with-mocks') - -axios.defaults.auth = { username: 'alice' } +const cds = require('@sap/cds') describe('Test The GET Endpoints', () => { + const { GET, expect, axios } = cds.test(__dirname+'/..') + axios.defaults.auth = { username: 'alice' } + it('Should check Processor Service', async () => { - const processorService = await cds.connect.to('ProcessorService') - const { Incidents } = processorService.entities - expect(await SELECT.from(Incidents)).to.have.length(4) + let srv = await cds.connect.to('ProcessorService') + let {Incidents} = srv.entities + let incidents = await SELECT.from(Incidents) + expect(incidents).to.have.length(4) }) it('Should check Customers', async () => { - const processorService = await cds.connect.to('ProcessorService') - const { Customers } = processorService.entities - expect(await SELECT.from(Customers)).to.have.length(3) + let srv = await cds.connect.to('ProcessorService') + let {Customers} = srv.entities + let customers = await SELECT.from(Customers) + expect(customers).to.have.length(3) }) it('Test Expand Entity Endpoint', async () => { - const { data } = await GET`/odata/v4/processor/Customers?$select=firstName&$expand=incidents` + let {data} = await GET `/odata/v4/processor/Customers?$select=firstName&$expand=incidents` expect(data).to.be.an('object') }) }) diff --git a/test/change-tracking.test.js b/test/change-tracking.test.js new file mode 100644 index 0000000..f6daee6 --- /dev/null +++ b/test/change-tracking.test.js @@ -0,0 +1,140 @@ +const cds = require("@sap/cds") + +describe("Integration Test for ChangeTracking", () => { + + const { copy, rm, exists, path } = cds.utils; cds.root = path.resolve(__dirname,'..') + beforeAll (()=> copy('xmpls/change-tracking.cds').to('srv/change-tracking.cds')) + afterAll (() => rm('srv/change-tracking.cds')) + + it('should have the srv/change-tracking.cds file in place', () => { + expect(exists('srv/change-tracking.cds')).to.be.true + }) + + const { GET, POST, PATCH, DELETE, expect, axios} = cds.test() + axios.defaults.auth = { username: 'alice' } + + const Incidents = '/odata/v4/processor/Incidents' + const edit = 'ProcessorService.draftEdit' + const activate = 'ProcessorService.draftActivate' + const active = 'IsActiveEntity=true' + const draft = 'IsActiveEntity=false' + let ID = null, id = null + + let processorService = null + let ChangeView = null + + beforeAll(async () => { + processorService = await cds.connect.to('ProcessorService') + ChangeView = processorService.entities.ChangeView + }) + + it('Create an incident ', async () => { + const { status, data } = await POST (`${Incidents}`, { + title: 'Urgent attention required !', + status_code: 'N', + customer: { ID: '1004100' } + }) + expect(status).to.equal(201) + ID = `ID=${id=data.ID}` //> captures the newly created Incident's ID for subsequent use... + }) + + it('+ Activate the draft & check Urgency code as H using custom logic', async () => { + const response = await POST `${Incidents}(${ID},${draft})/${activate}` + expect(response.status).to.eql(201) + expect(response.data.urgency_code).to.eql('H') + }) + + it('+ Test the incident status', async () => { + const { status, data } = await GET `${Incidents}(${ID},${active})` + expect(status).to.eql(200) + expect(data.status_code).to.eql('N') + }) + + it('+ Test the title detail in ChangeView', async () => { + const changes = await SELECT.from(ChangeView).where({ + entity: "sap.capire.incidents.Incidents", + attribute: "title", + }) + expect(changes.length).to.equal(1) + const [change] = changes + expect(change.entityKey).to.equal(id) + expect(change.attribute).to.equal("title") + expect(change.modification).to.equal("create") + expect(change.valueChangedFrom).to.equal("") + expect(change.valueChangedTo).to.equal("Urgent attention required !") + }) + + it('+ Test the status detail in ChangeView', async () => { + const changes = await SELECT.from(ChangeView).where({ + entity: "sap.capire.incidents.Incidents", + attribute: "status", + }) + expect(changes.length).to.equal(1) + const [change] = changes + expect(change.entityKey).to.equal(id) + expect(change.attribute).to.equal("status") + expect(change.modification).to.equal("create") + expect(change.valueChangedFrom).to.equal("") + expect(change.valueChangedTo).to.equal("N") + }) + + it('+ Test the customer detail in ChangeView', async () => { + const changes = await SELECT.from(ChangeView).where({ + entity: "sap.capire.incidents.Incidents", + attribute: "customer", + }) + expect(changes.length).to.equal(1) + const [change] = changes + expect(change.entityKey).to.equal(id) + expect(change.attribute).to.equal("customer") + expect(change.modification).to.equal("create") + expect(change.valueChangedFrom).to.equal("") + expect(change.valueChangedTo).to.equal("Sunny Sunshine") + }) + + describe("Test Changes for Update Incident", () => { + it(`Should Close the Incident-${id}`, async ()=>{ + const {status} = await POST `${Incidents}(${ID},${active})/${edit}` + expect(status).to.equal(201) + }) + it(`Should Close the Incident-${id}`, async ()=>{ + const {status} = await PATCH (`${Incidents}(${ID},${draft})`,{status_code: 'C'}) + expect(status).to.equal(200) + }) + it('+ Activate the draft & check Status code as C using custom logic', async () => { + const {status} = await POST `${Incidents}(${ID},${draft})/${activate}` + expect(status).to.eql(200) + }) + it('+ Test the status detail in ChangeView', async () => { + // await GET `${Incidents}?$filter=ID eq ${id}` + const changes = await SELECT.from(ChangeView).where({ + entity: "sap.capire.incidents.Incidents", + attribute: "status", + modification: 'update', + }) + expect(changes.length).to.equal(1) + const [change] = changes + expect(change.entityKey).to.equal(id) + expect(change.attribute).to.equal("status") + expect(change.modification).to.equal("update") + expect(change.valueChangedFrom).to.equal("N") + expect(change.valueChangedTo).to.equal("C") + }) + }) + + describe("Test Changes for Delete Incident", () => { + it('- Delete the Incident', async () => { + const {status} = await DELETE `${Incidents}(${ID},${active})` + expect(status).to.eql(204) + }) + + it('+ Test the status detail in ChangeView', async () => { + const changes = await SELECT.from(ChangeView).where({ + entity: "sap.capire.incidents.Incidents", + attribute: "status", + }) + expect(changes.length).to.equal(0) + }) + }) + +}) diff --git a/test/drafts.test.js b/test/drafts.test.js index 68dc32d..fa6c37c 100644 --- a/test/drafts.test.js +++ b/test/drafts.test.js @@ -1,94 +1,72 @@ -const cds = require('@sap/cds/lib') -const { GET, POST, DELETE, PATCH, expect, axios } = cds.test(__dirname + '/..', '--with-mocks') - -axios.defaults.auth = { username: 'alice' } +const cds = require('@sap/cds') describe('Draft Choreography APIs', () => { - let draftId, incidentId - it('Create a new incident', async () => { - const { status, statusText, data } = await POST(`/odata/v4/processor/Incidents`, { + const { GET, POST, DELETE, PATCH, expect, axios } = cds.test(__dirname+'/..') + axios.defaults.auth = { username: 'alice' } + + const Incidents = '/odata/v4/processor/Incidents' + const edit = 'ProcessorService.draftEdit' + const activate = 'ProcessorService.draftActivate' + const active = 'IsActiveEntity=true' + const draft = 'IsActiveEntity=false' + let ID + + it('should create a new incident', async () => { + const { status, data } = await POST (`${Incidents}`, { title: 'Urgent attention required !', status_code: 'N' }) - draftId = data.ID expect(status).to.equal(201) - expect(statusText).to.equal('Created') + ID = `ID=${data.ID}` //> captures the newly created Incident's ID for subsequent use... }) - it('Save the draft & check urgency code as H using custom logic', async () => { - const response = await POST( - `/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)/ProcessorService.draftActivate` - ) - expect(response.status).to.eql(201) - expect(response.data.urgency_code).to.eql('H') + it('should save the draft & check urgency code as H using custom logic', async () => { + const { status, data } = await POST `${Incidents}(${ID},${draft})/${activate}` + expect(status).to.eql(201) + expect(data.urgency_code).to.eql('H') }) it ('should test the incident status', async () => { - const { - status, - data: { status_code, ID } - } = await GET(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)`) - incidentId = ID + const { status, data } = await GET `${Incidents}(${ID},${active})` expect(status).to.eql(200) - expect(status_code).to.eql('N') + expect(data.status_code).to.eql('N') }) it ('should edit the incident again', async () => { - const { status } = await POST( - `/odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=true)/ProcessorService.draftEdit`, - { - PreserveChanges: true - } - ) + const {status} = await POST `${Incidents}(${ID},${active})/${edit}` expect(status).to.equal(201) }) it (`should set status to closed`, async () => { - const { status } = await PATCH(`/odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=false)`, { - status_code: 'C' - }) + const { status } = await PATCH (`${Incidents}(${ID},${draft})`, { status_code: 'C' }) expect(status).to.equal(200) }) it ('should save the draft & check status code as C using custom logic', async () => { - const response = await POST( - `/odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=false)/ProcessorService.draftActivate` - ) - expect(response.status).to.eql(200) + const {status} = await POST `${Incidents}(${ID},${draft})/${activate}` + expect(status).to.eql(200) }) it ('should test the incident status to be closed', async () => { - const { - status, - data: { status_code } - } = await GET(`/odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=true)`) + const { status, data } = await GET `${Incidents}(${ID},${active})` expect(status).to.eql(200) - expect(status_code).to.eql('C') + expect(data.status_code).to.eql('C') }) - it (`should re-open the closed incident-${draftId}`, async () => { - const { status } = await POST( - `/odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=true)/ProcessorService.draftEdit`, - { - PreserveChanges: true - } - ) + it (`should re-open the closed incident-${ID}`, async () => { + const {status} = await POST `${Incidents}(${ID},${active})/${edit}` expect(status).to.equal(201) }) it (`should fail setting the status to 'N'`, async () => { - const { status } = await PATCH(`/odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=false)`, { - status_code: 'N' - }) + const {status} = await PATCH (`${Incidents}(${ID},${draft})`, { status_code: 'N' }) expect(status).to.equal(200) }) it ('should fail to save drafts for closed incidents', async () => { try { - await POST( - `/odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=false)/ProcessorService.draftActivate` - ) + await POST `${Incidents}(${ID},${draft})/${activate}` } catch (error) { expect(error.response.status).to.eql(500) expect(error.response.data.error.message).to.include(`Can't modify a closed incident`) @@ -96,12 +74,13 @@ describe('Draft Choreography APIs', () => { }) it ('should delete the Draft', async () => { - const response = await DELETE(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)`) - expect(response.status).to.eql(204) + const {status} = await DELETE `${Incidents}(${ID},${draft})` + expect(status).to.eql(204) }) it ('should delete the Incident', async () => { - const response = await DELETE(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)`) - expect(response.status).to.eql(204) + const {status} = await DELETE `${Incidents}(${ID},${active})` + expect(status).to.eql(204) }) + }) diff --git a/test/messaging.test.js b/test/messaging.test.js new file mode 100644 index 0000000..244cfea --- /dev/null +++ b/test/messaging.test.js @@ -0,0 +1,113 @@ +const cds = require("@sap/cds") + +describe("Integration Test for Eventing", () => { + + const { GET, POST, PATCH, expect, axios } = cds.test(__dirname + '/../xmpls/messaging','--with-mocks') + axios.defaults.auth = { username: "alice" } + + const bupa = '/odata/v4/api-business-partner' + const Incidents = '/odata/v4/processor/Incidents' + const edit = 'ProcessorService.draftEdit' + const activate = 'ProcessorService.draftActivate' + const active = 'IsActiveEntity=true' + const draft = 'IsActiveEntity=false' + let ID, id + + describe("GET should return 200", () => { + + it("Should return list of Business Partners", async () => { + const { status } = await GET `${bupa}/A_BusinessPartner` + expect(status).to.eql(200) + }) + + it("Should return list of Business Partners Address", async () => { + const { status } = await GET `${bupa}/A_BusinessPartnerAddress` + expect(status).to.eql(200) + }) + + it("Should return list of Business Partners Email Address", async () => { + const { status } = await GET `${bupa}/A_AddressEmailAddress` + expect(status).to.eql(200) + }) + + it("Should return list of Business Partners Address PhoneNumber", async () => { + const { status } = await GET `${bupa}/A_AddressPhoneNumber` + expect(status).to.eql(200) + }) + + }) + + + describe('Draft Choreography APIs', () => { + + it('Create an incident ', async () => { + let { status, data } = await POST(`${Incidents}`, { + title: 'Urgent attention required !', + status_code: 'N', + customer: { ID: '1004100' } + }) + expect(status).to.equal(201) + ID = `ID=${id=data.ID}` //> captures the newly created Incident's ID for subsequent use... + }) + + it('+ Activate the draft & check Urgency code as H using custom logic', async () => { + let { status, data } = await POST `${Incidents}(${ID},${draft})/${activate}` + expect(status).to.eql(201) + expect(data.urgency_code).to.eql('H') + }) + + it('+ Test the customer detail', async () => { + let { status, data } = await GET `${Incidents}?$filter=ID eq ${id}` + expect(status).to.eql(200) + expect(data.value).to.exist + expect(data.value[0]).to.contains({ + customer_ID: '1004100' + }) + }) + + describe("Create annd Update Business Partner", () => { + + it("Update Business Partner", async () => { + let { status } = await PATCH(`${bupa}/A_BusinessPartner('1004100')`, { + to_BusinessPartnerAddress: [{ + AddressID: "457", + to_EmailAddress: [{ + AddressID: "457", + Person: "johnson", + OrdinalNumber: "334", + EmailAddress: "sunny@test.com" + }] + }] + }) + expect(status).to.eql(200) + }) + + it("Verify the Address of Business Partner", async () => { + let { status, data } = await GET `${bupa}/A_BusinessPartnerAddress?$filter=BusinessPartner eq '1004100'` + expect(status).to.eql(200) + expect(data.value).to.exist + expect(data.value[0]).to.contains({ + AddressID: "457", + }) + }) + + it("Verify the Email address of Business Partner", async () => { + let { status, data } = await GET `${bupa}/A_AddressEmailAddress?$filter=AddressID eq '457'` + expect(status).to.eql(200) + expect(data.value).to.exist + expect(data.value[0]).to.contains({ + AddressID: "457", + Person: "johnson", + OrdinalNumber: "334", + EmailAddress: "sunny@test.com" + }) + }) + + }) + + it(`Should Close the Incident-${ID}`, async () => { + const { status } = await POST `${Incidents}(${ID},${active})/${edit}` + expect(status).to.equal(201) + }) + }) +}) diff --git a/test/remote-service.test.js b/test/remote-service.test.js new file mode 100644 index 0000000..9f63ae4 --- /dev/null +++ b/test/remote-service.test.js @@ -0,0 +1,87 @@ +const cds = require("@sap/cds") + +describe("Integration Test for Remote Service", () => { + + const { GET, POST, PATCH , expect, axios} = cds.test(__dirname + '/../xmpls/remote-service', '--with-mocks') + axios.defaults.auth = { username: 'alice' } + + const bupa = '/odata/v4/api-business-partner' + const Incidents = '/odata/v4/processor/Incidents' + const edit = 'ProcessorService.draftEdit' + const activate = 'ProcessorService.draftActivate' + const active = 'IsActiveEntity=true' + const draft = 'IsActiveEntity=false' + let ID = null, id = null + + describe("Test the BusinessPartner GET Endpoints", () => { + + it("Should return list of Business Partners", async () => { + const {status} = await GET `${bupa}/A_BusinessPartner` + expect(status).to.eql(200) + }) + + it("Should return list of Business Partners Address", async () => { + const {status} = await GET `${bupa}/A_BusinessPartnerAddress` + expect(status).to.eql(200) + }) + it("Should return list of Business Partners Email Address", async () => { + const {status} = await GET `${bupa}/A_AddressEmailAddress` + expect(status).to.eql(200) + }) + it("Should return list of Business Partners Address PhoneNumber", async () => { + const {status} = await GET `${bupa}/A_AddressPhoneNumber` + expect(status).to.eql(200) + }) + }) + + describe('Draft Choreography APIs', () => { + it('Create an incident ', async () => { + const { status, data } = await POST (`${Incidents}`, { + title: 'Urgent attention required !', + status_code: 'N', + "customer": { ID: "1004100" } + }) + expect(status).to.equal(201) + ID = `ID=${id=data.ID}` //> captures the newly created Incident's ID for subsequent use... + }) + it('+ Activate the draft & check Urgency code as H using custom logic', async () => { + const { status, data} = await POST `${Incidents}(${ID},${draft})/${activate}` + expect(status).to.eql(201) + expect(data.urgency_code).to.eql('H') + }) + it('+ Test the customer detail', async () => { + const { status, data } = await GET(`${Incidents}?$filter=ID eq ${id}`) + expect(status).to.eql(200) + expect(data.value).to.exist + expect(data.value[0]).to.contains({ + "customer_ID": "1004100" + }) + }) + + describe("Create and Update Business Partner", () => { + it("Creates a new Business Partner", async () => { + const {status} = await POST (`${bupa}/A_BusinessPartner`, { + BusinessPartner: "17100015", + BusinessPartnerIsBlocked: true, + BusinessPartnerFullName: "John Doee", + }) + expect(status).to.eql(201) + }) + it("Update Business Partner", async () => { + const {status} = await PATCH (`${bupa}/A_BusinessPartner('17100015')`, { + BusinessPartnerIsBlocked: false + }) + expect(status).to.eql(200) + }) + }) + + it(`Should Close the Incident-${id}`, async () => { + const {status} = await POST `${Incidents}(${ID},${active})/${edit}` + expect(status).to.equal(201) + }) + it(`Update Business Partner details of the Incident`, async () => { + const {status} = await PATCH(`${Incidents}(${ID},${draft})`, { customer_ID: '17100015' }) + expect(status).to.equal(200) + }) + }) +}) diff --git a/xmpls/attachments.cds b/xmpls/attachments.cds index f58acd5..e08e571 100644 --- a/xmpls/attachments.cds +++ b/xmpls/attachments.cds @@ -1,5 +1,5 @@ -using { sap.capire.incidents as my } from './schema'; +using { sap.capire.incidents as my } from '../db/schema'; using { Attachments } from '@cap-js/attachments'; extend my.Incidents with { diff --git a/xmpls/attachments.test.js b/xmpls/attachments.test.js deleted file mode 100644 index eec6b76..0000000 --- a/xmpls/attachments.test.js +++ /dev/null @@ -1,83 +0,0 @@ -const cds = require('@sap/cds') -const { GET, POST, PUT, DELETE , expect, axios} = cds.test(__dirname + '/..', '--with-mocks') -const { createReadStream } = cds.utils.fs; -const { join } = cds.utils.path; -axios.defaults.auth = { username: 'alice' } - -jest.setTimeout(11111) - -describe('Test attachments service', () => { - let draftId = null; - let docId = null; - - it('Create an incident ', async () => { - const { status, statusText, data } = await POST(`/odata/v4/processor/Incidents`, { - title: 'Urgent attention required !', - status_code: 'N' - }) - draftId = data.ID - expect(status).to.equal(201) - expect(statusText).to.equal('Created') - }) - - it('+ Activate the draft', async () => { - const response = await POST( - `/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)/ProcessorService.draftActivate` - ) - expect(response.status).to.eql(201) - - }) - - - describe('Test the file upload', () => { - it(`Should Close the Incident-${draftId}`, async () => { - const { status } = await POST( - `/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)/ProcessorService.draftEdit`, - { - PreserveChanges: true - } - ) - - - const content = createReadStream(join(__dirname, "../xmpls/SolarPanelReport.pdf")); - const attachRes = await POST(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)/attachments`, - { - up__ID: draftId, - filename: "SolarPanelReport.pdf", - mimeType: "application/pdf", - status: "Clean", - createdAt: new Date(), - }, { headers: { 'Content-Type': 'application/json' } }); - - console.log(attachRes); - docId = attachRes.data.ID; - console.log("doc id"+docId); - // Upload the file content with PUT - const uploadResp = await PUT( - `/odata/v4/processor/Incidents_attachments(up__ID=${draftId},ID=${docId},IsActiveEntity=false)/content`, - content, - { headers: { 'Content-Type': 'application/pdf' } } - ); - expect(uploadResp.status).to.equal(204); - // add attachments here - - - const response = await POST( - `/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)/ProcessorService.draftActivate` - ) - expect(response.status).to.eql(200) - }) - - - }) - it('Check the uploaded file', async () => { - const response = await GET(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)/attachments(up__ID=${draftId},ID=${docId},IsActiveEntity=true)/content`); - expect(response.status).to.equal(200); - expect(response.data).to.not.be.undefined; - }) - - it('- Delete the Incident', async () => { - const response = await DELETE(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)`) - expect(response.status).to.eql(204) - }) -}) diff --git a/xmpls/audit-log.test.js b/xmpls/audit-log.test.js deleted file mode 100644 index 3de685c..0000000 --- a/xmpls/audit-log.test.js +++ /dev/null @@ -1,62 +0,0 @@ -const cds = require("@sap/cds"); -const { GET, POST, PATCH, DELETE, expect, axios, assert } = cds.test(__dirname + '/..', '--with-mocks') - -axios.defaults.auth = { username: "alice" }; -describe("Integration Test for AuditLog", () => { - let customerID,audit; - beforeAll(async () => { - audit = await cds.connect.to('audit-log') - }); - - it("Should return list of Customers", async () => { - const response = await GET("/odata/v4/processor/Customers"); - - audit.on('SensitiveDataRead', function (req) { - const { event, data } = req - assert.ok(event.includes("SensitiveDataRead")) - }) - expect(response.status).to.eql(200); - }); - - - it("Should return list of Customers data by explicitly selecting the fields", async () => { - const response = await GET("/odata/v4/processor/Customers?$select=name"); - expect(response.status).to.eql(200); - }); - - - it('Creating a customer with personal data', async () => { - const response = await POST(`/odata/v4/admin/Customers`, { - ID: "{{$guid}}", - firstName: "Bob", - lastName: "Builder", - email: "bob.builder@example.com" - }); - audit.on('PersonalDataModified', function (req) { - const { event, data } = req - assert.ok(event.includes("PersonalDataModified")) - }) - customerID = response.data.ID; - expect(response.status).to.equal(201); - }); - - - it('Updating a customer with personal data details', async () => { - const audit = await cds.connect.to('audit-log') - audit.on('PersonalDataModified', function (req) { - const { event, data } = req - assert.ok(event.includes("PersonalDataModified")) - }) - const response = await PATCH(`/odata/v4/admin/Customers('${customerID}')`, { - "addresses": [ - { - "city": "Walldorf", - "postCode": "69190", - "streetAddress": "Dietmar-Hopp-Allee 16" - } - ] - }); - expect(response.status).to.equal(200); - }); - -}); diff --git a/xmpls/change-tracking.test.js b/xmpls/change-tracking.test.js deleted file mode 100644 index 89bf809..0000000 --- a/xmpls/change-tracking.test.js +++ /dev/null @@ -1,133 +0,0 @@ -const cds = require("@sap/cds"); -const { GET, POST, PATCH, DELETE , expect, axios} = cds.test(__dirname + '/..', '--with-mocks') - -describe("Integration Test for ChangeTracking", () => { - let draftId,incidentId; - axios.defaults.auth = { username: "alice" }; - let processorService = null; - let ChangeView = null; - beforeAll(async () => { - processorService = await cds.connect.to('ProcessorService'); - ChangeView = processorService.entities.ChangeView; - }); - it('Create an incident ', async () => { - const { status, statusText, data } = await POST(`/odata/v4/processor/Incidents`, { - title: 'Urgent attention required !', - status_code: 'N', - "customer": {ID:"1004100"} - }); - draftId = data.ID; - expect(status).to.equal(201); - expect(statusText).to.equal('Created'); - }); - - it('+ Activate the draft & check Urgency code as H using custom logic', async () => { - const response = await POST( - `/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)/ProcessorService.draftActivate` - ); - expect(response.status).to.eql(201); - expect(response.data.urgency_code).to.eql('H'); - }); - - it('+ Test the incident status', async () => { - const { status, data: { status_code, ID } } = await GET(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)`); - incidentId = ID; - expect(status).to.eql(200); - expect(status_code).to.eql('N'); - }); - - it('+ Test the title detail in ChangeView', async () => { - const response = await GET(`/odata/v4/processor/Incidents?$filter=ID eq ${draftId}`); - const incidentChanges = await SELECT.from(ChangeView).where({ - entity: "sap.capire.incidents.Incidents", - attribute: "title", - }) - expect(incidentChanges.length).to.equal(1); - const incidentChange = incidentChanges[0]; - expect(incidentChange.entityKey).to.equal(draftId); - expect(incidentChange.attribute).to.equal("title"); - expect(incidentChange.modification).to.equal("create"); - expect(incidentChange.valueChangedFrom).to.equal(""); - expect(incidentChange.valueChangedTo).to.equal("Urgent attention required !"); - }); - - it('+ Test the status detail in ChangeView', async () => { - const response = await GET(`/odata/v4/processor/Incidents?$filter=ID eq ${draftId}`); - const incidentChanges = await SELECT.from(ChangeView).where({ - entity: "sap.capire.incidents.Incidents", - attribute: "status", - }) - expect(incidentChanges.length).to.equal(1); - const incidentChange = incidentChanges[0]; - expect(incidentChange.entityKey).to.equal(draftId); - expect(incidentChange.attribute).to.equal("status"); - expect(incidentChange.modification).to.equal("create"); - expect(incidentChange.valueChangedFrom).to.equal(""); - expect(incidentChange.valueChangedTo).to.equal("N"); - }); - - it('+ Test the customer detail in ChangeView', async () => { - const response = await GET(`/odata/v4/processor/Incidents?$filter=ID eq ${draftId}`); - const incidentChanges = await SELECT.from(ChangeView).where({ - entity: "sap.capire.incidents.Incidents", - attribute: "customer", - }) - expect(incidentChanges.length).to.equal(1); - const incidentChange = incidentChanges[0]; - expect(incidentChange.entityKey).to.equal(draftId); - expect(incidentChange.attribute).to.equal("customer"); - expect(incidentChange.modification).to.equal("create"); - expect(incidentChange.valueChangedFrom).to.equal(""); - expect(incidentChange.valueChangedTo).to.equal("Sunny Sunshine"); - }); - - describe("Test Changes for Update Incident", () => { - it(`Should Close the Incident-${draftId}`, async ()=>{ - const {status} = await POST(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)/ProcessorService.draftEdit`, - { - "PreserveChanges": true - }); - expect(status).to.equal(201); - }); - it(`Should Close the Incident-${draftId}`, async ()=>{ - const {status } = await PATCH(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)`,{status_code: 'C'}); - expect(status).to.equal(200); - }); - it('+ Activate the draft & check Status code as C using custom logic', async () => { - const response = await POST( - `/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)/ProcessorService.draftActivate` - ); - expect(response.status).to.eql(200); - }); - it('+ Test the status detail in ChangeView', async () => { - const response = await GET(`/odata/v4/processor/Incidents?$filter=ID eq ${draftId}`); - const incidentChanges = await SELECT.from(ChangeView).where({ - entity: "sap.capire.incidents.Incidents", - attribute: "status", - modification: 'update', - }) - expect(incidentChanges.length).to.equal(1); - const incidentChange = incidentChanges[0]; - expect(incidentChange.entityKey).to.equal(draftId); - expect(incidentChange.attribute).to.equal("status"); - expect(incidentChange.modification).to.equal("update"); - expect(incidentChange.valueChangedFrom).to.equal("N"); - expect(incidentChange.valueChangedTo).to.equal("C"); - }); - }); - - describe("Test Changes for Delete Incident", () => { - it('- Delete the Incident', async () => { - const response = await DELETE(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)`); - expect(response.status).to.eql(204); - }); - - it('+ Test the status detail in ChangeView', async () => { - const incidentChanges = await SELECT.from(ChangeView).where({ - entity: "sap.capire.incidents.Incidents", - attribute: "status", - }) - expect(incidentChanges.length).to.equal(0); - }); - }); -}); diff --git a/xmpls/messaging/app/fields.cds b/xmpls/messaging/app/fields.cds deleted file mode 100644 index cdc5648..0000000 --- a/xmpls/messaging/app/fields.cds +++ /dev/null @@ -1 +0,0 @@ -using from './incidents/field'; \ No newline at end of file diff --git a/xmpls/messaging/package.json b/xmpls/messaging/package.json index 7759e67..1cad7f2 100644 --- a/xmpls/messaging/package.json +++ b/xmpls/messaging/package.json @@ -98,10 +98,10 @@ "messaging": { "kind": "local-messaging", "[production]": { - "kind": "enterprise-messaging-shared", - "format": "cloudevents" + "kind": "enterprise-messaging-shared", + "format": "cloudevents" } - } + } } } } \ No newline at end of file diff --git a/xmpls/messaging/services.cds b/xmpls/messaging/services.cds new file mode 100644 index 0000000..ebe3849 --- /dev/null +++ b/xmpls/messaging/services.cds @@ -0,0 +1,3 @@ +// Using local service implementation +using { ProcessorService } from '../../app/services'; +annotate ProcessorService with @impl: 'srv/services.js'; diff --git a/xmpls/messaging/srv/remote.cds b/xmpls/messaging/srv/mashup.cds similarity index 82% rename from xmpls/messaging/srv/remote.cds rename to xmpls/messaging/srv/mashup.cds index a03ea8e..704189d 100644 --- a/xmpls/messaging/srv/remote.cds +++ b/xmpls/messaging/srv/mashup.cds @@ -1,9 +1,6 @@ -/** -* Expose Remote Services with associations. Eventing/Messaging sample is an extension on top -* of Remote Service integration. This file is same as in Remote Service Sample -*/ +// REVISIT: This file and all in ./external is copy from ../remote-service -> should be refactored +// REVISIT: This is not in line with our best practices. using { API_BUSINESS_PARTNER as S4 } from './external/API_BUSINESS_PARTNER'; - service RemoteService { entity BusinessPartner as projection on S4.A_BusinessPartner { key BusinessPartner as ID, @@ -27,4 +24,4 @@ service RemoteService { key AddressID as addressId, PhoneNumber as phone } -} \ No newline at end of file +} diff --git a/xmpls/messaging/srv/services.js b/xmpls/messaging/srv/services.js index e170a96..f5f7189 100644 --- a/xmpls/messaging/srv/services.js +++ b/xmpls/messaging/srv/services.js @@ -1,6 +1,6 @@ -/** -* Same as Remote Service Sample. Added additional Handlers for recieving and -* handling events/messages +/** +* Same as Remote Service Sample. Added additional Handlers for recieving and +* handling events/messages */ const cds = require('@sap/cds') @@ -12,7 +12,7 @@ class ProcessorService extends cds.ApplicationService { this.on('READ', 'Customers', (req) => this.onCustomerRead(req)); this.on(['CREATE','UPDATE'], 'Incidents', (req, next) => this.onCustomerCache(req, next)); this.S4bupa = await cds.connect.to('API_BUSINESS_PARTNER'); - this.remoteService = await cds.connect.to("RemoteService"); + this.remoteService = await cds.connect.to("RemoteService"); // REVISIT: What is this for? // Added Handlers for Eventing on top of remote service sample this.messaging = await cds.connect.to('messaging'); @@ -58,7 +58,7 @@ class ProcessorService extends cds.ApplicationService { }) }) }).where({ ID: newCustomerId })); - + if(customer) { customer.email = customer.addresses[0]?.email[0]?.email; customer.phone = customer.addresses[0]?.phoneNumber[0]?.phone; @@ -75,7 +75,7 @@ class ProcessorService extends cds.ApplicationService { console.log('>> delegating to S4 service...', req.query); const top = parseInt(req._queryOptions?.$top) || 100; const skip = parseInt(req._queryOptions?.$skip) || 0; - + const { BusinessPartner } = this.remoteService.entities; // Expands are required as the runtime does not support path expressions for remote services @@ -88,7 +88,7 @@ class ProcessorService extends cds.ApplicationService { }); }) }).limit(top, skip)); - + result = result.map((bp) => ({ ID: bp.ID, name: bp.name, @@ -100,7 +100,7 @@ class ProcessorService extends cds.ApplicationService { console.log("after result", result); return result; } - + changeUrgencyDueToSubject(data) { if (data) { diff --git a/xmpls/messaging/test/messaging.test.js b/xmpls/messaging/test/messaging.test.js deleted file mode 100644 index 3e8dfb2..0000000 --- a/xmpls/messaging/test/messaging.test.js +++ /dev/null @@ -1,106 +0,0 @@ -const cds = require("@sap/cds"); -const { GET, POST, PATCH, DELETE, expect, axios } = cds.test(__dirname + '/..', '--with-mocks') - -describe("Integration Test for Eventing", () => { - let draftId,incidentId; - axios.defaults.auth = { username: "alice" }; - describe("GET should return 200", () => { - - it("Should return list of Business Partners", async () => { - const response = await GET("/odata/v4/api-business-partner/A_BusinessPartner"); - expect(response.status).to.eql(200); - }); - - it("Should return list of Business Partners Address", async () => { - const response = await GET("/odata/v4/api-business-partner/A_BusinessPartnerAddress"); - expect(response.status).to.eql(200); - }); - it("Should return list of Business Partners Email Address", async () => { - const response = await GET("/odata/v4/api-business-partner/A_AddressEmailAddress"); - expect(response.status).to.eql(200); - }); - it("Should return list of Business Partners Address PhoneNumber", async () => { - const response = await GET("/odata/v4/api-business-partner/A_AddressPhoneNumber"); - expect(response.status).to.eql(200); - }); - }); - - describe('Draft Choreography APIs', () => { - it('Create an incident ', async () => { - const { status, statusText, data } = await POST(`/odata/v4/processor/Incidents`, { - title: 'Urgent attention required !', - status_code: 'N', - "customer": {ID:"1004100"} - }); - draftId = data.ID; - expect(status).to.equal(201); - expect(statusText).to.equal('Created'); - }); - it('+ Activate the draft & check Urgency code as H using custom logic', async () => { - const response = await POST( - `/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)/ProcessorService.draftActivate` - ); - expect(response.status).to.eql(201); - expect(response.data.urgency_code).to.eql('H'); - }); - it('+ Test the customer detail', async () => { - const response = await GET(`/odata/v4/processor/Incidents?$filter=ID eq ${draftId}`); - expect(response.status).to.eql(200); - expect(response.data.value).to.exist; - expect(response.data.value[0]).to.contains({ - "customer_ID": "1004100" - }); - incidentId = response.data.ID; - }); - - describe("Create annd Update Business Partner", () => { - it("Update Business Partner", async () => { - const response = await PATCH( - `/odata/v4/api-business-partner/A_BusinessPartner('1004100')`, - { - to_BusinessPartnerAddress: [{ - AddressID: "457", - to_EmailAddress:[{ - AddressID: "457", - Person: "johnson", - OrdinalNumber: "334", - EmailAddress: "sunny@test.com" - }] - }] - } - ); - expect(response.status).to.eql(200); - }); - describe("Verify the updated Business Partner", () => { - it("Verify the Address of Business Partner", async () => { - const response = await GET(`/odata/v4/api-business-partner/A_BusinessPartnerAddress?$filter=BusinessPartner eq '1004100'`); - expect(response.status).to.eql(200); - expect(response.data.value).to.exist; - expect(response.data.value[0]).to.contains({ - AddressID: "457", - }); - }); - - it("Verify the Email address of Business Partner", async () => { - const response = await GET(`/odata/v4/api-business-partner/A_AddressEmailAddress?$filter=AddressID eq '457'`); - expect(response.status).to.eql(200); - expect(response.data.value).to.exist; - expect(response.data.value[0]).to.contains({ - AddressID: "457", - Person: "johnson", - OrdinalNumber: "334", - EmailAddress: "sunny@test.com" - }); - }); - }); - }); - - it(`Should Close the Incident-${draftId}`, async ()=>{ - const {status} = await POST(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)/ProcessorService.draftEdit`, - { - "PreserveChanges": true - }); - expect(status).to.equal(201); - }); - }); -}); diff --git a/xmpls/messaging/req.http b/xmpls/messaging/test/req.http similarity index 100% rename from xmpls/messaging/req.http rename to xmpls/messaging/test/req.http diff --git a/xmpls/remote-service/services.cds b/xmpls/remote-service/services.cds new file mode 100644 index 0000000..ebe3849 --- /dev/null +++ b/xmpls/remote-service/services.cds @@ -0,0 +1,3 @@ +// Using local service implementation +using { ProcessorService } from '../../app/services'; +annotate ProcessorService with @impl: 'srv/services.js'; diff --git a/xmpls/remote-service/srv/remote.cds b/xmpls/remote-service/srv/mashup.cds similarity index 93% rename from xmpls/remote-service/srv/remote.cds rename to xmpls/remote-service/srv/mashup.cds index 2d85da7..a8fd8f6 100644 --- a/xmpls/remote-service/srv/remote.cds +++ b/xmpls/remote-service/srv/mashup.cds @@ -1,5 +1,5 @@ +// REVISIT: This is not in line with our best practices. using { API_BUSINESS_PARTNER as S4 } from './external/API_BUSINESS_PARTNER'; - service RemoteService { entity BusinessPartner as projection on S4.A_BusinessPartner { key BusinessPartner as ID, @@ -23,4 +23,4 @@ service RemoteService { key AddressID as addressId, PhoneNumber as phone } -} \ No newline at end of file +} diff --git a/xmpls/remote-service/srv/services.js b/xmpls/remote-service/srv/services.js index bddb5d4..afdb70b 100644 --- a/xmpls/remote-service/srv/services.js +++ b/xmpls/remote-service/srv/services.js @@ -8,7 +8,7 @@ class ProcessorService extends cds.ApplicationService { this.on('READ', 'Customers', (req) => this.onCustomerRead(req)); this.on(['CREATE','UPDATE'], 'Incidents', (req, next) => this.onCustomerCache(req, next)); this.S4bupa = await cds.connect.to('API_BUSINESS_PARTNER'); - this.remoteService = await cds.connect.to('RemoteService'); + this.remoteService = await cds.connect.to('RemoteService'); // REVISIT: What is this for? return super.init(); } @@ -33,7 +33,7 @@ class ProcessorService extends cds.ApplicationService { }) }) }).where({ ID: newCustomerId })); - + if(customer) { customer.email = customer.addresses[0]?.email[0]?.email; customer.phone = customer.addresses[0]?.phoneNumber[0]?.phone; @@ -50,7 +50,7 @@ class ProcessorService extends cds.ApplicationService { console.log('>> delegating to S4 service...', req.query); const top = parseInt(req._queryOptions?.$top) || 100; const skip = parseInt(req._queryOptions?.$skip) || 0; - + const { BusinessPartner } = this.remoteService.entities; // Expands are required as the runtime does not support path expressions for remote services @@ -63,7 +63,7 @@ class ProcessorService extends cds.ApplicationService { }); }) }).limit(top, skip)); - + result = result.map((bp) => ({ ID: bp.ID, name: bp.name, @@ -75,7 +75,7 @@ class ProcessorService extends cds.ApplicationService { console.log("after result", result); return result; } - + changeUrgencyDueToSubject(data) { if (data) { diff --git a/xmpls/remote-service/test/remote-service.test.js b/xmpls/remote-service/test/remote-service.test.js deleted file mode 100644 index e83fe42..0000000 --- a/xmpls/remote-service/test/remote-service.test.js +++ /dev/null @@ -1,91 +0,0 @@ -const cds = require("@sap/cds"); -const { GET, POST, PATCH, DELETE, expect, axios } = cds.test(__dirname + '../../', '--with-mocks'); -axios.defaults.auth = { username: "alice" }; - -describe("Integration Test for Remote Service", () => { - let draftId,incidentId; - describe("Test the BusinessPartner GET Endpoints", () => { - - it("Should return list of Business Partners", async () => { - const response = await GET("/odata/v4/api-business-partner/A_BusinessPartner"); - expect(response.status).to.eql(200); - }); - - it("Should return list of Business Partners Address", async () => { - const response = await GET("/odata/v4/api-business-partner/A_BusinessPartnerAddress"); - expect(response.status).to.eql(200); - }); - it("Should return list of Business Partners Email Address", async () => { - const response = await GET("/odata/v4/api-business-partner/A_AddressEmailAddress"); - expect(response.status).to.eql(200); - }); - it("Should return list of Business Partners Address PhoneNumber", async () => { - const response = await GET("/odata/v4/api-business-partner/A_AddressPhoneNumber"); - expect(response.status).to.eql(200); - }); - }); - - describe('Draft Choreography APIs', () => { - it('Create an incident ', async () => { - const { status, statusText, data } = await POST(`/odata/v4/processor/Incidents`, { - title: 'Urgent attention required !', - status_code: 'N', - "customer": {ID:"1004100"} - }); - draftId = data.ID; - expect(status).to.equal(201); - expect(statusText).to.equal('Created'); - }); - it('+ Activate the draft & check Urgency code as H using custom logic', async () => { - const response = await POST( - `/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)/ProcessorService.draftActivate` - ); - expect(response.status).to.eql(201); - expect(response.data.urgency_code).to.eql('H'); - }); - it('+ Test the customer detail', async () => { - const response = await GET(`/odata/v4/processor/Incidents?$filter=ID eq ${draftId}`); - //incidentId = ID; - expect(response.status).to.eql(200); - expect(response.data.value).to.exist; - expect(response.data.value[0]).to.contains({ - "customer_ID": "1004100" - }); - incidentId = response.data.ID; - }); - - describe("Create annd Update Business Partner", () => { - it("Creates a new Business Partner", async () => { - const payload = { - BusinessPartner: "17100015", - BusinessPartnerIsBlocked: true, - BusinessPartnerFullName: "John Doee", - }; - const response = await POST( - "/odata/v4/api-business-partner/A_BusinessPartner", - payload - ); - expect(response.status).to.eql(201); - }); - it("Update Business Partner", async () => { - const response = await PATCH( - `/odata/v4/api-business-partner/A_BusinessPartner('17100015')`, - {BusinessPartnerIsBlocked: false} - ); - expect(response.status).to.eql(200); - }); - }); - - it(`Should Close the Incident-${draftId}`, async ()=>{ - const {status} = await POST(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=true)/ProcessorService.draftEdit`, - { - "PreserveChanges": true - }); - expect(status).to.equal(201); - }); - it(`Update Business Partner details of the Incident`, async ()=>{ - const {status } = await PATCH(`/odata/v4/processor/Incidents(ID=${draftId},IsActiveEntity=false)`,{customer_ID: '17100015'}); - expect(status).to.equal(200); - }); - }); -});