How can we apply fault string for Quota policy in apigee.

I configured quota policy by including custom attributes from application(Interval,TimeUnit,count) and fetched those values from the application.Now I am trying to configure fault string to specify the customized error information to the consumer for the quota policy.So,that I am want to notify the consumer with the present "counter (request count) status in the response at the time of violating the quota policy want to see the request count as a response to the consumer.

Solved Solved
1 10 538
1 ACCEPTED SOLUTION

Yes, this is a good pattern.

It is not possible to do it solely with a Quota policy. As you have probably seen there is no configuration option in the Quota policy that allows you to set the message that gets returned to the caller.

BUT, there is a general purpose way to do this within Apiege Edge: Fault Rules. Handling faults with Fault Rules is described in detail in the Apigee docs. But the short story is this: when a fault occurs for any reason, the "logic flow" within Apigee Edge transfers to Fault Rules. When a Quota policy rejects a call, it throws a fault, which means you can specify a Fault Rule that sets the appropriate response including payload and headers sent back to the caller.

For example you might want to respond with an HTTP 429 status, and a payload indicating "quota exceeded", and a set of headers that indicate the available quota, the call count available, and when the quota will reset.

The fault rules section in your proxyendpoint might look like this:

  <FaultRules>
    <FaultRule name="quota">
      <Condition>(fault.name = "QuotaViolation")</Condition>
      <Step><Name>AM-QuotaViolationMessage</Name></Step>
    </FaultRule>
  </FaultRules>

And the AssignMessage policy would look like this:

<AssignMessage name='AM-QuotaViolationMessage'>
  <Description>message for quota exceeded</Description>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <Set>
    <Headers>
      <Header name='X-Quota-Reset'>{ratelimit.Quota-1.expiry.time}</Header>
      <Header name='X-Quota-Allowed'>{ratelimit.Quota-1.allowed.count}</Header>
      <Header name='X-Quota-Available'>{ratelimit.Quota-1.available.count}</Header>
    </Headers>
    <Payload contentType='application/json'>{
  "error" : {
    "message" : "you have exceeded your quota",
    "clientId" : "{request.queryparam.apikey}"
  }
}
</Payload>
    <StatusCode>429</StatusCode>
    <ReasonPhrase>Quota Exceeded</ReasonPhrase>
  </Set>
</AssignMessage>

The above assumes your Quota policy has a name of "Quota-1".

You may decide to inject those Quota information into every response, in headers. Even responses that do not exceed the quota. That way a client would be able to always track how many calls it has made, when the quota will reset, how many calls are remaining, and so on.

If you did that, then ... the way to do it would be to drop a different AssignMessage policy, in the postflow. Something like this:

<AssignMessage name='AM-QuotaHeaders'>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <Set>
    <Headers>
      <Header name='X-Quota-Reset'>{ratelimit.Quota-1.expiry.time}</Header>
      <Header name='X-Quota-Allowed'>{ratelimit.Quota-1.allowed.count}</Header>
      <Header name='X-Quota-Available'>{ratelimit.Quota-1.available.count}</Header>
    </Headers>
  </Set>
</AssignMessage>

This policy would have the affect of adding headers, but it would not change the response payload or status code.

View solution in original post

10 REPLIES 10

Yes, this is a good pattern.

It is not possible to do it solely with a Quota policy. As you have probably seen there is no configuration option in the Quota policy that allows you to set the message that gets returned to the caller.

BUT, there is a general purpose way to do this within Apiege Edge: Fault Rules. Handling faults with Fault Rules is described in detail in the Apigee docs. But the short story is this: when a fault occurs for any reason, the "logic flow" within Apigee Edge transfers to Fault Rules. When a Quota policy rejects a call, it throws a fault, which means you can specify a Fault Rule that sets the appropriate response including payload and headers sent back to the caller.

For example you might want to respond with an HTTP 429 status, and a payload indicating "quota exceeded", and a set of headers that indicate the available quota, the call count available, and when the quota will reset.

The fault rules section in your proxyendpoint might look like this:

  <FaultRules>
    <FaultRule name="quota">
      <Condition>(fault.name = "QuotaViolation")</Condition>
      <Step><Name>AM-QuotaViolationMessage</Name></Step>
    </FaultRule>
  </FaultRules>

And the AssignMessage policy would look like this:

<AssignMessage name='AM-QuotaViolationMessage'>
  <Description>message for quota exceeded</Description>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <Set>
    <Headers>
      <Header name='X-Quota-Reset'>{ratelimit.Quota-1.expiry.time}</Header>
      <Header name='X-Quota-Allowed'>{ratelimit.Quota-1.allowed.count}</Header>
      <Header name='X-Quota-Available'>{ratelimit.Quota-1.available.count}</Header>
    </Headers>
    <Payload contentType='application/json'>{
  "error" : {
    "message" : "you have exceeded your quota",
    "clientId" : "{request.queryparam.apikey}"
  }
}
</Payload>
    <StatusCode>429</StatusCode>
    <ReasonPhrase>Quota Exceeded</ReasonPhrase>
  </Set>
</AssignMessage>

The above assumes your Quota policy has a name of "Quota-1".

You may decide to inject those Quota information into every response, in headers. Even responses that do not exceed the quota. That way a client would be able to always track how many calls it has made, when the quota will reset, how many calls are remaining, and so on.

If you did that, then ... the way to do it would be to drop a different AssignMessage policy, in the postflow. Something like this:

<AssignMessage name='AM-QuotaHeaders'>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <Set>
    <Headers>
      <Header name='X-Quota-Reset'>{ratelimit.Quota-1.expiry.time}</Header>
      <Header name='X-Quota-Allowed'>{ratelimit.Quota-1.allowed.count}</Header>
      <Header name='X-Quota-Available'>{ratelimit.Quota-1.available.count}</Header>
    </Headers>
  </Set>
</AssignMessage>

This policy would have the affect of adding headers, but it would not change the response payload or status code.

Could you please tell how can we put count value in a variable and display each and every request in the trace session

@Rajesh Nimmada - The Flow Variables section here has the info

I don't understand the question. I showed you how to add a count value to the headers. If you use that policy, that information will appear in the Trace UI. That seems to be what you're asking for here. Therefore, one of us seems to be not clear. Which is it, you or me? If you need something beyond that, ... please elaborate.

I executed this and seen the headers in the trace.Now I am want to kept these values in a variable by using the assign message policy.

for that, I configured below code

<AssignVariable>
<Name>time</Name>
<Ref>
{ratelimit.Quota-1.expiry.time}</Ref>
<Value/>
</AssignVariable>

in the payload I configured

<Set>
<Payload contentType="text/plain">

<variables>

<expiryTime>{time}</expiryTime>

</variables>

</Set>

But I didn't get the value from the ref tag

Ah, I see.

OK, 2 things.

  1. The value for Ref should be the name of a variable. It should not be surrounded in curly braces. Just the name of the variable. And you do not need the Value element. It should look like this:
    <AssignVariable>
      <Name>time</Name>
      <Ref>ratelimit.Quota-1.expiry.time</Ref>
    </AssignVariable>
  2. Assigning a variable to another variable might not be necessary. Are you sure you need it? the effect of the AssignVariable stanza is to assign the value held in "ratelimit.Quota-1.expiry.time" to "time". In subsequent policies you can reference the variable "time". but without the AssignVariable, you could reference the variable "ratelimit.Quota-1.expiry.time". In effect, you are creating an alias of an already-existing variable. Are you sure you need to do this?

I removed the braces, instead of the value it showing the variable name(time)in the response payload.

<AssignVariable>
  <Name>time</Name>
  <Ref>ratelimit.Quota-1.expiry.time</Ref>
</AssignVariable>

<Payload contentType="text/plain">
<variables>

<expiryTime>time</expiryTime>

<AllowedCount>{ratelimit.Quota-1.allowed.count}</AllowedCount>

<AvailableCount>{ratelimit.Quota-1.available.count}</AvailableCount>

</variables>

</Payload>

yes. ok, let's clarify. the braces must be absent in the AssignVariable. The braces must be present in the Payload. The Payload element is a "message template" . Everything between curly braces is de-referenced as a variable. if you specify the Payload as "foo{time}bar", and the variable "time" contains value 123456, then the result of the message template will be "foo123456bar". If you specify the Payload as 123time456 then the result of the message template will be "123time456" , the same as the original value, because there are no curly braces.

Therefore you must use

 ...
<AssignVariable>
  <Name>time</Name>
  <Ref>ratelimit.Quota-1.expiry.time</Ref>
</AssignVariable>
 ...

and

<AssignMessage name="..."> 
  <Set>
  <Payload contentType="text/plain">
  <variables>
  <expiryTime>{time}</expiryTime>
  <AllowedCount>{ratelimit.Quota-1.allowed.count}</AllowedCount>
  <AvailableCount>{ratelimit.Quota-1.available.count}</AvailableCount>
  </variables>
  </Payload>
  </Set>
</AssignMessage>

but remember, the latter is equivalent to :

<AssignMessage name="..."> 
  <Set>
  <Payload contentType="text/plain">
  <variables>
  <expiryTime>{ratelimit.Quota-1.expiry.time}</expiryTime>
  <AllowedCount>{ratelimit.Quota-1.allowed.count}</AllowedCount>
  <AvailableCount>{ratelimit.Quota-1.available.count}</AvailableCount>
  </variables>
  </Payload>
  </Set>
</AssignMessage>

You do not need the AssignVariable.

<Set>
<Payload contentType="text/plain">
<variables>
<!--<expiryTime>{ratelimit.Quota-1.expiry.time}</expiryTime> -->
<expiryTime>{time}</expiryTime>
<AllowedCount>{ratelimit.Quota-1.allowed.count}</AllowedCount>
<AvailableCount>{ratelimit.Quota-1.available.count}</AvailableCount>
</variables>
</Payload>
</Set>

But it shows empty tags instead of the value of "time"

Response Section:

<variables>
<expiryTime/>
<AllowedCount>5</AllowedCount>
<AvailableCount>2</AvailableCount>
</variables>

yes. that suggests the "time" context variable has not been set.

If you have AssignVariable in the same policy as AssignMessage, this might occur. And again, I BELIEVE YOU DO NOT NEED the "time" VARIABLE.

Have you tried the other way - with the existing variable? Can you please confirm you have read my suggestion and recommendation to try the existing variable, called ratelimit.Quota-1.expiry.time ? And can you please describe the results you see when you use that variable?