When you need to extend your SaaS application you may use PaaS solutions to do it!
In this blog post I will use Oracle Mobile Cloud Service (MCS) and Oracle Mobile Application Accelerator (MAX) to create a mobile application for my Oracle Sales Cloud.
Download the packages: paas4saas-with-mcs-and-max.zip.
First of all we need to create a new Mobile Backend.
Go to Menu > Applications > Mobile Backends.
Click “New Mobile Backend” button to create a new Mobile Backend and name it as SalesMB.
To create a new Connector, go to Menu > Applications > Connectors.
Click “New Connector” button to create a new SOAP Connector and name it as SalesConn.
Don’t forget to provide the WSDL of ContactService.
Click Next twice.
Select “oracle/http_basic_auth_over_ssl_client_policy” and add a new csf-key.
Click Save and then click Select.
Click Save and then click Close.
To create a new API, go to Menu > Applications > APIs.
Click “New API” button to create a new API and name it as SalesAPI.
In Security, disable the “Login Required” option.
In Schema, click “New Schema” button.
Use the following code to create 4 schemas: contactBase, newContact, contact and contactList.
contactBase
{ "$schema": "http://json-schema.org/draft-04/schema#", "id": "contactBase", "type": "object", "allOf": [ { "properties": { "PartyId": { "type": "integer" } } }, { "$ref": "newContact" } ] }
newContact
{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "id": "newContact", "allOf": [ { "properties": { "FirstName": { "type": "string" }, "LastName": { "type": "string" } } } ] }
contact
{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "id": "contact", "allOf": [ { "$ref": "contactBase" }, { "properties": { "CreatedBy": { "type": "string" }, "CreationDate": { "type": "string" }, "LastUpdateDate": { "type": "string" }, "LastUpdatedBy": { "type": "string" } } } ] }
contactList
{ "$schema": "http://json-schema.org/draft-04/schema#", "id": "contactList", "type": "array", "items": { "$ref" : "contactBase" } }
Click Save.
In Endpoints, click “New Resource” button and create two endpoints: contacts and contact/{id}.
Click “Methods” link of contacts endpoint and add a new GET method.
In Request section, add a new Parameter and name it as “q”.
In Responses section, add a new response.
In Body section, add a new Media Type.
In Schema tab, select “contactList”.
In Example tab, add the following code:
[ { "PartyId": 100000000409231, "FirstName": "John", "LastName": "Dunbar" }, { "PartyId": 100000000409233, "FirstName": "Lisa", "LastName": "Jones" }, { "PartyId": 100000000409251, "FirstName": "Bala", "LastName": "Gupta" } ]
Click “Add Method” button to add a new POST method.
In Request section, go to Body section and add a new Media Type.
In Schema tab, select “newContact”.
In Example tab, add the following code:
{ "FirstName": "Bala", "LastName": "Gupta" }
In Responses section, add a new response.
In Body section, add a new Media Type.
In Schema tab, select “contact”.
In Example tab, add the following code:
{ "PartyId": 100000000409251, "FirstName": "Bala", "LastName": "Gupta", "CreatedBy": "LISA.JONES", "CreationDate": "2016-10-06T10:39:17.0Z", "LastUpdateDate": "2016-10-06T10:39:17.167Z", "LastUpdatedBy": "LISA.JONES" }
Click Save and go back to Endpoints.
Click “Methods” link of contact/{id} endpoint and add a new GET method.
In Responses section, add a new response.
In Body section, add a new Media Type.
In Schema tab, select “contact”.
In Example tab, add the following code:
{ "PartyId": 100000000409251, "FirstName": "Bala", "LastName": "Gupta", "CreatedBy": "LISA.JONES", "CreationDate": "2016-10-06T10:39:17.0Z", "LastUpdateDate": "2016-10-06T10:39:17.167Z", "LastUpdatedBy": "LISA.JONES" }
Click “Add Method” button to add a new DELETE method.
Click “Add Method” button again to add a new PUT method.
In Request section, go to Body section and add a new Media Type.
In Schema tab, select “newContact”.
In Example tab, add the following code:
{ "FirstName": "Bala", "LastName": "Gupta" }
Click Save and go to Endpoints.
In Implementation, click “JavaScript Scaffold” button to download the implementation archive.
Unzip the package, open the package.json file and replace its content with the following code:
{ "name" : "salesapi", "version" : "1.0.0", "description" : "API for Sales Mobile", "main" : "salesapi.js", "oracleMobile" : { "dependencies" : { "apis" : { }, "connectors" : { "/mobile/connector/SalesConn": "1.0" } } } }
Open the salesapi.js file and replace its content with the following code:
/** * The ExpressJS namespace. * @external ExpressApplicationObject * @see {@link http://expressjs.com/3x/api.html#app} */ /** * Mobile Cloud custom code service entry point. * @param {external:ExpressApplicationObject} * service */ module.exports = function(service) { service.put('/mobile/custom/SalesAPI/contact/:id', function(req,res) { var result = {}; var acceptType = req.accepts(['application/json']); if (acceptType == 'application/json'){ console.fine("Body: "+ JSON.stringify(req.body)); req.oracleMobile.connectors.SalesConn.post('updateContact', { "Header": null, "Body": { "updateContact": { "contact": { "PartyId": req.params.id, "FirstName": req.body.FirstName, "LastName": req.body.LastName } } } }, {inType: "json", outType: "json"} ).then( function(conResult) { if(conResult.result.Body.updateContactResponse && conResult.result.Body.updateContactResponse.result.Value) { console.fine("Response: "+ JSON.stringify(conResult.result.Body.updateContactResponse)); result = conResult.result.Body.updateContactResponse.result.Value; result = result.map(function(item) { return { "FirstName": item.FirstName, "LastName": item.LastName }; }); } else { console.fine("no response!"); result = []; } console.fine("Result: "+ JSON.stringify(result[0])); res.send(conResult.statusCode, result[0]); }, function(error){ console.error("Error: "+ JSON.stringify(error)); res.send(500, error.error); } ); } }); service.delete('/mobile/custom/SalesAPI/contact/:id', function(req,res) { var result = {}; var acceptType = req.accepts(['application/json']); if (acceptType == 'application/json'){ console.fine("Body: "+ JSON.stringify(req.body)); req.oracleMobile.connectors.SalesConn.post('deleteContact', { "Header": null, "Body": { "deleteContact": { "contact": { "PartyId": req.params.id } } } }, {inType: "json", outType: "json"} ).then( function(conResult) { res.send(conResult.statusCode, result[0]); }, function(error){ console.error("Error: "+ JSON.stringify(error)); res.send(500, error.error); } ); } }); service.get('/mobile/custom/SalesAPI/contact/:id', function(req,res) { var result = {}; var acceptType = req.accepts(['application/json']); if (acceptType == 'application/json'){ console.fine("Body: "+ JSON.stringify(req.body)); req.oracleMobile.connectors.SalesConn.post('getContact', { "Header": null, "Body": { "getContact": { "PartyId": req.params.id } } }, {inType: "json", outType: "json"} ).then( function(conResult) { if(conResult.result.Body.getContactResponse && conResult.result.Body.getContactResponse.result.Value) { console.fine("Response: "+ JSON.stringify(conResult.result.Body.getContactResponse)); result = conResult.result.Body.getContactResponse.result.Value; result = result.map(function(item) { return { "PartyId": item.PartyId, "FirstName": item.FirstName, "LastName": item.LastName, "CreatedBy": item.CreatedBy, "CreationDate": item.CreationDate, "LastUpdateDate": item.LastUpdateDate, "LastUpdatedBy": item.LastUpdatedBy }; }); } else { console.fine("no response!"); result = []; } console.fine("Result: "+ JSON.stringify(result[0])); res.send(conResult.statusCode, result[0]); }, function(error){ console.error("Error: "+ JSON.stringify(error)); res.send(500, error.error); } ); } }); service.post('/mobile/custom/SalesAPI/contacts', function(req,res) { var result = {}; var acceptType = req.accepts(['application/json']); if (acceptType == 'application/json'){ console.fine("Body: "+ JSON.stringify(req.body)); req.oracleMobile.connectors.SalesConn.post('createContact', { "Header": null, "Body": { "createContact": { "contact": { "FirstName": req.body.FirstName, "LastName": req.body.LastName } } } }, {inType: "json", outType: "json"} ).then( function(conResult) { if(conResult.result.Body.createContactResponse && conResult.result.Body.createContactResponse.result.Value) { console.fine("Response: "+ JSON.stringify(conResult.result.Body.createContactResponse)); result = conResult.result.Body.createContactResponse.result.Value; result = result.map(function(item) { return { "PartyId": item.PartyId, "FirstName": item.FirstName, "LastName": item.LastName, "CreatedBy": item.CreatedBy, "CreationDate": item.CreationDate, "LastUpdateDate": item.LastUpdateDate, "LastUpdatedBy": item.LastUpdatedBy }; }); } else { console.fine("no response!"); result = []; } console.fine("Result: "+ JSON.stringify(result[0])); res.send(conResult.statusCode, result[0]); }, function(error){ console.error("Error: "+ JSON.stringify(error)); res.send(500, error.error); } ); } }); service.get('/mobile/custom/SalesAPI/contacts', function(req,res) { var result = {}; var acceptType = req.accepts(['application/json']); if (acceptType == 'application/json'){ console.fine("Params: "+ JSON.stringify(req.query)); req.oracleMobile.connectors.SalesConn.post('findContact', { "Header": null, "Body": { "findContact": { "findCriteria": { "fetchStart": 0, "fetchSize": 20, "filter": { "conjunction": "And", "group": [ { "conjunction": "And", "upperCaseCompare": false, "item": [ { "conjunction": "And", "upperCaseCompare": true, "attribute": "ContactName", "operator": "CONTAINS", "value": [ req.query.q ? req.query.q : "%" ] } ] } ] }, "sortOrder": { "sortAttribute": [ { "name": "CreationDate", "descending": true } ] }, "findAttribute": [ "PartyId", "FirstName", "LastName", "CreationDate", "LastUpdateDate", "CreatedBy" ] }, "findControl": { "retrieveAllTranslations": false } } } }, {inType: "json", outType: "json"} ).then( function(conResult) { if(conResult.result.Body.findContactResponse && conResult.result.Body.findContactResponse.result.Value) { console.fine("Response: "+ JSON.stringify(conResult.result.Body.findContactResponse)); result = conResult.result.Body.findContactResponse.result.Value; result = result.map(function(item) { return { "PartyId": item.PartyId, "FirstName": item.FirstName, "LastName": item.LastName }; }); } else { console.fine("no response!"); result = []; } console.fine("Result: "+ JSON.stringify(result)); res.send(conResult.statusCode, result); }, function(error){ console.error("Error: "+ JSON.stringify(error)); res.send(500, error.error); } ); } }); };
Go to salesapi folder and create a new zip file.
Drag the zip file and drop into implementation page.
Click “Publish” button to use this implementation.
Click “Check Dependencies” button and then click “Publish All” button.
Done with MCS!
Let’s start with MAX!
Click “New Application” link.
Name the application as “Sales Mobile” and click Next twice.
Name the first screen as “Contacts” and click Next.
Select List, click Next and then click Create.
In Properties section, set Search to “Always Visible”.
In QuickStarts, click “Add Data”.
Select “Title Subtitle” layout and click Next.
Click “Add Service” and select SalesAPI.
Select contacts and click Next.
Map the fields and click Next.
Select “Search Field” of Current Screen and click Finish.
In QuickStarts, click “Add a Detail Screen”.
Click Next.
Name the screen as “Contact” and click Next.
Select Form, click Next and then click Finish.
Click “Go to Detail Screen”.
In QuickStarts, click “Add Data”.
Select contact and click Next.
Map the fields and click Next.
Drag “PartyId” of Parent Screen, drop inside the Id field and click Finish.
In QuickStarts, click “Add Edit Screen” and then click “Go to Edit Screen”.
Click “EDIT CONTATCT” and then Actions > Navigation Data Mapper.
Drag “PartyId” of Current Screen, drop inside the Id field and click Finish.
Click “Update contact/{id}”.
Click “Business Action Mapper”.
Drag “PartyId” of Parent Screen, drop inside the Id field and click Finish.
Click Save.
In Screen Flow, double-click Contact Screen.
Click “CONTACT” and then Header Buttons.
Click “Button +” and set Button Label as Delete.
In Action section, add a new action and click Tap.
Drag actions, drop inside pointed lines and click “Business Action Mapper” of Delete contact/{id}.
Drag “PartyId” of Current Screen, drop inside the Id field and click Finish.
Click Save.
In Screen Flow, double-click Contacts Screen.
In QuickStarts, click “Add a Create Screen”.
Done!!
Click Test button to test your mobile application!
If you want to test the application with your smartphone, go to Google Play or Apple App Store and download Oracle MAXApp.
Open the MAXApp and tap “+”.
In MAX, click “Test on Phone” and then click “Build Test Application”.
Click “Show QR Code”.
Scan the QR Code with MAXApp to install the application on your phone.