0

I am writing a service in nodejs. Please read below for the approach i am doing to solve this problem.

First I call a rest endpoint(say /offers) to fetch data . say it cloudSenseData

Final Response I need to massage/manipulate the data to give back the needed response as output.

During massaging the data(from above call) I have to check if there is a relatedProduct info present.

if present I need to call another rest endpoint(say /offers/:id/products) the :id is catalogueitemid obtained in previous call(cloudSenseData) to get more relatedProduct info details which i can include in the final massaged output.

So lets say in cloudSenseData I have 10 catalogueItems.

These are the steps i am doing during massaging the data:

  1. Using async.map on cloudSenseData and mapping it to the needed response format.(I used it to make things parallel done)and have a callback function doing the need
  2. In the callback apart while making the response as needed I am checking if it has relatedProduct info if it doesnt have no issue else i am calling downstream endpoint to get more relatedProductInfo using catologueItemId.(here i am using deasync )

This is taking more time than needed.

Can anyone please suggest any alternatives to approach this?

Update with Code : common-nodejs is a library we have written that wraps many functionalities like calling the rest endpoints using restify, reading the app configuration and many such. The below code is in typescript . Hope this helps.

    import {log,serviceInfo,HttpMethod} from "common-nodejs";
    import { CloudsenseBaasJsonAction } from './cloudsense-baas-connector';
    import {Constants} from "./Constants";
    let request = require('request');
    let deasync = require('deasync');
    let moment = require("moment");
    let async= require("async");
    let PropertiesReader = require("properties-reader");
     let endpointsConfigFile = require("../../config/endpoints.json");
     //load the properties file to map the cloudsense attributes with        required keys.
     let parseConfig=new  PropertiesReader("config/fields_config.properties");

      // helper method  in adding more details in response by reading the properties file
     export let parsePropertiesFile =            function(attribute,microserviceResponse,key,value){
     let cloudSenseKey = attribute[key];
     let microServiceKey = parseConfig.get(cloudSenseKey);
     //console.log("********cloudSenseKey***************",cloudSenseKey,"************ microServiceKey***" ,microServiceKey);
     if( microServiceKey!= undefined && microServiceKey!=null){
    // console.log("********microServiceKey***************",microServiceKey  ,attribute[value]);
      microserviceResponse[microServiceKey] = attribute[value];
     }

     };
     // this method does the fetching the detailed info if relatedProducts are there
     export let requestRelatedProductsInfo = function(offerId):any{
     //  console.log("****************Above to Parse*******");
     let body={};
      let cloudsenseBaasJsonAction = new  CloudsenseBaasJsonAction(HttpMethod.GET, body, '');
      let sendRequestForRelatedProducts = deasync(function(callback){
       request({
         proxy: serviceInfo.extras.internetProxy,
        url:  serviceInfo.extras.serviceCloudsense.apiEndpoint+"/services/current/offer/"+offerId+"/products",
         method: endpointsConfigFile.cloudsense_baas.offers.method,
         headers: cloudsenseBaasJsonAction.modifyHeadersWithParams({
            "csTime": Date.now(),
            "method":   HttpMethod[endpointsConfigFile.cloudsense_baas.offers.method],
            "path": "/services/current/offer/"+offerId+"/products",
            "clientKey": serviceInfo.extras.serviceCloudsense.clientKey,
            "clientSecret":  serviceInfo.extras.serviceCloudsense.clientSecret
        })
    },function (err, res, body) {
        if(res.statusCode==404 || res.statusCode==500){
            console.log("********res***offerId*************",res.statusCode,offerId);
        }

        if(err){
            // console.log("*****************Errors****************",err);
            callback(err,null);
        }
        callback(null,body);
    });
});
return JSON.parse(sendRequestForRelatedProducts());
        }
       export class Parser {

/*
 * This method is used to massage the cloudsense data and respond with the below formate
 *
 * {
 *        "catalogueId": "a26O0000000SOS7IAO",
 *       "price": 1536,
 *        "name": "IPHONE 6S PLUS",
 *        "default": "true",
 *        "color": "Silver",
 *        "memory": "128GB",
 *        "contentId": "IPHONE6S128GBSILVER",
 *        "featured": "true",
 *        "isOutright": "Outright",
 *        "brand": "Apple",
 *        "startdate": "01-09-2016",
 *        "enddate": "01-09-2017",
 *        "OS": "iOS",
 *        "bluetick": true
 *    }
 *
 *
 */
public parseCloudsenseData(CloudsenseData:any,isDefaultFlow : boolean):any{
    console.log('*******isDefaultFlow********',isDefaultFlow);
    let current_Date = moment().format(Constants.DateFormate);
    let  parseCloudData = function(result,callback){
        try{
            let microserviceResponse={
                "catalogueId"      :    result.catalogueId,
                "catalogueItemId"  :   result.catalogueItemId,
                "outrightPrice"    :   result.cscfga__One_Off_Charge__c,
                "displayName"      :   result.name,
                "currentDate"      :   current_Date,
                "recurringPrice"   :   result.cscfga__Recurring_Charge__c
            };
            let key = Constants.Name;
            let value = Constants.Value;
            //fetch the list of attributes.
            for(let att of result.attributes){
                parsePropertiesFile(att,microserviceResponse,key,value);
            }
            debugger;
            //fetching the relatedProducts Data. if there are relatedProducts calling the endpoint to get more details 
            if(!isDefaultFlow && result.relatedProducts!= undefined && result.relatedProducts!=null  && result.relatedProducts.length>0 ){

                let microserviceRelatedProductArray=[];
              // debugger;
              //   result.catalogueItemId = 'caf71d86-bca3-4bed-a2d5-b233305b8e76'
                let relatedProductArray = requestRelatedProductsInfo(result.catalogueItemId);
                for(let relatedProduct of relatedProductArray.results){
                //     for(let relatedProduct of relatedProductArray){
                    let finalRelatedProduct ={
                        "productId"        :   relatedProduct.productId,
                        "name"             :   relatedProduct.name,
                        "sku"              :   relatedProduct.sku,
                        "productType"      :   relatedProduct.productType,
                        "productSubType"   :   relatedProduct.productSubType,
                        "outrightPrice"    :   relatedProduct.cscfga__One_Off_Charge__c,
                        "recurringPrice"   :   relatedProduct.cscfga__Recurring_Charge__c,
                        "contentId"        :   '',
                        "mobileRepaymnetOption":''
                    };
                    //This loop is there to find the content_id among available attributes dynamically.
                    for(let att of relatedProduct.attributes){
                        parsePropertiesFile(att,finalRelatedProduct,key,value);
                    }
                    microserviceRelatedProductArray.push(finalRelatedProduct);
                } // end of for loop.
                microserviceResponse.relatedProducts =microserviceRelatedProductArray;
            }//end of if. ( view details flow).
            // if(!isDefaultFlow && result.relatedProducts!= undefined && result.relatedProducts!=null  && result.relatedProducts.length>0 ) {
            // var catalogueItemIdArray = [];
            //     catalogueItemIdArray.push(result.catalogueId);
            // }
            return callback(null,microserviceResponse);
        }catch(error){
            // log.debug("************error block**********",error);
            return callback(error,null);
        }
    };

    let microServiceOutput;
    //calling the parseCloudData method asynchronusly for each element in the array.
    async.map(CloudsenseData.results, parseCloudData ,function (error,result){

        if(error){
            // console.log("***************Error***************",error);
            microServiceOutput = {
                "code":1005,
                "message": "The downstream is not available"
            };
            return microServiceOutput;
        }

        microServiceOutput = result;
    });



    return microServiceOutput;
}//End of parseCloudsenseData();

}

  • Problems like this can be very difficult using callbacks. I find they become much easier with [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) and almost trivial with the [async function syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) or [co-routines](https://www.npmjs.com/package/co). – SimpleJ Feb 21 '17 at 17:58
  • 2
    Since there is no code provided, it's very difficult to figure out what you are actually asking us for. Each async operation is coded and then you continue the operation in the completion callback of that async operation. For multiple operations that all must be sequenced, if you're coding them with normal callbacks, then these will end up nested (each inside the previous one's callback). Converting all async operations to promises will make a lot of things more managable. But, without specific code, we can't really say much more than that. – jfriend00 Feb 21 '17 at 18:14
  • jfriend, @SimpleJ I have updated the post with code. Please check and let me know if anything i can add that helps – Saikumar Kunchakuri Feb 22 '17 at 04:13

0 Answers0