Modify json payload Attributes in Apigee Proxy API

Assuming APIRequest received from user which eventually is going to be passed to TargetAPI to get the response, how can I

  1. Modify a property name "abc" to "xyz" in the API Request? assuming my TargetAPI understands only xyz and I can't share xyz name with my client? (Example refer to below APIRequest json object and here assume I want to modify "display-name" to 'fullName'
  2. Modify a complexType object present in APIRequest to a List of array object to be passed to TargetAPI. For example location to be an array object and added to a list like: 
    "locationList": {
      "location": [
        {
          "locationId": "1234",
          "locationName": "sdfsdfsdfsd",
          "locationCode": "AC"
        }
      ]
    }
    
    ​
  3. How can I hardcode some of the fields value in API Proxy so that I have to take just minimal input from customer and rest I can handle and pass to TargetAPI
  4. How can we use the ENUM option forcing customer to enter and pass only certain type of values--one option I could think is adding the same in OPEN API spec and handle the same through validations, is there any other option (example PhoneType)
    {
      "name" : "my_username",
      "first-name" : "My",
      "last-name" : "Username",
      "display-name" : "My Username",
      "email" : "user@example.test",
      "phone": [
        {
          "phoneNumber": "2021063016",
          "phoneType": "WORK"
        },
        {
          "phoneNumber": "2021063028",
          "phoneType": "HOME"
        }
      ], 
      "password" : {
        "value" : "my_password"
      },
      "location" : {
        "locationId" : "124",
        "locationName" : "asdasd",
        "locationCode" : "AC"
      }, 
      "active" : true
    }​

 

Any help will be appreciated.

Thanks

Solved Solved
1 8 7,394
1 ACCEPTED SOLUTION

Did this answer help?

View solution in original post

8 REPLIES 8

I'd suggest using a Javascript callout for these. Inside the Javascript callout you can read in the message body, then set or change arbitrary parts of it. You could also use this to perform additional verification. For the verification you could either set flow variables to indicate success/failure then trigger a conditional raisefault based on that, or simply throw an error from the JS if you don't care about the error message returned.

Thanks Christian!!

I can generalize the technical use-cases you described into two categories: payload modification, and payload validation. 

For #4, validating against an Enum, that sounds like a job for JSON Schema. You could do that with the OpenAPI Spec validator, or ... if you don't want the full validation of the request, and you want only the validation of the JSON payload itself, then you can use your own JSON Schema validator. 

For #1, #2, and #3, these are all just different flavors of the general case of "modifying a payload within the proxy." And I agree with Christian, use a JavaScript callout. In all cases, you will: (a) parse the payload into a JSON object. (b) mdoify the object. (c) stringify the resulting JSON object

For #1, you want to replace a property name.  Use something like this: 

var c = JSON.parse(context.getVariable('request.content'));
c.xyz = c.abc;
delete c.abc; 
context.setVariable('request.content', JSON.stringify(c));

For #2, you want to replace a complex object, and Wrap it into a list. You can do this with JavaScript that is not very different form the above:

var c = JSON.parse(context.getVariable('request.content'));
var loclist = [c.location];
delete c.location;
context.setVariable('request.content', JSON.stringify(c));

 For #3, now you are talking about something a little more interesting. Of course the simple way to solve it is to just add the set of default fields, like this: 

var c = JSON.parse(context.getVariable('request.content'));
c.addedField1 = VALUE1;
c.addedField2 = VALUE2;
context.setVariable('request.content', JSON.stringify(c));

But that's not a very nice, scalable solution. I assume the set of defaults will 

  • be spread across the entire JSON payload
  • vary according to the app, the client, the user, etc.  

For those reasons the simple JS shown above won't satisfy to set defaults. 

Instead what you need is

  • a way to walk through the JSON graph to set defaults at whatever level
  • a way to retrieve the defaults to use for each app, client, user, or etc.

To solve the first challenge you can use a recursive function.  It might look like this: 

function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

function deepMerge(orig, defaults) {
  var output = {};
  if (isObject(orig) && isObject(defaults)) {
    Object.keys(orig).forEach(function(key) {
      output[key] = orig[key];
    });
    Object.keys(defaults).forEach(function(key) {
      if (key in output) {
        if (isObject(defaults[key])) {
          output[key] = deepMerge(output[key], defaults[key]);
        }
      }
      else {
        output[key] = defaults[key];
      }
    });
  }
  return output;
}

To solve the second challenge, just use a custom attribute on the API Product, or client or app or token.  

Please find attached a working example. Using defaults of

{
      "field1" : 24,
      "phn" : {
        "type" : "mobile"
      }
}

if I pass in

{
    "name" : "Chris",
    "phn" : {
      "num" : "201-555-1212"
    }
}

...then the output is: 

{
  "name": "Chris",
  "phn": {
    "num": "201-555-1212",
    "type": "mobile"
  },
  "field1": 24
}

Whereas if I provide input of:

{
    "name" : "Chris",
    "phn" : {
      "num" : "201-555-1212",
      "type" : "work"
    }
}

 ...then I get this output: 

{
  "name": "Chris",
  "phn": {
    "num": "201-555-1212",
    "type": "work"
  },
  "field1": 24
}

Did this answer help?

Thank you, I am still getting error for the variables not defined and I am still working on it, bust thanks for the suggestions 

Hi,

I am still facing an issue with modifying the Request Payload, I followed the exact way initially as you suggested.

var c = JSON.parse(context.getVariable('request.content'));
var loclist = [c.location];
delete c.location;
context.setVariable('request.content', JSON.stringify(c));

my payload was a bit different and then I modified the js code as below
Assuming my request payload is as below:

{
	"requestInput": {
		"name": "my_username",
		"first-name": "My",
		"last-name": "Username",
		"display-name": "My Username",
		"email": "user@example.test",
		"phone": [{
				"phoneNumber": "2021063016",
				"phoneType": "WORK"
			},
			{
				"phoneNumber": "2021063028",
				"phoneType": "HOME"
			}
		],
		"password": {
			"value": "my_password"
		},
		"location": {
			"locationId": "124",
			"locationName": "asdasd",
			"locationCode": "AC",
			"code1": "value1",
			"locationInfo": {
				"status": "OPEN",
				"type": "NEW",
				"appointment": {
					"date": "2021-08-01",
					"visibility": "D",
					"category": "02"
				}
			}
		},
		"active": true
	}
}

I want

1. Modify attribute in the above request payload requestInput.location.code1 to requestInput.location.key1
2. Modify the above request payload to have the complex type appointment (requestInput.location.locationInfo.appointment)
to be as a list having appointment array with the complex types with three fields as given below
"appointmentList" :  {
    "appointment" :  [
         {

             "date": "2021-08-01",
             "visibility": "D",

             "category": "02"
        }
                              ]
}

Can you please help, I tried to read the json following way, but did not work

var request = JSON.parse(context.getVariable('request.content'));
request.requestInput.location.key1= request.requestInput.location.code1;
delete request.requestInput.location.code1;
context.setVariable('request.content', JSON.stringify(request));

 

 

Sure, I can try to help. This code works for me.

 

var r = JSON.parse(context.getVariable('request.content'));
r.requestInput.location.key1 = r.requestInput.location.code1;
delete(r.requestInput.location.code1);

r.requestInput.location.locationInfo.appointmentList = {appointment:[
  r.requestInput.location.locationInfo.appointment
]};
delete(r.requestInput.location.locationInfo.appointment);

//print(JSON.stringify(r, null, 2));
context.setVariable('request.content', JSON.stringify(r));

 

BTW, Your challenge is one of JSON re-shaping. It is not an Apigee-specific thing, you can test this in any JS execution environment. I tested this in Rhino ... but you could do the same in node, or any other JavaScript. here's my way of running the code in rhino (outside of Apigee):

 

java -cp .:./js-engine.jar:./rhino/buildGradle/libs/rhino-1.7.11-SNAPSHOT.jar EvalScriptFile ./jsonReshaping.js

 

I got the rhino jar from cloning the Rhino repo, and building it. 

Thank you, I was also able to do it yesterday, though I did the second one with two additional.

my solution

r.requestInput.location.locationInfo.appointmentList = {};
r.requestInput.location.locationInfo.appointmentList.appointment = [];
r.requestInput.location.locationInfo.appointmentList.appointment[0] = 
r.requestInput.location.locationInfo.appointment;

your solution 

r.requestInput.location.locationInfo.appointmentList = {appointment:[
  r.requestInput.location.locationInfo.appointment
]};


Kindly Comment.