Developing a Custom Response Policy

When you are ready to develop your custom policy, follow the "Good Weather company" example to learn how to plan, develop, and implement your own policy later. Creating a policy involves the following steps:

The Use Case

To better understand how to implement a custom policy, imagine an example "Good Weather" company that is in the business of forecasting weather for a given location and time. Good Weather collects quantitative data about the current state of the atmosphere at a given place and, using meteorology, predicts how that current state will change over time.

Based on the current weather patterns, Good Weather wants to provide the capability to add custom responses to the weather forecast in the consuming application. The application must be able to manipulate the status code and response message based on a series of conditions that are evaluated on each request.

Telstra is an IT company that develops operating systems and applications for mobile and home computing technologies. Telstra wants to integrate with Good Weather to display daily weather forecasts on mobiles and PCs.

Good Weather uses an HTTP proxy with a Basic Authentication policy applied to it. Each time a request arrives, the policy checks the bearer token and performs one of the following tasks:

  • Allow the request to reach the backend, or

  • Reject the request, if validation is not successful, with a 401 Unauthorized Status code.

But what if, in case of unauthorized access, Telstra wants to include a different message each time it receives a 401 error. Developing a custom response policy allows Telstra to change the response message and status code based on a series of different conditions. For example,Telstra can state that if the response contains an Unauthorized status code, they modify the response payload accordingly.

However, if Telstra requires the application to provide its users with a custom response in case of a failed validation based on specific conditions, no default policy can work. For example, imagine that Telstra wants its application to not just return an error code but also provide additional response payload. In such a scenario, Telstra needs to create a custom policy.

Setting Up the Project

Telstra starts developing a custom policy by first creating a custom policy archetype project. Then Telstra runs the archetype, replacing the parameters shown here in bold with their own parameters.

  • DgroupId is Telstra Organization ID.

  • DartifactId is the custom-response-policy value.

    You can set it to any other value as required.

  • Dversion is 1.0.0.

    Note that DartifactId and Dversion do not have any restrictions.

mvn -Parchetype-repository archetype:generate \
-DarchetypeGroupId=org.mule.tools \
-DarchetypeArtifactId=api-gateway-custom-policy-archetype \
-DarchetypeVersion=1.2.0 \
-DgroupId=<Telstra_organization_ID> \
-DartifactId=custom-response-policy \
-Dversion=1.0.0 \
-Dpackage=mule-policy

Creating the YAML Configuration File

After Telstra sets up a project, they start adding dependencies and components to customize it. They start by defining the input parameters of the policy to configure in API Manager:

  1. Specify the conditions used to evaluate the request and response.

  2. If conditions are parametric, then DataWeave expressions are created.

  3. API Manager verifies whether the conditions are met at the same time.

  4. If the conditions are met, the status code is set.

  5. If the conditions are met, the payload is set.

The following code snippet shows an example YAML file for the policy UI:

  id: Custom Response
  name: Custom Response
  description: Modifies the payload and/or the status code when some condition is met.
  category: Custom
  type: custom
  resourceLevelSupported: true
  encryptionSupported: false
  standalone: true
  requiredCharacteristics: []
  providedCharacteristics: []
  configuration:
   - propertyName: evaluateInRequest
     name: Evaluate in the request
     description: If the condition should be evaluated in the request.
     type: boolean
     optional: true
     defaultValue: false
   - propertyName: requestCondition
     name: Request Condition
     description: Dataweave expression that will be evaluated to define if the request should be modified.
     type: expression
     optional: true
     sensitive: false
     allowMultiple: false
     dependsOnKey: evaluateInRequest
     dependsOnValue: true
   - propertyName: evaluateInResponse
     name: Evaluate in the response
     description: If the condition should be evaluated in the response.
     type: boolean
     optional: true
     defaultValue: false
   - propertyName: responseCondition
     name: Response Condition
     description: Dataweave expression that will be evaluated to define if the request should be modified.
     type: expression
     optional: true
     sensitive: false
     allowMultiple: false
     dependsOnKey: evaluateInResponse
     dependsOnValue: true
   - propertyName: mergeBothConditions
     name: Merge both conditions
     description: Check if both request and response conditions need to be met.
     type: boolean
     optional: true
     defaultValue: false
   - propertyName: statusCode
     name: Status Code
     type: int
     minimumValue: 0
     maximumValue: 999
     description: Status code to return when the condition is met.
     optional: true
     sensitive: false
     allowMultiple: false
   - propertyName: payload
     name: Payload
     type: expression
     description: Expression that sets the payload to return when the condition is met.
     optional: true
     sensitive: false
     allowMultiple: false

In the example, the dependsOnKey and dependsOnValue parameters specify that the key indicated in dependsOnKey must be set to the value indicated in dependsOnValue, to display the fields responseCondition or requestCondition.

In the UI, this translates to a checkbox in evaluateInTheRequest or evaluateInTheResponse that causes either the requestCondition or the responseCondition field to appear, depending on the value of the checkbox.

For more information about how to set up of the YAML file, see YAML Configuration File.

Based on the example, the UI is generated as:

custom-response-policy

The following screenshot illustrates the dependsOnKey and dependsOnValue parameters:

depends-on-key-demo

Implementing the Policy

After Telstra has defined the input parameters for their policy, they can start implementing their policy as follows:

  1. Add dependencies in the pom.xml file.

  2. Add conditions in the template.xml file.

Add Dependencies in pom.xml File

To transform the custom response with the new status code and payload, Telstra must create an extension. This helps them to avoid developing the boilerplate code to transform the response message:

  1. In the pom.xml, Telstra includes all the dependencies that they need to use.

    In this case, they need only the http-policy-transform-extension, which helps them to easily modify the response information.

  2. In the <dependencies> section of the policy pom.xml file, Telstra includes the following dependencies:

<dependency>
   <groupId>com.mulesoft.anypoint</groupId>
   <artifactId>mule-http-policy-transform-extension</artifactId>
   <version>${httpPolicyTransformVersion}</version>
   <classifier>mule-plugin</classifier>
</dependency>

For more information about how to create your own connector in Java, see Getting Started with the Mule SDK for Java.

Add Conditions in template.xml File

Telstra must now associate the YAML file with the Handlebars. The YAML file, is used to complete the fields in the XML, for example, to create the expressions that will be used, or to add an entire block of code.

In the template.xml file, Telstra must include the following code:

{{#if evaluateInRequest}}
<set-variable variableName="requestConditionMet" value="{{{requestCondition}}}" />
{{/if}}
<http-policy:execute-next/>
{{#if evaluateInResponse}}
<set-variable variableName="responseConditionMet" value="{{{responseCondition}}}" />
{{/if}}

Note that the pieces of code in double braces ({{}}) correspond to Handlebars syntax, which uses a template engine to compose the policy template with the configuration defined in the YAML file. The values used to configure the template must be the keys defined earlier in the YAML configuration file. Both values must match. Misspelling in any of the parameters can generate errors.

In the previous example, the parameters evaluateInRequest and evaluateInResponse define elements that will hold the value of the condition to be evaluated, defined in requestCondition and responseCondition.

For example, when you configure your policy, you want to validate that a particular header is present in the request. However, you do not want to check anything in the response. In this case, your policy configuration looks like this:

custom-response-configuration

When deployed, the template is resolved as displayed in the following code example. Because you did not specify any response condition, the Handlebars do not include it in the resolved template.

<set-variable variableName="requestConditionMet" value="#[attributes.headers['X-MyHeader'] == 'MyValue']" />
<http-policy:execute-next/>

Telstra then completes the logic of their template file with the following code:

<choice>
   {{#if mergeBothConditions}}
   <when expression="#[(vars.requestConditionMet default false) and (vars.responseConditionMet default false)]" >
       {{#if statusCode}}
       <set-variable variableName="statusCode" value="{{statusCode}}" />
       {{/if}}

       {{#if payload}}
       <set-variable variableName="payload" value="{{{payload}}}" />
       {{/if}}

       <http-transform:set-response statusCode="#[vars.statusCode default attributes.statusCode]">
           <http-transform:body>#[vars.payload default payload]</http-transform:body>
           <http-transform:headers>#[attributes.headers]</http-transform:headers>
       </http-transform:set-response>
   </when>
   {{else}}
   <when expression="#[(vars.requestConditionMet default false) or (vars.responseConditionMet default false)]" >
       {{#if statusCode}}
       <set-variable variableName="statusCode" value="{{statusCode}}" />
       {{/if}}

       {{#if payload}}
       <set-variable variableName="payload" value="{{{payload}}}" />
       {{/if}}

       <http-transform:set-response statusCode="#[vars.statusCode default attributes.statusCode]">
           <http-transform:body>#[vars.payload default payload]</http-transform:body>
           <http-transform:headers>#[attributes.headers]</http-transform:headers>
       </http-transform:set-response>
   </when>
   {{/if}}
</choice>

To modify the payload and the status code, if set, Telstra uses the http-policy-transform-extension parameter that is included in the dependencies.

Notice how Handlebars is used to resolve pieces of the template, as described earlier.

Telstra’s Custom Response policy is now ready to be uploaded to Exchange and tested.

Uploading to Exchange

To upload a custom policy to Exchange, Telstra must verify that their pom.xml file is correctly configured for their organization.

Task Prerequisites

Before Telstra uploads their policy to Exchange, they verify that:

  • A new server is created in their Maven settings.xml file.

  • A unique ID is assigned, and the username and password of the Exchange credentials exist.

  • The URL matches their region.

    Warning: When you perform similar actions to create your own custom policy, note that the template uses the URL that corresponds to the US region. If your account does not belong to this region, change this value.

  • The repository ID matches the one from the Maven settings.

    Tip: You can define an alias to that repository using the name tag.

<servers>
      <server>
         <id>your_server_id</id>
         <username>your_username</username>
         <password>your_password</password>
       </server>
</servers>

Next, Telstra goes to their policy pom.xml file and locates the distributionManagement section:

<distributionManagement>
    <repository>
        <id>your_server_id</id>
        <name>My Repository</name>
        <url>${exchange.url}</url>
        <layout>default</layout>
    </repository>
</distributionManagement>

After Telstra checks the configuration of the servers, they begin uploading their policy to Exchange using the following command:

> mvn deploy

The custom policy that Telstra developed is now uploaded to Exchange.

Applying the Policy

Telstra next applies a custom policy the same way as they apply a Default policy. When they go to API Manager, they can see the new policy available and ready to use:

custom-response-policy-platform

Was this article helpful?

💙 Thanks for your feedback!

Edit on GitHub