REST-SOAP remove null parameters

Not applicable

I am using the REST-SOAP-REST proxy-api

My WSDL endpoint consist of 3 parameters, call them p1, p2, p3. All of them are optional and has [0-9A-Za-Z]{8} 8 characters constraint for validation.

After deploying my WSDL, I attempted the rest call by doing http://xxx.xxx/myendpoint?p1=xx&p3=xx

Unfortunately I get an error doing that because it is saying p2 is not passing validation. So the leads me to think that even though I did not pass that parameter in the REST call, the proxy is passing p2 as an empty string to the WSDL endpoint.

Is there a way for me to not include parameters that are null?

0 5 3,180
5 REPLIES 5

Not applicable

Hi Churk,

How do you indicate your parameters are optional? Would you be willing to share your WSDL so we can debug it? If not, could you at least show the snippet where you indicate it's optional?

Thanks,

Charles

<s:element minOccurs="0" maxOccurs="1" name="param1" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="param2" type="s:guid"/>
<s:element minOccurs="0" maxOccurs="1" name="param3" type="s:string"/>
<s:schema elementFormDefault="qualified" targetNamespace="http://microsoft.com/wsdl/types/">
<s:simpleType name="guid">
<s:restriction base="s:string">
<s:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"/>
</s:restriction>
</s:simpleType>
</s:schema>

If you inspect your proxy there will be a AssignMessage Policy in request flow that produces the soap payload. currently, it includes all the parameters.

While charles is looking into this, one of the things you could do is to change the behavior how this soap payload is produced. For eg, you could create the soap payload either in Java or Js based on the input parameters and use it in you AssignMessage policy. Not ideal, but it would help overcome this for now

It's all good @mukundha@apigee.com I already came up with a solution using javascript policy.

Not applicable

For those who find this later and are looking for a solution to a similar problem I recommend the following pattern:

  1. An Extract Messages policy to pull the relevant fields for your SOAP call into flow variables from queryParams, headers, and/or payload. I typically name these ExtractResourceVariables where resource is the API exposed resource (eg. /v1/coupons/1234 where coupons is the resource).
  2. A Javascript callout policy to build the optional soap query elements and store these snippets of XML as flow variables. Javascript allows us to handle complex conditions that we could not achieve with a simpler policy.
  3. A BuildResourceQuery.xml file using the AssignMessage policy to assemble the bits into a backend query. The bit of magic here is we can include all of the potential query snippets realizing that some will return blank when they are not required (see example below).
  4. From there the target runs the query. This allows one single flow to service many different possible combinations.

Examples:

ExtractCouponVariables

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<ExtractVariables async="false" continueOnError="false" enabled="true" name="extractCouponVars">
    <DisplayName>extractCouponVars</DisplayName>
    <URIPath>
        <Pattern ignoreCase="true">/{version}/coupons/{coupon.code}</Pattern>
    </URIPath>
    <URIPath>
        <Pattern ignoreCase="true">/{version}/profile/{profile.aliasId}/coupons/{coupon.code}</Pattern>
    </URIPath>
    <QueryParam name="couponcode">
        <Pattern ignoreCase="true">{coupon.code}</Pattern>
    </QueryParam>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</ExtractVariables>

The Javascript code that builds the optional query elements for the SOAP message:

var filterOptions = "";

var entitled=context.getVariable("request.queryparam.entitled");
if(entitled==="true") entitled="Entitled";
else if(entitled==="false") entitled="NotEntitled";
if (entitled) {
    if (filterOptions === "") filterOptions = "<Option type=\"filter\">EntitlementState==" + entitled;
    else filterOptions += "&&EntitlementState=" + entitled;
}

var propogationState = context.getVariable("request.queryparam.propagation");
if (!propogationState || propogationState === "") propogationState = "true";
switch (propogationState) {
    case "true":
        if (filterOptions === "")
            filterOptions = "<Option type=\"filter\">[CustomProperties.href^propagationState&&CustomProperties==true]";
        else
            filterOptions += "&&[CustomProperties.href^propagationState&&CustomProperties==true]";
        break;
    case "false":
        if (filterOptions === "")
            filterOptions = "<Option type=\"filter\">[CustomProperties.href^propagationState&&CustomProperties==false]";
        else
            filterOptions += "&&[CustomProperties.href^propagationState&&CustomProperties==false]";
        break;
}
if (filterOptions !== "") filterOptions += "</Option>";

context.setVariable("coupon.filterOptions", filterOptions);

And finally the build SOAP policy (edited for privacy):

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<AssignMessage name="buildCouponLookup">
    <DisplayName>buildCouponLookup</DisplayName>
    <AssignTo createNew="true" type="request">request</AssignTo>
    <Set>
        <Headers>
            <Header name="Content-Type">text/xml; charset=utf-8</Header>
        </Headers>
        <Payload contentType="text/xml">
            <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <soap:Body>
                    <fetchCoupon xmlns="http://soap.foo.com/v4_2/Coupon">
                        <auth>
                            <version>4.2</version>
                            <login>{target.username}</login>
                            <password>{target.password}</password>
                        </auth>
                        <couponId>{coupon.code}</couponId>
			{coupon.filterOptions}
                    </fetchCoupon>
                </soap:Body>
            </soap:Envelope>
        </Payload>
        <Verb>POST</Verb>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</AssignMessage>

Of note here is the IgnoreUnresolvedVariables option in the AssignMessage policy - this allows the policy to execute even if we don't have a value for coupon.filterOptions - that line is blank in that case allowing us to achieve an optional filtered query with the same policy as an unfiltered query for all coupons matching a particular code.

The downside of this approach is the JSC can get fairly complex when you have many different options. In cases where this complexity is too great we find it worthwhile to replicate the build policies and live with the redundancy of the configuration in favor of readability and reduced complexity.