This is pretty basic but I am struggling trying to build/retrieve values from a keyvaluemap. For example given variables X and Y where X=123abc,Y=foo on the first execution and X=456def, Y=bar on the second execution, I would expect to have a key value map where [123abc] -> [foo] and [456def] -> [bar].
However, when I attempt to retrieve the value in variable Z, using either value of X, I always get the last value added: Z=bar. If I delete key X=456def, retrieving key X=123abc returns an error that the key does not exist.
My guess is that only one value is being built into the map, and it is getting put in as "" as if the variable is not set. The override value "true" is replacing it with the last added value. I am sure the variable has a value as it is displayed correctly in the prior policy execution in the trace.
Is there something in the syntax? Any ideas?
I am using the policy below to build the key
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations mapIdentifier="map1" enabled="true" continueOnError="false" async="false" name="buildmap1">
<DisplayName>build map1</DisplayName>
<FaultRules/>
<Properties/>
<ExclusiveCache>false</ExclusiveCache>
<ExpiryTimeInSecs>-1</ExpiryTimeInSecs>
<InitialEntries/>
<Put override="true">
<Key>
<Parameter>ref="X"</Parameter>
</Key>
<Value ref="Y"></Value>
</Put>
<Scope>organization</Scope>
</KeyValueMapOperations>
I am using the following to retrieve the key
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations mapIdentifier="map1" enabled="true" continueOnError="false" async="false" name="getmap1">
<DisplayName>get map1</DisplayName>
<FaultRules/>
<Properties/>
<ExclusiveCache>false</ExclusiveCache>
<ExpiryTimeInSecs>-1</ExpiryTimeInSecs>
<InitialEntries/>
<Get assignTo="Z">
<Key>
<Parameter>ref="X"</Parameter>
</Key>
</Get>
<Scope>organization</Scope>
</KeyValueMapOperations>
Please have a look at article on https://community.apigee.com/articles/3384/keyvaluemap-kvmap-operations.html
<Parameter ref='X'></Parameter>
Where X is the variable. But, you were also looking to build a composite key out of two variables
<Parameter ref='X-Y'></Parameter>
will not work. You will need to use a Javascript policy to build that composite key.
Related
I built a proxy that basically expects a different JSON input object than the one the final endpoint is expecting to receive. So, in order to bridge the request object from one to the other I'm using an AssingMessage policy to transform the json input.
I'm doing something like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="false" enabled="true" name="Assign-Message-Sample">
<DisplayName>Assign Message-Sample</DisplayName>
<Remove>
<Headers>
<Header name="login_id"/>
<Header name="Authorization"/>
</Headers>
<Payload>true</Payload>
</Remove>
<Set>
<Payload contentType="application/json">
{
"valueA": "{clientrequest.valueA}",
"valueB": "{clientrequest.valueB}",
"valueC": "{clientrequest.valueC}",
"valueD": "{clientrequest.valueD}",
"valueE": "{clientrequest.valueE}",
"valueF": "{clientrequest.valueF}",
}
</Payload>
<Verb>POST</Verb>
</Set>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>
The problem comes when some of the values are empty. The destination server does not handle properly any empty values (escapes from my control).
My question is: how can I skip entirely a parameter if value is empty?
I'm looking for something like this (or better alternative):
<Payload contentType="application/json">
{
<skip-if-empty name="clientrequest.valueA">
"valueA": "{clientrequest.valueA}",
</skip-if-empty>
"valueB": "{clientrequest.valueB}",
...
}
</Payload>
For what I have found from my research, it seems this is a job for a Javascript Policy.
How is this done?
You basically need to place a javascript policy right before the AssignMessage execution. In the javascript policy you have the freedom to apply all the logic to omit certain parameters if values are not provided.
So for example, say we have already extracted the request values to variables using an ExtractVariables policy. Then, in the Javascript policy we can validate those values and build the resulting JSON object to later store it in another variable that will be picked up by the AssingMessage policy.
javascript policy:
var valueA = context.getVariable("clientrequest.valueA"),
valueB = context.getVariable("clientrequest.valueB"),
valueC = context.getVariable("clientrequest.valueC"),
...
var result = {};
if(valueB) {
result.b = valueB;
}
if(valueA) {
result.a = valueA;
}
if(valueC) {
result.c = valueC;
}
...
context.setVariable("newInput", JSON.stringify(result));
Then our AssignMessage will just pick up the variable we just stored: "newInput" that will contain the complete JSON object string:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="false" enabled="true" name="Assign-Message-Sample">
<DisplayName>Assign Message-Sample</DisplayName>
<Remove>
<Headers>
<Header name="login_id"/>
<Header name="Authorization"/>
</Headers>
<Payload>true</Payload>
</Remove>
<Set>
<Payload contentType="application/json">
{newInput}
</Payload>
<Verb>POST</Verb>
</Set>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>
This solution worked fine for me. I hope someone else finds it helpful.
I'm trying to cancel a pending transaction via PayPal's Classic API (SOAP) using the ManagePendingTransactionStatus method, but the response is always an internal error. I have already tried to change the Action, DetailLevel, Version, and I've used various (existing) TransactionIDs, but nothing worked.
Have someone passed through something alike? Is there some configuration I'm missing?
I'm using the sandbox environment, from my ASP.NET WebForms (framework version 4.5) app, within my localhost on Windows 7 x64. Here are the request content:
URL: https://api-3t.sandbox.paypal.com/2.0/
Request:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cc="urn:ebay:apis:CoreComponentTypes" xmlns:ebl="urn:ebay:apis:eBLBaseComponents" xmlns:ed="urn:ebay:apis:EnhancedDataTypes" xmlns:ns="urn:ebay:api:PayPalAPI">
<soapenv:Header>
<ns:RequesterCredentials>
<ebl:Credentials>
<ebl:Username>correct.username</ebl:Username>
<ebl:Password>correct.password</ebl:Password>
<ebl:Signature>Correct.Signature</ebl:Signature>
</ebl:Credentials>
</ns:RequesterCredentials>
</soapenv:Header>
<soapenv:Body>
<ns:ManagePendingTransactionStatusReq>
<ns:ManagePendingTransactionStatusRequest>
<ebl:Version>104.0</ebl:Version>
<ns:TransactionID>3B880366F0154954J</ns:TransactionID>
<ns:Action>Deny</ns:Action>
</ns:ManagePendingTransactionStatusRequest>
</ns:ManagePendingTransactionStatusReq>
</soapenv:Body>
</soapenv:Envelope>
Response:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:cc="urn:ebay:apis:CoreComponentTypes" xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext" xmlns:ed="urn:ebay:apis:EnhancedDataTypes" xmlns:ebl="urn:ebay:apis:eBLBaseComponents" xmlns:ns="urn:ebay:api:PayPalAPI">
<SOAP-ENV:Header>
<Security xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext" xsi:type="wsse:SecurityType"></Security>
<RequesterCredentials xmlns="urn:ebay:api:PayPalAPI" xsi:type="ebl:CustomSecurityHeaderType">
<Credentials xmlns="urn:ebay:apis:eBLBaseComponents" xsi:type="ebl:UserIdPasswordType">
<Username xsi:type="xs:string"></Username>
<Password xsi:type="xs:string"></Password>
<Signature xsi:type="xs:string"></Signature>
<Subject xsi:type="xs:string"></Subject>
</Credentials>
</RequesterCredentials>
</SOAP-ENV:Header>
<SOAP-ENV:Body id="_0">
<ManagePendingTransactionStatusResponse xmlns="urn:ebay:api:PayPalAPI">
<Timestamp xmlns="urn:ebay:apis:eBLBaseComponents">2014-05-28T14:27:44Z</Timestamp>
<Ack xmlns="urn:ebay:apis:eBLBaseComponents">Failure</Ack>
<CorrelationID xmlns="urn:ebay:apis:eBLBaseComponents">1c49de851e39e</CorrelationID>
<Errors xmlns="urn:ebay:apis:eBLBaseComponents" xsi:type="ebl:ErrorType">
<ShortMessage xsi:type="xs:string">Internal Error</ShortMessage>
<LongMessage xsi:type="xs:string">Internal Error</LongMessage>
<ErrorCode xsi:type="xs:token">10001</ErrorCode>
<SeverityCode xmlns="urn:ebay:apis:eBLBaseComponents">Error</SeverityCode>
</Errors>
<Version xmlns="urn:ebay:apis:eBLBaseComponents">104.0</Version>
<Build xmlns="urn:ebay:apis:eBLBaseComponents">10958405</Build>
<TransactionID xsi:type="xs:string">3B880366F0154954J</TransactionID>
<Status xsi:type="xs:string">The Status of the transaction after running the your action (accept/deny) is:Unable To Determine</Status>
</ManagePendingTransactionStatusResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
According to the documentation, The ManagePendingTransactionStatus API operation accepts or denys a pending transaction held by Fraud Management Filters. If it is pending for any other reason the operation will likely produce an error.
I have an http method fault that executes when an incorrect http method is sent in the request.
when I set the status code as 405 ,the request returns a 502 bad gateway .
my fault is :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="invalid-htttp-method-fault">
<DisplayName>invalid htttp method fault</DisplayName>
<FaultRules/>
<Properties/>
<FaultResponse>
<Set>
<Headers/>
<Payload contentType="application/xml">
<Fault>
<Code>405</Code>
<Description>Method Not Allowed</Description>
</Fault>
</Payload>
<StatusCode>405</StatusCode>
<ReasonPhrase>Method Not Allowed</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>
If I change
<StatusCode>405</StatusCode>
<ReasonPhrase>Method Not Allowed</ReasonPhrase>
to
<StatusCode>403</StatusCode>
<ReasonPhrase>Method Not Allowed</ReasonPhrase>
I can see the response payload is returned perfectly . when I use 405 the response returned is :
{"fault":{"faultstring":"Received 405 Response without Allow Header","detail":{"errorcode":"protocol.http.Response405WithoutAllowHeader"}}}
I was able to reproduce the exact issue that you are facing and by doing some more research I found that HTTP 405 response must include an Allow-Header
Try changing your fault policy by adding a header -
<Headers>
<Header name="Allow">YOUR ALLOWED METHODS LIST</Header>
</Headers>
By doing this you should no more be getting the 502 bad gateway and will get what you are expecting as a response.
I hope this helps.
Thanks!
Ah.... Interesting. Response 405 requires an Allow header per the HTTP spec (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html). So, the Apigee error is telling you that you need to add an Allow header to your FaultResponse like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="Fault-405">
<DisplayName>Fault 405</DisplayName>
<FaultRules/>
<Properties/>
<FaultResponse>
<Set>
<Headers>
<Header name="Allow">GET, PUT, POST, DELETE</Header>
</Headers>
<Payload contentType="text/plain">This wasn't supposed to happen</Payload>
<StatusCode>405</StatusCode>
<ReasonPhrase>405 Rules</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>
So add the Allow verbs in the <Set> block and you should be cool.
This is the service callout policy:
<ServiceCallout name="GeoCodeClient">
<Request clearPayload="false" variable="GeocodingRequest" />
<Response>GeocodingResponse</Response>
<Timeout>30000</Timeout>
<HTTPTargetConnection>
<URL>http://maps.googleapis.com/maps/api/geocode/json</URL>
</HTTPTargetConnection>
</ServiceCallout>
Let us say I have to access a resource that is username/password protected. How do I add that basic authorization to this policy to enable me to do that?
In our project a KeyValueMaps are used to store the basic auth info at org level. The authorisation information is retrieved using the KeyValueMap policy and added as the basic auth header to the request message.
See if this approach works for you.
To add Basic Authentication header for your service callout, you can use an 'AssignMessage' policy that sets the 'Authorization' header in the 'GeocodingRequest' as follows:
<AssignMessage enabled="true" continueOnError="true" async="false" name="AssignAuthorizationHeaderPolicy">
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="true" transport="http" type="request">GeocodingRequest</AssignTo>
<Add>
<Headers>
<Header name="Authorization">Basic YourAuthenticationHeader</Header>
</Headers>
</Add>
</AssignMessage>
Once you have created this policy, you will need to attach it in the request flow before the serviceCallout in the proxy.xml as flows:
<Step>
<FaultRules/>
<Name>AssignAuthorizationHeaderPolicy</Name>
</Step>
<Step>
<FaultRules/>
<Name>GeoCodeClient</Name>
</Step>
to add to what's already been said, if you need base64 encoding (and you probably will if you're using Basic Authorization), you'll need to do script callout. For instance, you can use the following Python:
import base64
if (client_secret is not None):
data = client_id + ":" + client_secret
header_value = base64.b64encode(data)
header_value = "Basic " + header_value
flow.setVariable("request.header.Authorization", header_value)
JS will be a little trickier since you need to include appropriate libraries, but I'm sure SO has plenty of more examples to follow for that.
Using Key Value Map to store sensitive data in a secure way
Step 1)Use below API to Create/Update the key Value maphttps://api.enterprise.apigee.com/v1/o/{orgname}/environments/{env}/keyvaluemaps Body:-{
"entry" : [ {
"name" : "basic_auth_system1",
"value" : "Basic XXXXXXXXXXX"
} ],
"name" : "system1_credentials"
}
Step 2) Policy used to lookup The key Value map
<KeyValueMapOperations enabled="true" continueOnError="false" async="false" name="keymap_get_credentials" mapIdentifier="system1_credentials">
<DisplayName>keymap_get_credentials</DisplayName>
<FaultRules/>
<Properties/>
<ExpiryTimeInSecs>-1</ExpiryTimeInSecs>
<Get assignTo="basic_auth_system1">
<Key>
<Parameter>basic_auth_system1</Parameter>
</Key>
</Get>
<Scope>environment</Scope>
</KeyValueMapOperations>
I am trying to write our own RIA services provider to expose data from a server that I access via ODBC. I follow th eguidelines set out at http://blogs.msdn.com/alexj/archive/2010/03/02/creating-a-data-service-provider-part-9-un-typed.aspx
I have written our own IDataServiceMetadataProvider / IDataServiceQueryProvider pair and get no errors on what i do.
I am putting in a resource set like this:
ResourceType tableType = new ResourceType(
typeof(Dictionary<string, object>),
ResourceTypeKind.EntityType,
null,
"Martini",
table_name,
false
);
tableType.CanReflectOnInstanceType = false;
var prodKey = new ResourceProperty(
"Key",
ResourcePropertyKind.Key |
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(int))
);
prodKey.CanReflectOnInstanceTypeProperty = false;
tableType.AddProperty(prodKey);
var prodName = new ResourceProperty(
"Name",
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(string))
);
prodName.CanReflectOnInstanceTypeProperty = false;
tableType.AddProperty(prodName);
_MetaDataProvider.AddResourceType(tableType);
_MetaDataProvider.AddResourceSet(new ResourceSet(table_name, tableType));
I see the requests coming in for enumerating the resource sets. I check them there in a breakpoint, and the resource set and the type is there, with all properties.
Still, the output I get is:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
- <service xml:base="http://localhost:2377/MartiniData.svc/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns="http://www.w3.org/2007/app">
- <workspace>
<atom:title>Default</atom:title>
</workspace>
</service>
And for the $metadata version:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
- <edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
- <edmx:DataServices xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="1.0">
- <Schema Namespace="Martini" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://schemas.microsoft.com/ado/2007/05/edm">
<EntityContainer Name="Martini" m:IsDefaultEntityContainer="true" />
</Schema>
</edmx:DataServices>
</edmx:Edmx>
The actual metadata for the types never shows up, no error is shown. pretty frustrating. Anyone any idea?
Hmpf. Found.
config.SetEntitySetAccessRule("*", EntitySetRights.All);
was missing in the initialization, so all entities were filtered out ;)