CRM and CX Blogs by SAP
Stay up-to-date on the latest developments and product news about intelligent customer experience and CRM technologies through blog posts from SAP experts.
cancel
Showing results for 
Search instead for 
Did you mean: 
Ankit_Maskara
Product and Topic Expert
Product and Topic Expert
2,319
Hi fellow SCNers,

In this blog I will demonstrate how seamlessly we can integrate Qualtrics XM Platform with SAP Cloud Platform (SCP) application using Node.js and SAP Cloud Application Programming (CAP) Model.

Before the blog, I would like to thank my team mates along with whom I realized the same.

Thanks mohak.bhatia and aman.khanna.

Use Case

Below solution intends to pull experience data from the Qualtrics platform and then write same in the HANA container available in the SCP Application. We have used as an example deals/opportunities business object. Once the experience data is available in HANA, it can be readily consumed by other SAP products (Such consumption is not in scope of this blog but can be presented later ?).

I will break this in following steps –

  1. Qualtrics survey which captures the experience data.

  2. Creating the SCP Application which has the database, the OData Service and the API trigger code.


In detail,

  1. Qualtrics survey which captures the experience data.



  • Login to the Qualtrics XM Platform and create a survey choosing the CoreXM option or choose from any template.

  • Sample survey can look like -

  • Note the survey id encircled at the top.






2.Creating the SCP App which has the database, the OData Service and the API triggercode.

  • Open WebIDE and create an MTA Application. This demo is based on the development done using Cloud Application Programming Model - https://github.com/SAP-samples/cloud-cap-walkthroughs.

  • Final folder structure would be -

  • Go to DB module and create the database table –Code
    namespace sap.dealmakers;

    entity BidOutcomes {
    key bidId : UUID;
    reqUnderstoodRate : Integer;
    fitmentVsRequiredFeatureRatio : Integer;
    identifiedServicesAndDeliveryOptions : String;
    solutionValidated : String;
    riskRedressalDone : String;
    clarificationAddressed : String;
    pocRequested : String;
    bidChecklistFollowed : String;
    referencesPastProposals : String;
    isCompetitorProductExpiring : String;
    dealSize : String;
    discountSlabOffered : Integer;
    cLevelExecInMeeting : String;
    }


  • Enable Web IDE extensions to allow working with the Database explorer and building of DB module as below -

  • Save and build the DB module -

  • Configure the Cloud foundry region so that the database artifacts are created appropriately -

  • Maintain below project settings for Cloud Foundry to avoid the error - Error -

  • Post successful build we can see tables for various entities in the HDI container. Choose the DB module and open the HDI container – Table structure -

  • Now we will create ODATA Service - Code -
    using sap.dealmakers from '../db/hdb';

    service DealsService {
    entity BidOutcomes as projection on dealmakers.BidOutcomes;
    }


  • Now, we need to create the Node.js module which will be used to fetch data from Qualtrics XM Platform and write same to the SCP HANA module of current application.  Code -
    /*eslint no-console: 0*/
    // Get all the libraries required for processing
    const axios = require('axios');
    var fs = require('fs');
    var url = require('url');
    var http = require('http');

    var AdmZip = require('adm-zip');

    var request = require('request');

    // Pass your survey id
    var surveyid = 'SV_eDIL1bRzKlebXXX'

    // Pass your API token from your qualtrics account
    var headers = {
    'X-API-TOKEN': 'dBCclMcOWOYCMgquJtcsplJIw59W6jy2lcjXXXXX',
    'Content-Type': 'application/json'
    };

    // Declare the data format for processing
    var dataString = '{"format": "json" }';

    // Prepare the URL for the XM platform
    var options = {
    url: 'https://xxxxx.qualtrics.com/API/v3/surveys/' + surveyid + '/export-responses',
    method: 'POST',
    headers: headers,
    body: dataString
    };

    // Trigger call to fetch the data from Qualtrics platform
    function callback(error, response, body) {
    if (!error && response.statusCode == 200) {

    var options2 = {
    url: 'https://xxxxx.qualtrics.com/API/v3/surveys/' + surveyid + '/export-responses/' + JSON.parse(response.body).result.progressId,
    headers: headers
    };

    var intervalComplete = false;

    function callback2(error, response, body2) {

    if (!error && !intervalComplete && response.statusCode === 200 && JSON.parse(response.body).result.status === "complete") {

    clearInterval(intervalId);
    intervalComplete = true;

    var options3 = {
    url: 'https://xxxxx.qualtrics.com/API/v3/surveys/' + surveyid + '/export-responses/' + JSON.parse(response.body).result.fileId +
    '/file',
    headers: headers,
    encoding: null
    };

    function callback3(error, response, body) {

    if (!error && response.statusCode == 200) {
    // Qualtrics exports the survey response in ZIP format
    if (response.headers['content-type'] == 'application/zip') {
    var zip = new AdmZip(body);
    var zipEntries = zip.getEntries();
    var json = JSON.parse(zip.readAsText(zipEntries[0]));
    var oData = {};
    //Unzip the response and parse the results in format which is mapped with the DB table structure
    json.responses.forEach(resp => {
    console.log("--- Response is ----");
    oData = {
    "dealStatus": resp.labels.QID11,
    "cLevelExecInMeeting": resp.labels.QID1,
    "numberOfMeetings": resp.values.QID13_TEXT,
    "competitorsInvolvedInBidding": resp.labels.QID3 ,
    "demoRequested": resp.labels.QID6,
    "demoDelivered": resp.labels.QID14 ,
    "cLevelExecInDemo": resp.values.QID15,
    "demoFeedbackRating": resp.values.QID16_1,
    "wasFollowupAfterDemoRequested": resp.labels.QID8,
    "discountSlabOffered": resp.values.QID17_TEXT,
    "sapSolutionsOffered": resp.values.QID18_TEXT,
    "reqUnderstoodRate": resp.values.QID20_1,
    "fitmentVsRequiredFeatureRatio": resp.values.QID22_TEXT,
    "identifiedServicesAndDeliveryOptions": resp.labels.QID23,
    "solutionValidated": resp.labels.QID24,
    "riskRedressalDone": resp.labels.QID25,
    "clarificationAddressed": resp.labels.QID26,
    "bidChecklistFollowed": resp.labels.QID29,
    "isCompetitorProductExpiring": resp.labels.QID30,
    "referencesPastProposals": resp.labels.QID31
    };

    console.log("--- POSTING Odata ----", oData);
    // Prepare fully qualified URL for the ODATA service which will hit the table and write the data in the HANA container
    axios.post('https://xxxxx.cfapps.eu10.hana.ondemand.com/deals/BidOutcomes', oData)
    .then((res) => {
    console.log(`statusCode: ${res.statusCode}`);
    })
    .catch((error) => {
    console.error(error);
    });
    });

    console.log('CALLBACK4');
    }
    }
    }
    // Download the survey responses .zip
    request(options3, callback3);
    }
    }

    var intervalId = setInterval(function () {
    // Trigger to get progress update
    console.log("triggering progress update call and waiting...");
    request(options2, callback2);
    }, 500);

    }
    else{
    response.write("Failed!!");
    }
    }
    // Create Export and get ProgressID
    request(options, callback);


  • Final output would be, HANA container table updated with Qualtrics experience data –


Happy to have your feedback and questions.

Thanks for reading.
5 Comments