Non key/value query parameters

We have a partner that needs to call an API on our side in Apigee Edge cloud in response to an order triggered on their website. We hoped to configure this response to go throught Apigee. The response is a HTTP GET verb which is passing a number of query parameters that are NOT Key value pairs i.e. just seperated with ampersands as in ?1&6&name&&. The kicker is that some of these are query parameters are optional and when the response is sent they represent the optional query parameter without a value as &&. The double ampersands are being removed by Apigee when we see the request in the trace.

Take an Example mydomain.com\test?1&&3

Where the first query parameter sent is 1, the second is optional and has no value and the third query parameter is a 3.

This request as seen in the trace shows as

mydomain.com\test?1&3

We need the querystring to contain all the queryparameters because since they are not key/value pairs we need to parse the querystring positionally and with the && being removed is throwing off the positions. Has anyone had to deal with anything like this in Apigee or have a solution to this problem.

Thanks,

Paul

3 11 2,905
11 REPLIES 11

Irrespective of the involvement in Apigee Edge in collapsing a double ampersand to a single ampersand, relying on the double-ampersand trick to represent a "missing" queryparam seems like a bad idea.

We need the querystring to contain all the query parameters because since they are not key/value pairs we need to parse the querystring positionally and with the && being removed is throwing off the positions.

I may be coming at this with a naive viewpoint, but I don't understand "we need to parse the querystring positionally". I don't think that is so. For example, you could parse them in JavaScript callout, like this:

// When retrieving queryparam names, convert the set to a string:
var names = context.getVariable('request.queryparams.names') + '';
// result is a string, like so: "[name1,name2]"
// or "[]" in the case of no query params passed.

// To then get an array of names, use substring and split:
names = names.substring(1,names.length-1).split(',');
// names = [ 'name1', 'name2' ]

var values = [];
var trimmed_names = [];
var hash = {};
names.forEach(function(n){
  n = n.trim();
  if (n) {
    var varname = 'request.queryparam.' + n;
    var v = context.getVariable(varname);
    values.push(v?v:'(blank)');
    trimmed_names.push(n);
    hash[n] = v;
  }
});

context.setVariable('qparam_names', JSON.stringify(trimmed_names));
context.setVariable('qparam_values', JSON.stringify(values));
context.setVariable('qparam_hash', JSON.stringify(hash));

If you include a JS policy like the above, and then add an AssignMessage policy like so:

<AssignMessage name='AM-Response'>
  <Set>
    <Payload contentType='application/json'>{
  "qparams" : {
    "names" : {qparam_names:[]},
    "values" : {qparam_values:[]},
    "hash" : {qparam_hash:null}
  }
}
</Payload>
    <StatusCode>200</StatusCode>
    <ReasonPhrase>OK</ReasonPhrase>
  </Set>
</AssignMessage>

Then, with this request:

curl -i http://myorg-myenv.apigee.net/queryparams1/t1?1&3&other=7

you will see this response:

{
  "qparams" : {
    "names" : ["1","3","other"],
    "values" : ["(blank)","(blank)","7"],
    "hash" : {"1":null,"3":null,"other":"7"}
  }
}

I suppose you would want to compare the received query params against the possible expected query params, and then you would find which ones were "missing".

I realize that NOT Passing them as Key/Value is a bad Idea. This is a restriction placed on us by our partner who is not willing to change their code. Believe me we asked.

I think you missed my point, there are no names just values being passed (no id=3). Your examples assumes the id is the name with value of 3, in my example I just have values 1, null, 3 no names but the second value is lost and becomes 3 erroneously.

I think you missed my point, there are no names just values being passed (no id=3). Your examples assumes the id is the name with value of 3, in my example I just have values 1, null, 3 no names but the second value is lost and becomes 3 erroneously.

I don't think I missed it. I understand it's not id=3. If you look at the example request I showed, some of the queryparams have no values. The example code I showed, demonstrates how to parse query params whether they have values or not. I've updated the example to try to be more explicit about it.

Your example doesn't address my exact problem. In my examples there are three query parameters lets call then q1,q2 & q3. the value of q1=1, the value of q2=null and the value of q3=3. The way these are being passed to us which we have no control of is myorg.com\test?1&&3. In an ideal world these would be passed as myorg.com\test?q1=1&q2=&q3=3

We seem to be misunderstanding each other. I showed a way to parse the incoming query params. Once you parse the list, I think it is possible to compare the parsed list, against the list of *possible, valid* query params, and from that you could determine which ones are not present in the request.

Eg, if the request sends in /test?1&&3

and the API is able to handle qparams of 1, 2 and/or 3

...then after the parse is complete, you can determine that 1 and 3 were passed, and 2 was not passed, because it is not present in the hash, which is the output of the parse.

That's what I meant when I wrote, in the answer

you would want to compare the received query params against the possible expected query params, and then you would find which ones were "missing".

Does that not solve your issue? If not, then I don't understand the problem.

queryparams.png3667-queryparams.png

Above is the trace of your code when I passed /test?1&&3. Query parameter 2 is missing but I don't know its missing cause I can't look by name because what is showing up in the names array are the values being passed. I know its confusing because whoever would pass the parameters this way. Long story short we have a client were trying to integrate with which is. Thanks for your help.

I think I get that, Paul. The part I think you're missing is something I left as an exercise for the reader :). But I'll show you what I mean, basically just compare the received qparams against the set of supported qparams. And that would give you the set of qparams that have been passed, and the set of qparams that are missing (or not passed) in the request.

Modified JS follows:

// getQueryParamNames.js
// ------------------------------------------------------------------
//

// It probably makes sense to parameterize supportedNames, with a Property setting
// in the JS policy configuration. Eg
//  <Property name='supported'>1,2,3</Property>
// and
// var supportedNames = properties.supported.split();


// for this demonstration, hard-code the supported params:
var supportedNames = ['1', '2', '3'];


// When retrieving queryparam names, convert the set to a string:
var receivedNames = context.getVariable('request.queryparams.names') + '';
// result is a string, like so: "[name1,name2]"
// or "[]" in the case of no query params passed.


// To then get an array of names, use substring and split:
receivedNames = receivedNames.substring(1,receivedNames.length-1).split(', ');
// names = [ 'name1', 'name2' ]


var values = [];
var hash = {};
receivedNames.forEach(function(n){
  n = n.trim();
  if (n) {
    var varname = 'request.queryparam.' + n;
    var v = context.getVariable(varname);
    values.push(v?v:null);
    hash[n] = v;
  }
});


context.setVariable('qparam_received', JSON.stringify(receivedNames));
context.setVariable('qparam_values', JSON.stringify(values));
context.setVariable('qparam_hash', JSON.stringify(hash));


// Now, compare the received qparams against the supported qparams.
var notpassed = supportedNames.filter(function(n) {
      return !hash.hasOwnProperty(n);
});


context.setVariable('qparam_notpassed', JSON.stringify(notpassed));
context.setVariable('qparam_supported', JSON.stringify(supportedNames));

I think the part your missing is the 3 and 1 in my example are values not the names. I can't compare them to the list of all names because these are not the names of the parameters they are the values. Your example would work if they passed us the names of every parameter each time as a Key/Value pair and if there was no value you still passed us the key but they are not doing that. This is no longer an issue because we kept asking them to send us Key/Value pairs and they finally reluctantly have agreed too.

Paul, glad you sorted it. I'm sorry I was unable to understand the problem you were having.

Not applicable

Unfortunately this is not possible as the message processor in APIGEE removes any empty "&". My suggestion is to try to get your partner to change how they are passing this value to you to be a key/value pair.

Thanks.

seeing the double-ampersand is not possible, but it's still possible to parse queryparams and determine which are passed, and which of the "Supported" params for a given API have not been passed. See code in my other answer.