Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
somnath
Active Participant
7,167

Introduction:


Job Scheduling is nothing new for us being an ABAPer and with SM36/37 or even programmatically we submit jobs, so now the question is how to use the same job scheduling from BTP.

Obviously leveraging a service available for the same. Challenge was a little more for me when I had to perform certain on-prem table updates from a RAP service and that too through BTP job scheduling.

But here for my readers, first of all, I must say, the current blog post is NOT talking about how to update the on-prem custom table from BTP, but more on a simpler use case, about how to consume a RAP service and fetch certain data though a background job scheduling process. So if you are not interested now, a good time to skip this blog post, otherwise let us move to understand different steps. Anyways, I will talk at the end of this blog post about an approach that I tried to update the on-prem table using RAP service (in a short way).

If you prefer to watch the topic rather reading it, I also have shared the video URL for your convenience. You can find it at the end of this blog post. 

Challenge:


Calling a RAP service demands certain OAuth Authentication things to consider.

This means, we need some token and this is nothing but JWT (JSON Web Token) is needed to authenticate the RAP service. Hmm, but that's fine, we have XSUAA service for that and I can use that for authentication purposes so what is the concern?

But we will be scheduling this job as a background, so the dialog user is not there to help with the Authentication process, which means we have to generate the JWT token programmatically.

Step1: Create service instance for BTP Job Scheduler


From the BTP service instance search for JOB and create an instance for the service.


Job instance creation


Now the service instance you can find from the instance section and click on the Dashboard option as marked


Go to Dashboard from Job Instance



Step 2: Create your job



Create your job


Here you have to give a logical job name and an important thing to note HTTP Method option, here you need to choose the verb you want for your scenario. For our scenario, we will select the default 'GET' because we just need to fetch certain data through a RAP service. So here the action input field will contain the actual service endpoint which this background job will call to fetch some data.

But we don't have this service built yet!

True, so we will come back to this step completion once we are done with the service build-up.

We will create a node js application within which we will call our RAP service and also handle the authentication part. In the Appendix section, you can find the code snippet. I will explain in detail soon, but for the blog's continuation let's assume I have the service URL and put the same in the action field.

I create the job with a simple name as "rapjobdemo" as below and now click on this job name to perform Step 3


job details



job action details


**Action URL: we will create it soon as promised!

Step 3: Scheduling


Now job gets created and we need to complete the scheduling part now.


Create Schedule



Schedule information:



Schedule Information


Here Pattern provides you various options to schedule and in our case, default One Time is sufficient. Now comes the value part, here we will put it as 'now' because I want to trigger the job immediately. if you want to know various other available scheduling options, here is the link.

You can very get the purpose for Data (JSON) part I believe, as this will be used while we want to pass certain payload during our POST operation.

So our input should be as like this:


Scheduling Inputs


Alright! So if our service URL (Action field URL) is working fine, this will be able to trigger the job and fetch data like this:

You need to click on the "Run Logs" (left side pane) and will show below screen detail page as below:


Now click on the glass icon, and this will show you the response payload, cool!! simple isn't it?



Appendix


Now as promised, here is the node js code snippet
const oauthClient = require("client-oauth2")
const express = require("express");
const request = require("request-promise");
const env = require("dotenv").config();
const e = require("express");

const app = express();
const PORT = process.env.PORT || 3000;

//Use your RAP service URL here & make sure you remove "web" part from below RAP URL
//after .abap

const SERVICE_URL = "https://e70506aa-ddc0-435c-aab6-7abc40008538.abap.eu10.hana.ondemand.com/sap/opu/odata/sap/ZSB_SO_HDR/header";

const VCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES);
const XSUAA_CLIENTID = VCAP_SERVICES.xsuaa[0].credentials.clientid;
const XSUAA_CLIENTSECRET = VCAP_SERVICES.xsuaa[0].credentials.clientsecret;
const XSUAA_URL = VCAP_SERVICES.xsuaa[0].credentials.url;

const _getAccessToken = () => {
return new Promise((resolve, reject) => {
const client = new oauthClient({
accessTokenUri: XSUAA_URL + '/oauth/token',
clientId: XSUAA_CLIENTID,
clientSecret: XSUAA_CLIENTSECRET,
scopes: []
});

client.owner.getToken(process.env.USER_EMAIL, process.env.PASSWORD)
.catch((error) => {
reject({ message: 'Error in getting access token', error: error });
})
.then((result) => {
resolve({
accessToken: result.accessToken
});
})
});
}

const makeQuery = (serviceUrl, accessToken) => {
return new Promise((resolve, reject) => {
const options = {
url: serviceUrl,
resolveWithFullResponse: true,
headers: {
Authorization: "Bearer " + accessToken,
Accept: "application/json"
}

}

request(options)
.then((response) => {
if (response && response.statusCode == 200) {
resolve({
responseBody: response.body
});
}
reject({ message: 'Error while calling RAP Service' });
})
.catch((error) => {
reject({
message: 'Failed to receive RAP details',
error: error
})
})
});
}
app.get('/header', (req, res) => {

_getAccessToken()
.then((result) => {
return makeQuery(SERVICE_URL, result.accessToken)
})
.then((result) => {
res.send('<p> Result from RAP Service' + JSON.stringify(result.responseBody) + '</p>');
})
.catch((error) => {
res.send('ERROR' + error.message + 'FULL ERROR' + error.error)
});
})
app.listen(PORT, console.log(`Listening on port ${PORT}`));

**** I followed an interesting blog post from carlos.roggan and developed the node js part. Thanks to Carlos for sharing such rich content.

The complete project details you can find here.

Please follow the readme section of the project to know how to clone and deploy the application to the BTP cloud.

Certain important things are still there (like XSUAA service creation and linking the Role etc), which you definitely need to know to make it work successfully and so thought to share a video discussion that i published recently on my youtube channel.


Now how to post data to the on-prem table:


Assumptions:

The destination is created and the on-prem system is connected with a cloud connector

  • Next, get the metadata from the backend OData service which has the capability to update table records.

  • import the same metadata into the RAP project & create a custom entity

  • Expose this entity and now this is simply a part of your RAP project and add the behavioral capabilities

  • The class handler for your Custom entity will be doing the rest of the magic to call backend service and update records

  • If you need to post some default values thru background job, obviously you need to use the Data (JSON) section as I mentioned above


Tried to create a short demo, about how to consume external services from the RAP service, and here is the video URL for your reference:



Thanks for reading as well as for watching, and share your feedback, suggestions (if any).

Happy Learning!

With Regards, Somnath
5 Comments
Labels in this area