Using AssignMessage policy to generate a formatted string in Apigee - apigee

I am doing a URL rewrite in my API End point. So, the querystring is not passed by default to the backend service.
I want to create a variable with name "querystring" and format that string with the input query params. How can I do that?
For eg: apigee api url = https://myapigee-prod.agigee.net/v1/test/resource1?p1=abc&p2=123
I want to create the querystring variable as querystring = param1=abc&param2=123
I tried
<AssignVariable>
<Name>myqs</Name>
<Value>param1={request.queryparam.p1}&param2={request.queryparam.p2}</Value>
</AssignVariable>
<Ref> tag can be used instead of <Value> tag only if I am getting value of one parameter. To form a string, it wouldn't work.

Unfortunately you can't use the curly braces method of variable expansion within an AssignVariable.
You can do this via a JavaScript policy. Here is the JavaScript code:
var myqs = "param1=" + context.getVariable("request.queryparam.p1") +
"&param2=" + context.getVariable("request.queryparam.p2");
context.setVariable("myqs", myqs);

Also, you can use an AssignMessage policy to do this, on the Proxy request or Target request flow.
http://apigee.com/docs/api-services/content/generate-or-modify-messages-using-assignmessage
e.g.
<AssignMessage name="GenerateRequest">
<AssignTo createNew="false" type="request">Request</AssignTo>
<Set>
<QueryParams>
<QueryParam name="p1">{request.queryparam.param1}</QueryParam>
<QueryParam name="p2">{request.queryparam.param2}</QueryParam>
</QueryParams>
</Set>
</AssignMessage>

Related

Send a PUT request with a file and custom headers

I am stuck at a problem.
I need to upload a file (a CSV file) to a server by using Delphi. The server have certain requirements that must be followed :
The request must be an HTTP PUT request.
Must contain the Client ID in the URL request, like this : example.com/upload/{Client ID} ({Client ID} would be replaced).
Must contain the future file name that will be received also in the URL after the client ID, like this : example.com/upload/{Client ID}/{Future File Name} ({Future File Name} would be replaced), which can be different from your local file name.
The API Key must be in the request header (header name: x-api-key). Example : lXViWTzFic9sM8qe9Ew7JME8xTdBAOMJHdIjK7XkjQ00OWr.
I could do this in CURL, like this example : (Client ID : d32krpq, and Future file name : 20181023_update.csv)
curl -v -X PUT https://example.com/upload/d32krpq/20181023_update.csv --upload-file 20181023_update.csv --header "x-api-key: lXViWTzFic9sM8qe9Ew7JME8xTdBAOMJHdIjK7XkjQ00OWr"
How to achieve this using Delphi components (using either TNetHttpClient or TRESTClient)?
In Delphi, sending multiple fields (files, custom headers, body fields, ...) in your PUT request is possible in both HTTP client frameworks:
In TNetHTTPClient through the method Put(), you can use the overloaded function with the local file path's string parameter. And in the function's optional parameter of TNetHeaders object, you specify your custom header parameters.
TNetHeaders is a dynamic array of the TNameValuePair record.
So, you have to create a dynamic array of TNameValuePair, then you create an instance of the TNameValuePair record with your API key and add it to the dynamic array.
With the exception that in TNetHTTPClient, you have to specify the URL segments parameter directly in your URL's request by yourself.
And finally, you call the Put() method with your file path's string and TNetHeaders objects, like this example:
procedure TForm1.PutFile;
var
LHTTP: TNetHTTPClient;
LHeaders: array of TNameValuePair;
begin
SetLength(LHeaders, 1);
LHeaders[0] := TNameValuePair.Create('x-api-key', 'lXViWTzFic9sM8qe9Ew7JME8xTdBAOMJHdIjK7XkjQ00OWr');
LHTTP := TNetHTTPClient.Create(nil);
try
LHTTP.Put('https://example.com/upload/d32krpq/20181023_update.csv', 'C:/MyFile.csv', nil, LHeaders);
finally
LHTTP.Free;
end;
end;
For the REST Client library, you can simply use the TRESTRequest.AddFile() function to add a file, and you can use the AddParameter() function to add any custom fields as a body, an URL segment or a header parameter.
Another advantage in TRESTClient is that you can use a base URL or a resource where you define URL segments, like "example.com/upload/{Client ID}/{Future File Name}".
Drop the components TRESTClient, TRESTRequest, and TRESTResponse on the form, set the TRESTClient.BaseURL to your upload server, and write your code. Like in this example:
procedure TForm1.PutFile;
begin
try
try
RESTRequest1.Method := TRESTRequestMethod.rmPUT;
RESTRequest1.AddFile('File1', 'C:\MyFile.csv', TRESTContentType.ctTEXT_CSV);
// Adding the parameters to be included in the URL
RESTRequest1.AddField('Client ID', 'd32krpq', pkURLSEGMENT);
RESTRequest1.AddField('Future File Name', '20181023_update.csv', pkURLSEGMENT);
// Adding a header parameter
RESTRequest1.AddField('x-api-key', 'lXViWTzFic9sM8qe9Ew7JME8xTdBAOMJHdIjK7XkjQ00OWr', pkHTTPHeader);
RESTRequest1.Execute;
except
ShowMessage('Uploading failed');
end;
finally
ShowMessage(RESTRequest1.Response.Content); // here you can see your server's response
end;
end;
Here is an alternative approach using Indy's TIdHTTP component:
FS := TIdReadFileExclusiveStream.Create('20181023_update.csv');
try
IdHTTP1.Request.CustomHeaders.Values['x-api-key'] := 'lXViWTzFic9sM8qe9Ew7JME8xTdBAOMJHdIjK7XkjQ00OWr';
IdHTTP1.Put('https://example.com/upload/d32krpq/20181023_update.csv', FS);
finally
FS.Free;
end;

Path Variables in Web Security Expressions and set /users/{userId:\\d+}

spring security 4.1.3.RELEASE intercept-url request-matcher="mvc"
I try to use spring-security.xml to config my web security with request-matche="mvc", but the pattern "/users/{userId:id}" dosen't work.
My config just look like the following code:
<http entry-point-ref="myAuthenticationEntryPoint" auto-config="true" use-expressions="true" create-session="stateless" request-matcher="mvc"
access-decision-manager-ref="myAccessDecisionManager">
<intercept-url pattern="/users/{userId:\\d+}" method="GET" access="#webSecurity.isMe(authentication, #userId) or hasAnyRole('ADMIN')"/>
<intercept-url pattern="/users/management" method="GET" access="hasAnyRole('ADMIN')"/>
</http>
I expecte that the request for /users/12345 will match the pattern /users/{userId:\d+} which will match 12345 to the userId, and the request for /users/management will match the pattern /users/management. However, in fact, the pattern /users/{userId:\d+} dosen't work, it never match the request /users/12345. and if I change the setting to the following code:
<http entry-point-ref="myAuthenticationEntryPoint" auto-config="true" use-expressions="true" create-session="stateless" request-matcher="mvc"
access-decision-manager-ref="myAccessDecisionManager">
<intercept-url pattern="/users/{userId}" method="GET" access="#webSecurity.isMe(authentication, #userId) or hasAnyRole('ADMIN')"/>
<intercept-url pattern="/users/management" method="GET" access="hasAnyRole('ADMIN')"/>
</http>
Remove the :\d+ from /users/{userId:\d+}, it will match the request for /users/12345. But it will match the request for /users/management as well, which makes the value of userId equals to unexpected "management". I have try ant-matcher and regex-matcher, and I can't find the userId in a perfect solution.
I have solved the problem by myself. It's a stupid mistake. To solve the problem, I just need to replace /users/{userId:\\d+} with /users/{userId:\d+} or /users/{userId:[0-9]+} and use AntPathMatcher instead of using MvcPathMatcher. Configuring the security with spring-security.xml is defferent with using javaConfig or using annotation. In javaConfig or annotation #RequestMapping, the content in "" is String Object, so we need an annother \ to escape the \d+. But in xml, it's unnecessary. If using \\d+ here, spring security will append it to \\\\d+, which will never match number type string. I find this solution when I set a breakpoint in org.springframework.util.AntPathMatcher.matchStrings(String str, Map<String, String> uriTemplateVariables) and watch the request.

What is the best practice to transform the path between proxy and target in apigee?

I am making a request /Patient/1 at the proxy endpoint, and I want to transform this into target endpoint /openemr/api. I tried solutions given in https://community.apigee.com/articles/2793/how-to-geo-locate-calls-to-target-endpoint.html, but none of these worked for me. Please suggest an optimal way for achieving this.
Thanks in advance.
It depends on how many reroutes you have to implement. So in case of 5+, use a javascript policy, such as:
var reqPathsuffix = context.getVariable("proxy.pathsuffix");
var reqVerb = context.getVariable("request.verb");
if ((reqPathsuffix === "/contact") && (reqVerb === "GET"))
{
endPointURL = "https://yourendpointurl/
}
context.setVariable("target.url",endPointURL);
else maybe use a 'AssignMessage' policy.
<AssignMessage async="false" continueOnError="false" enabled="true" name="Assign-Message-1">
<AssignTo createNew="false" transport="http" type="request"/>
<AssignVariable>
<Name>target.url</Name>
<Value>http://google.nl</Value>
</AssignVariable>
Don't forget to do this on the 'target endpoint preflow request' flow. Else it won't work.

How to add basic auth to a service callout policy

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>

MuleESB - get file from http post

In Mule I need to manipulate via java an xlsx file sent via http post.
How can I get the file posted via java?
I thought it was reachable via Mule message but
eventContext.getMessage().getOutboundAttachmentNames()
and neither
eventContext.getMessage().getInboundAttachmentNames()
give results.
Any ideas?
to make http post test I use curl in this way:
curl --form upload=#filename --form press=OK http://localhost:8088/HttpController
The flow is simply something like this:
<flow name="xlsx_to_xls_converterFlow1" doc:name="xlsx_to_xls_converterFlow1">
<http:inbound-endpoint exchange-pattern="request-response" doc:name="HTTP" address="http://localhost:8088/HttpController"/>
<logger level="INFO" doc:name="Logger"/>
<component class="Convert_XLSXtoXLS" doc:name="Java"/>
</flow>
Thank you
UPDATED
To let the flagged solution work occurs to override extractPayloadFromHttpRequest of HttpMultipartMuleMessageFactory to choose the proper input file name.
In fact with the current HttpMultipartMuleMessageFactory implementation the file is uploaded only if input file name = "payload"
You need to configure your HTTP connector to handle multipart requests to receive them in attachments. Add the following inside its XML configuration:
<service-overrides messageFactory="org.mule.transport.http.HttpMultipartMuleMessageFactory"/>
( if you think this is cumbersome, please upvote https://www.mulesoft.org/jira/browse/MULE-6862 )
Putting a Java component behind an http:inbound-endpoint results in an InputStream as argument to the method in the component.
You have to work with the input stream or just put an echo in between:
<flow name="FileUpload" doc:name="FileUpload">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="9090" doc:name="HTTP"/>
<echo-component doc:name="Echo"/>
<component class="de.codecentric.basics.FileUploadComponent" doc:name="Java"/>
</flow>
The component has one method:
package de.codecentric.basics;
public class FileUploadComponent {
public String process(String message) {
System.out.println("message: " + message);
return "OK";
}
}
You still have to parse the multipart form data in this case.
Or try to use the REST component, see: http://www.javaroots.com/2013/05/createfileuploadmulejerseyrest.html

Resources