Request Payload transformation

Hi, I need to achieve the following:

1.For request body transformation as shown below –

{

"abc_def": "1",

"variables": {

"AbcDef": "xxxxxxxxxxxxxxxxxxxx",

"A": "xxxxxxxxxxxxxx",

"GhiJkl”: “xxxxxxxxxxxxxx",

}

}

We want to follow the standard case pattern(here Camel Case) for passing the variables like –

{

"abc_Def": "1",

"variables": {

"abcDef": "xxxxxxxxxxxxxxxxxxxx",

"a": "xxxxxxxxxxxxxx",

"ghiJkl”: “xxxxxxxxxxxxxx",

}

}

Currently, we are using JavaScript to loop through the JSON payload and using KVM to map the proxy based variables and target based variables. As, the number of variables scale up, the KVM structuring will be mpre complex . Can you suggest a better way to do this...

Also can we pass dynamic values in Parameter reference in KVM policy like –

Variable1, Variable2, Variable3 - for all these key names <Parameter ref=” Variable*”/> ?

1 3 2,174
3 REPLIES 3

Is there a set of rules you can follow?

It looks to me that the rules for converting property names are:

  • lowercase the first character ("A" => "a", and "AbcDef" => "abcDef" )
  • uppercase a character following an underscore. ("abc_def" => "abc_Def")

That's pretty easy, isn't it? You don't need a KVM for that. This works:

function pascalCaseToCamelCase(str) {
  return str.charAt(0).toLowerCase() +
    str.split('_').map(function(s){
      return s.charAt(0).toUpperCase() + s.slice(1);
    }).join('_').slice(1);
}

And here are some tests:

var tests = {
      "abc_def": "abc_Def",
      "variables": "variables",
      "AbcDef": "abcDef",
      "A": "a",
      "GhiJkl": "ghiJkl"
    };

var counts = {PASS:0, FAIL:0};
Object.keys(tests).forEach(function(testcase){
  var actualResult = pascalCaseToCamelCase(testcase);
  var expectedResult = tests[testcase];
  var resolution = (actualResult === expectedResult)?"PASS":"FAIL";
  console.log("%s : %s  %s", testcase, actualResult, expectedResult, resolution);
  counts[resolution]++;
});


console.log("\n==================================================================\n");
Object.keys(counts).forEach(function(r){
  console.log("%s : %d", r, counts[r]);
});

The results I see:

abc_def : abc_Def  abc_Def PASS
variables : variables  variables PASS
AbcDef : abcDef  abcDef PASS
A : a  a PASS
GhiJkl : ghiJkl  ghiJkl PASS

==================================================================

PASS : 5
FAIL : 0


You will probably want to write lots more tests to make sure the converter does what you want in all edge cases.

Thanks for the response,the given answer suffice our need of having properties with proper naming convention.

But we do have a more complex transformation between the frontend and backend parameters. For example:
While sending a request payload,the frontend parameters would be id and name as follows:
{

"id":"123",

"name":"xyz"

}

But,the backend recognize parameters "sys_id" , "sys_name".

So we need to map, id --> sys_id and name --> sys_name before the request is sent to backend. And this JSON varies based on some conditions and JSON can have many level of nested of objects.

Also,we would have this transformation applied to more than one APIs.

Would KVM would be the the recommmended way to do so? If not ,kindly suggest a better way to achieve the given scenario.

Yes, I think in that case a KVM storing a set of paths to map would be good.

For example if the JSON payload is

{
 "foo": "1",
 "variables": {
   "name": "xxxxxxxxxxxxxxxxxxxx",
   "id": "xxxxxxxxxxxxxx"
 }
}

Then you would want something like this in the KVM:

{"variables": { "id" : "sys" } }

And you'd need some JavaScript to walk the payload and modify it according to the map stored in the KVM.

This might sound exotic but it's really just name replacement and it's easy and fast with a recursive function in a regular JavaScript callout.

For the recursive walk, look here.

Here's an example that runs in nodejs from the command line:

// ===================================================================
// mock context
var contextVars = { };
var context = {
      getVariable : function(name) {
        var value = contextVars[name];
        console.log('GET %s = %s', name, value);
        return value;
      },
      setVariable: function (name, value) {
        console.log('SET %s := %s', name, value);
        contextVars[name] = value;
      }
    };
// =================================================================

var source = {
 "foo": "1",
 "variables": {
   "name": "xxxxxxxxxxxxxxxxxxxx",
   "id": "xxxxxxxxxxxxxx"
 }
};

var mapRules = {"variables": { "id" : "sys_id" } };

var walkObj = require('./walkObj.js');

console.log("RULES");
console.log(JSON.stringify(mapRules));
walkObj(mapRules,
        'rule',
        function(name, value) {
          context.setVariable(name, value);
        });


console.log("BEFORE");
console.log(JSON.stringify(source, null, 2) + '\n');
walkObj(source,
        '',
        function(fullpath, itemvalue, oldkey, obj) {
          var ruleName = 'rule.' + fullpath;
          var substitution = context.getVariable(ruleName);
          if (substitution) {
            obj[substitution] = itemvalue;
            delete obj[oldkey];
          }
        });


console.log("AFTER");
console.log(JSON.stringify(source, null, 2) + '\n');

and here is the output showing the results:

$ ./mapProperties.js
RULES
{"variables":{"id":"sys_id"}}
SET rule.variables.id := sys_id
BEFORE
{
  "foo": "1",
  "variables": {
    "name": "xxxxxxxxxxxxxxxxxxxx",
    "id": "xxxxxxxxxxxxxx"
  }
}


GET rule.foo = undefined
GET rule.variables.name = undefined
GET rule.variables.id = sys_id
AFTER
{
  "foo": "1",
  "variables": {
    "name": "xxxxxxxxxxxxxxxxxxxx",
    "sys_id": "xxxxxxxxxxxxxx"
  }
}

That code won't run "as is" within a JS callout. To get it to run within a JS callout, You would need to:

  1. remove the "require" and use an IncludeURL element in the JS policy for the walkObj.js function.
  2. remove the mock context
  3. get the "source" from JSON.parse(context.getVariable('message.content'))
  4. get the mapRules from a KVM read (and JSON.parse() THAT too)

But it shows the principle:

  1. codify the mapping rules in some way
  2. walk the object and apply the rules
  3. re-serialize the modified object back to... some other context variable