Skip to content

Commit

Permalink
Add batch request support + js tests
Browse files Browse the repository at this point in the history
  • Loading branch information
marikaner committed Dec 2, 2019
1 parent ecefe3e commit ba72f96
Show file tree
Hide file tree
Showing 61 changed files with 9,085 additions and 311 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ s4hana_pipeline/
business-partner/API_BUSINESS_PARTNER.edmx

vdm-output-tmp/
.DS_Store
36 changes: 36 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

def dockerImageName = 's4sdk/mock-server'
def dockerImage = null

lock('mock-server') {
stage('Build') {
node('master') {
milestone 10
deleteDir()

sh "git clone https://github.com/SAP/cloud-s4-sdk-book.git mock-server -b mock-server"

dir("mock-server"){

def buildArgs = [
'--pull'
]

withCredentials([string(credentialsId: 'api-sap-key', variable: 'KEY')]) {
buildArgs.add("--build-arg API_KEY=${KEY}")
dockerImage = docker.build(dockerImageName, buildArgs.join(' ') + ' .')
}
}
}
}

stage('Install') {
milestone 20
node('master') {
def containerName = 'mock-server'
sh "docker stop ${containerName} || echo 'Stopping failed'"
sh "docker run -d --rm -p 3000:8080 --name ${containerName} ${dockerImageName}"
currentBuild.result = 'SUCCESS'
}
}
}
14 changes: 11 additions & 3 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
const nodeAppStarted = Date.now();
const express = require('express');
const app = express();

const bupaApi = require('./business-partner/business-partner-api.js');
const socialMediaApi = require('./social-media-accounts/social-media-accounts-api.js');
const timeSheetEntryApi = require('./timeSheetEntryCollection/timeSheetEntryCollection-api.js')

const logRequests = function(req, res, next) {
console.log(`Request: ${req.method} ${req.originalUrl}`)
next();
};

const sendFakeCsrfToken = function(req, res, next) {
res.header('x-csrf-token', 'dummyToken123')
res.header('set-cookie', ['cookie'])
next()
}

app.use(logRequests);
app.use(sendFakeCsrfToken)

app.use('/sap/opu/odata/sap/API_BUSINESS_PARTNER', bupaApi);
app.use('/sap/opu/odata/sap/YY1_BPSOCIALMEDIA_CDS', socialMediaApi);
app.use('/sap/opu/odata/sap/API_MANAGE_WORKFORCE_TIMESHEET', timeSheetEntryApi)

app.get('/', function(req, res) {
res.set('Content-Type', 'text/html');
Expand All @@ -24,9 +32,9 @@ app.get('/', function(req, res) {
<body>
<div>OData mock service for Business Partner API of SAP S/4HANA Cloud is running at <a href="/sap/opu/odata/sap/API_BUSINESS_PARTNER">/sap/opu/odata/sap/API_BUSINESS_PARTNER</a>.</div>
<div>OData mock service for Business Partner Social Media custom API is running at <a href="/sap/opu/odata/sap/YY1_BPSOCIALMEDIA_CDS">/sap/opu/odata/sap/YY1_BPSOCIALMEDIA_CDS</a>.</div>
<div>OData mock service for TimeSheetEntryCollection custom API is running at <a href="/sap/opu/odata/sap/API_MANAGE_WORKFORCE_TIMESHEET">/sap/opu/odata/sap/API_MANAGE_WORKFORCE_TIMESHEET</a>.</div>
</body>
</html>`);
});

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Mock server started on port ${port} after ${Date.now() - nodeAppStarted} ms, running - stop with CTRL+C (or CMD+C)...`))
module.exports = app;
26 changes: 15 additions & 11 deletions business-partner/business-partner-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,26 +84,30 @@ router.get('/([$])metadata', function(req, res) {
});
});

router.post('/([$])batch', bodyParser.text({ type: () => true }), odata.batch, odata.set201Created);

const handlersForBusinessPartnerUpdate = odata.middlewareForUpdate(retrieveSingleBusinessPartner, modifyBusinessPartner);
const handlersForAddressUpdate = odata.middlewareForUpdate(retrieveSingleAddress, modifyAddress);

router.route('/A_BusinessPartner')
.get(retrieveAllBusinessPartners, odata.middlewareForSet())
.post(bodyParser.json(), createBusinessPartner, odata.sendAsODataResult);
.get(retrieveAllBusinessPartners, odata.middlewareForSet())
.post(odata.middlewareForCreate(createBusinessPartner));

router.route('/A_BusinessPartner\\((BusinessPartner=)?(\':id\'|%27:id%27)\\)')
.get(retrieveSingleBusinessPartner, odata.middlewareForEntity())
.delete(retrieveSingleBusinessPartner, odata.send404IfNotFound, deleteBusinessPartner, odata.send204NoContent)
.patch(handlersForBusinessPartnerUpdate).put(handlersForBusinessPartnerUpdate);
.get(retrieveSingleBusinessPartner, odata.middlewareForEntity())
.delete(retrieveSingleBusinessPartner, odata.send404IfNotFound, deleteBusinessPartner, odata.send204NoContent)
.patch(handlersForBusinessPartnerUpdate)
.put(handlersForBusinessPartnerUpdate);

router.route('/A_BusinessPartnerAddress')
.get(retrieveAllAddresses, odata.middlewareForSet())
.post(bodyParser.json(), createAddress, odata.sendAsODataResult);
.get(retrieveAllAddresses, odata.middlewareForSet())
.post(odata.middlewareForCreate(createAddress));

router.route('/A_BusinessPartnerAddress\\((BusinessPartner=)?(\':bupaId\'|%27:bupaId%27),(AddressID=)?(\':addressId\'|%27:addressId%27)\\)')
.get(retrieveSingleAddress, odata.middlewareForEntity())
.delete(retrieveSingleAddress, odata.send404IfNotFound, deleteAddress, odata.send204NoContent)
.patch(handlersForAddressUpdate).put(handlersForAddressUpdate);
.get(retrieveSingleAddress, odata.middlewareForEntity())
.delete(retrieveSingleAddress, odata.send404IfNotFound, deleteAddress, odata.send204NoContent)
.patch(handlersForAddressUpdate)
.put(handlersForAddressUpdate);

router.get('/', function(req, res) {
res.json({
Expand All @@ -116,4 +120,4 @@ router.get('/', function(req, res) {
});
});

module.exports = router;
module.exports = router;
4 changes: 2 additions & 2 deletions business-partner/business-partner-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const nextAddressId = function(existingAddresses) {
};

module.exports = {
data: data,
data,
newBusinessPartner: function (id) {
return Object.seal({
"__metadata": {
Expand Down Expand Up @@ -241,4 +241,4 @@ module.exports = {
}
Object.assign(addressToUpdate, addressInput);
}
};
};
114 changes: 114 additions & 0 deletions integration-tests/business-partner/batch-business-partner.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { ReadResponse, WriteResponses } from "@sap/cloud-sdk-core";
import { batch, BusinessPartner, BusinessPartnerAddress, changeset } from "@sap/cloud-sdk-vdm-business-partner-service";
import { createAddressRequest, createBusinessPartnerRequest, generateRandomName, retrieveAddressRequest, retrieveBusinessPartnerRequest } from "../util/request";
import { getUrl, startServer, stopServer } from "../util/server";

describe('batch business partner', () => {
const BUPA_ID = "1003764";
const ADDRESS_ID = "28238";

let server;
let destination;
beforeEach(async () => {
server = await startServer();
destination = { url: getUrl(server) };
});

afterEach(async () => {
return stopServer(server);
});

it('one retrieve', async () => {
const req = retrieveBusinessPartnerRequest(BUPA_ID);
const batchResponse = await batch(req).execute(destination);

const batchRetrievedBusinessPartner = (batchResponse[0] as ReadResponse).as(BusinessPartner)[0];
const retrievedBusinessPartner = await req.execute(destination);

expect(batchResponse).toHaveLength(1);
expect(batchRetrievedBusinessPartner).toEqual(retrievedBusinessPartner);
});

it('multi retrieve', async () => {
const req1 = retrieveBusinessPartnerRequest(BUPA_ID);
const req2 = retrieveAddressRequest(BUPA_ID, ADDRESS_ID);
const req3 = BusinessPartner.requestBuilder().getAll();
const batchResponse = await batch(req1, req2, req3).execute(destination);

const batchRetrievedBusinessPartner = (batchResponse[0] as ReadResponse).as(BusinessPartner)[0];
const batchRetrievedAddress = (batchResponse[1] as ReadResponse).as(BusinessPartnerAddress)[0];
const batchRetrievedBusinessPartners = (batchResponse[2] as ReadResponse).as(BusinessPartner);
const retrievedBusinessPartner = await req1.execute(destination);
const retrievedAddress = await req2.execute(destination);
const retrievedBusinessPartners = await req3.execute(destination);

expect(batchResponse).toHaveLength(3);
expect(batchRetrievedBusinessPartner).toEqual(retrievedBusinessPartner);
expect(batchRetrievedAddress).toEqual(retrievedAddress);
expect(batchRetrievedBusinessPartners).toEqual(retrievedBusinessPartners);
});

it('one changeset', async () => {
const req = createBusinessPartnerRequest(generateRandomName());
const batchResponse = await batch(
changeset(req)
).execute(destination);

const batchCreatedBusinessPartner = (batchResponse[0] as WriteResponses).responses[0].as(BusinessPartner);
const retrievedBusinessPartner = await retrieveBusinessPartnerRequest(batchCreatedBusinessPartner.businessPartner).execute(destination);

expect(batchResponse).toHaveLength(1);
expect((batchResponse[0] as WriteResponses).responses).toHaveLength(1);
expect(batchCreatedBusinessPartner).toEqual(retrievedBusinessPartner);
});

it('multi changeset', async () => {
const addressToDelete = await createAddressRequest(BUPA_ID, generateRandomName()).execute(destination);
const req1 = createBusinessPartnerRequest(generateRandomName());
const req2 = BusinessPartnerAddress.requestBuilder().delete(addressToDelete.businessPartner, addressToDelete.addressId);
const batchResponse = await batch(
changeset(req1, req2)
).execute(destination);

const batchCreatedBusinessPartner = (batchResponse[0] as WriteResponses).responses[0].as(BusinessPartner);
const retrievedBusinessPartner = await retrieveBusinessPartnerRequest(batchCreatedBusinessPartner.businessPartner).execute(destination);
const retrieveAddress = retrieveAddressRequest(BUPA_ID, ADDRESS_ID).execute(destination);

expect(batchResponse).toHaveLength(1);
expect((batchResponse[0] as WriteResponses).responses).toHaveLength(2);
expect(batchCreatedBusinessPartner).toEqual(retrievedBusinessPartner);
expect(retrieveAddress).toReject();
});

it('mixed batch', async () => {
const addressToDelete = await createAddressRequest(BUPA_ID, generateRandomName()).execute(destination);
const req1 = retrieveBusinessPartnerRequest(BUPA_ID);
const req2 = createBusinessPartnerRequest(generateRandomName());
const req3 = BusinessPartnerAddress.requestBuilder().delete(addressToDelete.businessPartner, addressToDelete.addressId);
const req4 = retrieveAddressRequest(addressToDelete.businessPartner, addressToDelete.addressId);
const req5 = createBusinessPartnerRequest(generateRandomName());

const batchResponse = await batch(
req1,
changeset(req2, req3),
req4,
changeset(req3, req5)
)
.execute(destination);

const batchRetrievedBusinessPartner = (batchResponse[0] as ReadResponse).as(BusinessPartner)[0];
const batchCreatedBusinessPartner = (batchResponse[1] as WriteResponses).responses[0].as(BusinessPartner);
const retrievedBusinessPartner = await req1.execute(destination);
const retrievedCreatedBusinessPartner = await retrieveBusinessPartnerRequest(batchCreatedBusinessPartner.businessPartner).execute(destination)

expect(batchResponse).toHaveLength(4);
expect(batchResponse[0].isSuccess()).toBeTruthy();
expect(batchResponse[1].isSuccess()).toBeTruthy();
expect(batchResponse[2].isSuccess()).toBeFalsy();
expect(batchResponse[3].isSuccess()).toBeFalsy();

expect(batchRetrievedBusinessPartner).toEqual(retrievedBusinessPartner);
expect(batchCreatedBusinessPartner).toEqual(retrievedCreatedBusinessPartner);
});
});

Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { BusinessPartnerAddress } from "@sap/cloud-sdk-vdm-business-partner-service";
import { createAddressRequest, generateRandomName, retrieveAddressRequest } from "../util/request";
import { getUrl, startServer, stopServer } from "../util/server";

describe('modify business partner address', () => {
const BUPA_ID = "1003764";

let server;
let destination;
beforeEach(async () => {
server = await startServer();
destination = { url: getUrl(server) };
});

afterEach(async () => {
return stopServer(server);
});

it('create', async () => {
const streetName = generateRandomName();
const address = await createAddressRequest(BUPA_ID, streetName).execute(destination);
const retrievedAddress = await retrieveAddressRequest(BUPA_ID, address.addressId).execute(destination);

expect(retrievedAddress).not.toBeNil();
expect(retrievedAddress.streetName).toEqual(streetName);
});

it('create for non exisitng business partner', async () => {
const create = createAddressRequest('DOES_NOT_EXIST', generateRandomName()).execute(destination);
expect(create).toReject();
});

it('update', async () => {
let originalAddress = await createAddressRequest(BUPA_ID, generateRandomName()).execute(destination);
let updateDiff = BusinessPartnerAddress.builder()
.businessPartner(originalAddress.businessPartner)
.addressId(originalAddress.addressId)
.streetName(generateRandomName())
.build();
await BusinessPartnerAddress.requestBuilder()
.update(updateDiff)
.execute(destination);
const retrievedAddress = await retrieveAddressRequest(originalAddress.businessPartner, originalAddress.addressId).execute(destination);

expect(retrievedAddress).not.toBeNil();
expect(retrievedAddress.streetName).toEqual(updateDiff.streetName);
});

it('update with workaround', async () => {
let originalAddress = await createAddressRequest(BUPA_ID, generateRandomName()).execute(destination);
originalAddress.streetName = generateRandomName();
await BusinessPartnerAddress.requestBuilder()
.update(originalAddress)
.execute(destination);
const retrievedAddress = await retrieveAddressRequest(originalAddress.businessPartner, originalAddress.addressId).execute(destination);

expect(retrievedAddress).not.toBeNil();
expect(retrievedAddress.streetName).toEqual(originalAddress.streetName);
});

it('update non exisitng', async () => {
const update = BusinessPartnerAddress.requestBuilder()
.update(
BusinessPartnerAddress.builder()
.businessPartner(BUPA_ID)
.addressId('DOES_NOT_EXIST')
.streetName(generateRandomName())
.build()
)
.execute(destination);

expect(update).toReject();
});

it('delete', async () => {
const streetName = generateRandomName();
const businessPartnerAddress = await createAddressRequest(BUPA_ID, streetName).execute(destination);
const addressToDelete = BusinessPartnerAddress.builder()
.businessPartner(businessPartnerAddress.businessPartner)
.addressId(businessPartnerAddress.addressId)
.build();

await BusinessPartnerAddress.requestBuilder()
.delete(addressToDelete)
.execute(destination);

const retrieve = retrieveAddressRequest(BUPA_ID, businessPartnerAddress.addressId).execute(destination);

expect(retrieve).toReject();
});
});
Loading

0 comments on commit ba72f96

Please sign in to comment.