How to set custom error message for Quota violation? - apigee

I tried multiple options of publishing custom error message for Quota violation. But I was not able to produce custom error message.
Please provide step by step process to generate custom error message. Thanks!

Create a Raise Fault Policy (but don't attach it to a flow):
<RaiseFault async="false" continueOnError="false" enabled="true" name="Fault-Bad-Quota">
<DisplayName>Fault-Bad-Quota</DisplayName>
<FaultRules/>
<Properties/>
<FaultResponse>
<Set>
<Headers/>
<Payload contentType="text/plain">My Custom Fault for {fault.name}</Payload>
<StatusCode>500</StatusCode>
<ReasonPhrase>Server Error</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>
Add a Conditional Fault to your Proxy configuration
<FaultRules>
<FaultRule name="BadQuota">
<Step>
<Name>Fault-Bad-Quota</Name>
</Step>
<Condition>fault.name = "QuotaViolation"</Condition>
</FaultRule>
</FaultRules>
You can add a global fault policy to your proxy default.xml. If you're using the UI go to the word "default" right under your target.

Related

APIGEE API Proxy - How do I prevent a request from hitting the target based on body content

I am trying to prevent requests that contain a json payload with a certain attribute value from hitting my target backend.
For example:
{"status":"pending"}
If the status is "pending", I don't want it to hit my target backend until I see a status of "delivered".
What is the best way to do this?
I have a step in the proxy endpoint preflow that uses a javascript policy to identify the attribute and its value. Now that I know this, how can I prevent the request from hitting the target backend and instead just return a 200 ok to the requester?
As you have the JS to identify the attribute already, now have a policy "Raise-Fault-Attribute" in your preflow after your JS policy.
<Step>
<Condition>(Status is pending) or (Status is null)</Condition>
<Name>Raise-Fault-Attribute</Name>
</Step>
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="Raise-Fault-Attribute">
<DisplayName>Raise Fault Attribute</DisplayName>
<Properties/>
<FaultResponse>
<Set>
<Headers/>
<Payload contentType="text/xml">
....
</Payload>
<StatusCode>500</StatusCode>
<ReasonPhrase>Server Error</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>
`
Hope this helps...

'Unexpected end of input block in comment' exception thrown when synapse configuration block is commented

'com.ctc.wstx.exc.WstxEOFException: Unexpected end of input block in comment' exception thrown when a configuration block is commented out in synapse.
Looks like the XML is not being parsed when an XML is commented out. If I have give a general XML as <!-- test -->, it gets parsed without any error.
Is there any workaround or a solution to overcome this issue?
Synapse Config:
<?xml version="1.0" encoding="UTF-8"?>
<proxy name="HelloWorld1" startOnLoad="true" transports="http https" xmlns="http://ws.apache.org/ns/synapse">
<target>
<inSequence>
<payloadFactory media-type="json">
<format>{"Hello":"World"}</format>
<args/>
</payloadFactory>
<sequence description="dfs" key="abcSequence"/>
<!-- <sequence key="testIn"/> -->
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</target>
</proxy>
Error Log:
[com.ctc.wstx.exc.WstxLazyException] com.ctc.wstx.exc.WstxEOFException: Unexpected end of input block in comment
at [row,col {unknown-source}]: [1,29]
at com.ctc.wstx.exc.WstxLazyException.throwLazily(WstxLazyException.java:45)
at com.ctc.wstx.sr.StreamScanner.throwLazyError(StreamScanner.java:720)
at com.ctc.wstx.sr.BasicStreamReader.safeFinishToken(BasicStreamReader.java:3677)
at com.ctc.wstx.sr.BasicStreamReader.getText(BasicStreamReader.java:858)
at org.apache.axiom.om.impl.builder.StAXOMBuilder.createComment(StAXOMBuilder.java:474)
at org.apache.axiom.om.impl.builder.StAXOMBuilder.next(StAXOMBuilder.java:279)
at org.apache.axiom.om.impl.llom.OMDocumentImpl.getOMDocumentElement(OMDocumentImpl.java:109)
at org.apache.axiom.om.impl.builder.StAXOMBuilder.getDocumentElement(StAXOMBuilder.java:570)
at org.apache.axiom.om.impl.builder.StAXOMBuilder.getDocumentElement(StAXOMBuilder.java:566)
at org.apache.axiom.om.util.AXIOMUtil.stringToOM(AXIOMUtil.java:54)
at org.apache.axiom.om.util.AXIOMUtil.stringToOM(AXIOMUtil.java:39)
at org.wso2.developerstudio.eclipse.gmf.esb.diagram.custom.deserializer.DummyMediatorFactoryFinder.getFactory(DummyMediatorFactoryFinder.java:241)
at org.wso2.developerstudio.eclipse.gmf.esb.diagram.validator.ProcessSourceView.validate(ProcessSourceView.java:954)
at org.wso2.developerstudio.eclipse.gmf.esb.diagram.validator.ProcessSourceView.mediatorValidation(ProcessSourceView.java:860)
at org.wso2.developerstudio.eclipse.gmf.esb.diagram.validator.ProcessSourceView.synapseValidation(ProcessSourceView.java:779)
at org.wso2.developerstudio.eclipse.gmf.esb.diagram.validator.ProcessSourceView.validateSynapseContent(ProcessSourceView.java:145)
at org.wso2.developerstudio.eclipse.gmf.esb.diagram.part.EsbMultiPageEditor.doSave(EsbMultiPageEditor.java:1015)
at org.eclipse.ui.internal.SaveableHelper.lambda$0(SaveableHelper.java:154)
at org.eclipse.ui.internal.SaveableHelper.lambda$3(SaveableHelper.java:271)
at org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:437)
at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:353)
at org.eclipse.ui.internal.WorkbenchWindow$14.run(WorkbenchWindow.java:2195)
at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
at org.eclipse.ui.internal.WorkbenchWindow.run(WorkbenchWindow.java:2191)
at org.eclipse.ui.internal.SaveableHelper.runProgressMonitorOperation(SaveableHelper.java:278)
This is because of a validation error in Integration Studio 6.5.0. (and in WSO2 EI Tooling 6.4.0) We have fixed it for the upcoming release. Please refer https://github.com/wso2/product-ei/issues/3985 for more details.

How to use the same Flow for request and response for Apigee proxy?

For Apigee API proxy, I need to select a flow based on request parameter. But I also need to remove that parameter before sending the request to the target.
I tried doing this:
<Flow name="SpecialFlow">
<Condition>request.queryparam.specialKey != null</Condition>
<Request>
<Step>
<Name>removeSpecialKey</Name>
</Step>
</Request>
<Response>
<Step><Name>doSpecialStuff</Name></Step>
</Response>
</Flow>
However, since specialKey is removed, the response step doSpecialStuff never gets called. How do I make sure the same flow is used for both request and response in this case?
I remove the parameter like:
context.removeVariable('request.queryparam.specialKey');
Great question and a common scenario. In my proxies I use one of two strategies.
In the first I a saveVars policy right before I head to the target flow, typically as the last step in the preflow request. That policy looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="true" enabled="true" name="assignSaveMessage">
<DisplayName>assignSaveMessage</DisplayName>
<AssignVariable>
<Name>save.request.verb</Name>
<Ref>request.verb</Ref>
</AssignVariable>
<AssignVariable>
<Name>save.request.queryparam.content</Name>
<Ref>request.queryparam.content</Ref>
</AssignVariable>
<AssignVariable>
<Name>save.request.content</Name>
<Ref>request.content</Ref>
</AssignVariable>
<AssignVariable>
<Name>save.request.queryparam.propagation</Name>
<Ref>request.queryparam.propagation</Ref>
</AssignVariable>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>
I then execute a restoreVars policy in the first step of postFlow response. This way I can use those variables as conditions. Here is the restore policy:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="true" enabled="true" name="assignRestoreMessage">
<DisplayName>assignRestoreMessage</DisplayName>
<AssignVariable>
<Name>request.verb</Name>
<Ref>save.request.verb</Ref>
</AssignVariable>
<AssignVariable>
<Name>request.queryparam.content</Name>
<Ref>save.request.queryparam.content</Ref>
</AssignVariable>
<AssignVariable>
<Name>request.queryparam.propagation</Name>
<Ref>save.request.queryparam.propagation</Ref>
</AssignVariable>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>
In your case, you would "stash" your queryparam right before you remove it as a queryparam.
The second approach is to simply assign the request param into another variable, a custom labelled variable that can be accessed throughout the flow. The only concern with this approach is the source of these copied variables is not obvious later flows.

Service callout with dynamic host name

I have a proxy in Apigee that uses a service callout to another proxy in the same environment. I would like to set the URL host for the callout to match the host of the initial request.
For example, if a request is made in the dev environment to:
https://example-dev.apigee.com/awesome-proxy
I need to make a call to:
https://example-dev.apigee.com/support-proxy
In a test environment the first call is to:
https://example-test.apigee.com/awesome-proxy
The support call needs to go to:
https://example-test.apigee.com/support-proxy
Here is how I would like to define the service callout policy:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="false" enabled="true" name="serviceCallout">
<DisplayName>serviceCallout</DisplayName>
<FaultRules/>
<Properties/>
<Request clearPayload="true" variable="example.request">
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
</Request>
<Response>example.response</Response>
<HTTPTargetConnection>
<Properties/>
<URL>{client.host}/support-proxy</URL>
</HTTPTargetConnection>
</ServiceCallout>
This will not save and complains about no protocol. The help indicates that this must be hard coded:
<HTTPTargetConnection>/<URL> element
The URL to the service being called. While the hostname portion of URL must be hard-coded, you can supply the remainder of the URL dynamically with a variable.
I found a variable to define the URL of a service callout:
servicecallout.{policy-name}.target.url
I attempted to use an assign message policy to dynamically set the variable, as follows:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="false" enabled="true" name="assignCalloutURL">
<DisplayName>assignCalloutURL</DisplayName>
<FaultRules/>
<Properties/>
<AssignVariable>
<Name>servicecallout.serviceCallout.target.url</Name>
<Value>{client.host}</Value>
<Ref/>
</AssignVariable>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>
This sets the URL to the literal text {client.host}
I have used the assign message policy in a similar fashion for other purposes and it actually resolves the variable listed. I'm not sure what's happening here.
client.host is not the correct variable to use, it returns an IP address 192.168....
I tried a few other variables:
proxy.url returns a strange host, it looks like internal Apigee machine names with a port. The proxy.url host times out when hit directly.
I ended up using virtualhost.aliases and proxy.pathsuffix. Here is the full JavaScript that solved it:
var base = context.getVariable("proxy.basepath");
var aliases = context.getVariable("virtualhost.aliases");
var url = "https://" + aliases[0] + base + "/support-proxy";
context.setVariable("servicecallout.serviceCallout.target.url", url);
Modifying target URLs gets a little goofy when dealing with the linter that ensures URLs have valid values.
This will not save and complains about no protocol.
This is because you're missing the scheme (https:// or http://) before the URL (client.host doesn't include scheme).
This sets the URL to the literal text {client.host}
That bit's not working properly because you need to use the ref tag to retrieve the existing variable:
<AssignVariable>
<Name>servicecallout.serviceCallout.target.url</Name>
<Ref>client.host</Ref>
</AssignVariable>
Now, that might work for the service callout, but it's not probably not going to work for setting the target URL.
I've ended up creating JavaScript policies to handle target.url, since the AssignMessage has been problematic for me:
var scheme = context.getVariable("client.scheme");
var host = context.getVariable("client.host");
var pathsuffix = context.getVariable("proxy.pathsuffix");
var newUrl = scheme + host + pathsuffix;
context.setVariable("target.url", newUrl);
Take look at this - it worked for me.

Statistics Collector Policy-Not able to add custom dimensions

I am unable to add Custom Dimensions in Custom Reports using statistics collector and extract variable policy mentioned below.
I have followed all the steps given in tutorial and docs but still I am stuck.
EXTRACT VARIABLE POLICY
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ExtractVariables async="false" continueOnError="false" enabled="true" name="Extract- Variables-1">
<DisplayName>Extract Variables 1</DisplayName>
<FaultRules/>
<Properties/>
<Source clearPayload="false">response</Source>
<VariablePrefix></VariablePrefix>
<XMLPayload stopPayloadProcessing="false">
<Namespaces>
<Namespace prefix="yweather">http://xml.weather.yahoo.com/ns/rss/1.0</Namespace>
</Namespaces>
<Variable name="weather.location" type="string">
<XPath>/rss/channel/link</XPath>
</Variable>
<Variable name="weather.condition" type="string">
<XPath>/rss/channel/item/yweather:condition/#text</XPath>
</Variable>
<Variable name="weather.forecast_today" type="string">
<XPath>/rss/channel/item/yweather:forecast[1]/#text</XPath>
</Variable>
<Variable name="weather.forecast_tommorow" type="string">
<XPath>/rss/channel/item/yweather:forecast[2]/#text</XPath>
</Variable>
</XMLPayload>
</ExtractVariables>
STATISTICS COLLECTOR POLICY
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<StatisticsCollector async="false" continueOnError="false" enabled="true" name="Statistics-Collector-1">
<DisplayName>Statistics Collector 1</DisplayName>
<FaultRules/>
<Properties/>
<Statistics>
<Statistic name="location" ref="weather.location" type="string">Earth</Statistic>
<Statistic name="condition" ref="weather.condition" type="string">Sunny</Statistic>
<Statistic name="forecast_today" ref="weather.forecast_today" type="string">Rainy</Statistic>
<Statistic name="forecast_tomorrow" ref="weather.forecast_tomorrow" type="string">Balmy</Statistic>
</Statistics>
</StatisticsCollector>
Your policies look fine (i.e., it looks like you haven't modified the samples in http://apigee.com/docs/analytics-services/content/analyze-api-message-content-using-custom-analytics), so the problem likely lies in your policy attachment, or, more likely, your deployment. Make sure your proxy is deployed in the expected environment. It can be useful to use the API to do this:
curl -u email:password https://api.enterprise.apigee.com/v1/o/{org_name}/apis/{api_name}/deployments
If your API proxy is deployed properly, please post your ProxyEndpoint configuration, and the request that you are sending to the API proxy and we'll take it from there.

Resources