Making query params optional in IBM API Connect - apiconnect

Hi we are using IBM Api Connect as a gateway for our api's, i am unable to figure out how to make query params optional, i tried doing it in the DESIGN and ASSEMBLE sections of IBM api connect, but no luck.
this is my final URL that i want to invoke
www.testdomain.products/getProducts?param1 = " "& param2 = " " & param3 = " "
here all the params are optional i am giving this url as
www.testdomain.products/getProducts?param1=$(request.parameters.param1)&param2=$(request.parameters.param2) & param3=$(request.parameters.param3)
in one use case i am only passing param1, and want the final url to be constructed only with param1 but this is what i am seeing
www.testdomain.products/getProducts?param1="value"&param2=&param3=
what should i do so that the url is constructed with only the values that i am passing, like this
www.testdomain.products/getProducts?param1="value"

It looks like you just want to proxy the query string? In which case you can use $(target-url)$(request.search) as your invoke URL.

This can be achieved by creating the dynamic target URL using a script rather than setting static backend service URLin invoke/Proxy action.
Assembly section
Get GatewayScript Action before invoke
var targetUrl = "www.testdomain.products/getProducts?";
var input_param1 = apim.getvariable('request.parameters.param1');
var input_param2 = apim.getvariable('request.parameters.param2');
if(input_param1 !==null || input_param1 !="" || input_param1 != undefined ){
targetUrl = targetUrl.concat("param1="+input_param1))
}
if(input_param2 !==null || input_param2 !="" || input_param2 != undefined ){
if(targetUrl.indexOf('param1') > 0){
targetUrl = targetUrl.concat('&')
}
targetUrl = targetUrl.concat("param2="+input_param2))
}
apim.setvariable('target-url',targetUrl)
In Invoke/Proxy - Use this $(target-url)
There might be some syntax issue, but we can achieve using the above way. Thanks!

Related

ASP.Net OnGet - URL encoded Parameters - Only some parameters are Decoded by ASP.Net Framework

Ola,
for some reason, it looks like ASP.Net Core Webserver (.Net 6) seems to url-decode some characters for the PageModel.OnGet Method and some characters are not url-decoded.
The request according URL in Internet Edge in the addressbar:
http://localhost:5000/ItemsOverview/Data/dm%3A%2F%2F%2Fxxx.ebs.data%3Fmetaclass%3Ddm%253A%252F%252F%252Fxxx.ebs%25232d73a22b-8505-4523-939d-7f524253f95b
This is what is sent to the OnGet Method:
ItemsOverview.cshtml: #page "/ItemsOverview/{workspace}/{extent}/{item?}"
ItemsOverview.cshtml.cs:
public void OnGet(string workspace, string extent, string? item)
{
Workspace = HttpUtility.UrlDecode(workspace);
Extent = HttpUtility.UrlDecode(extent);
Item = HttpUtility.UrlDecode(item);
}
The variable 'extent' as received by the debugger looks like the following:
dm:%2F%2F%2Fxxx.ebs.data?metaclass=dm%3A%2F%2F%2Fxxx.ebs%232d73a22b-8505-4523-939d-7f524253f95b
==> So, the server has changed %3F to '=' and %25 to '%', but not characters like %2F...
The url is generated by using the javascript function 'encodeURIComponent':
export function getLinkForNavigateToExtentItems(workspace: string, extentUri: string, parameter?: NavigationToExtentItemsParameter) {
let urlParameter = "";
let ampersand = '?';
if (parameter?.metaClass !== undefined) {
urlParameter += ampersand + "metaclass=" + encodeURIComponent(parameter.metaClass);
ampersand = '&';
}
return Settings.baseUrl + "ItemsOverview/" +
encodeURIComponent(workspace) + "/" +
encodeURIComponent(extentUri + urlParameter);
}
Expectation:
The OnGet Call is called with the parameter 'extent' is given in the Url by Browser.
Situation:
Some characters are already decoded by Browser(?) or ASP.Net Core Framework(?)
Resolved by:
public static string? DecodePath(string? pathToBeDecoded)
{
return pathToBeDecoded?
.Replace("%2F", "/")
.Replace("%2f", "/")
.Replace("%25", "#");;
}
I'm not convinced, but it seems to work.

If then else implementation to chose baseURL in Karate DSL

I have a little tricky requirement in Karate. I have a set of baseURL's in my karate.config which are chosen based on the implementation. Here is the snippet of it:
if (env == 'qa') {
config.apiKey = apiKey;
config.tsp_api = 'https://api.qa.tceu.net';
config.svt_dcm = 'https://svt.qa.tceu.net';
config.acn_dcm = 'https://acn.qa.tceu.net';
config.sos_dcm = 'https://sos.qa.tceu.net';
config.cust_dcm = 'https://cust.qa.tceu.net';
Here tsp,svt,acn,sos,cust are some actions.
I have a feature file which passes the action as a parameter:
# Vehicle Initiates the action
When def Perform_Report_Notification = call read('./../common/performActionNotification.feature') { action: '#(action)' }
In the called performActionNotification.feature, I need to pick up the url from the karate.config file based on the action that is passed. For example if the action is sos, then the url should be sos_dcm. If the action is svt then the url should be svt_dcm
Here is the snippet from performActionNotification.feature and what I am currently doing for sos:
Given url sos_dcm
And path '/AU/v1.0/TSP/'+ action
And request RequestPayload
When method post
Then status 200
I want to implement something like an if then else similar to:
if (action == 'sos')
then myurl == 'sos_dcm'
else if (action == 'acn')
then myurl == 'acn_dcm'
else if (action == 'svt')
then myurl == 'svt_dcm'
Given url myurl
And...
And...
...
I tried a sort of a hack and it works but its not a clean way of doing it. Instead of reading the URL from karate.config I am hardcoding it this way:
Given url 'https://'+act+'.qa.tceu.net'
One more thing I tried was
* def myurl = action +'_dcm' #so if action is acn then the variable myurl would be acn_dcm
Given url myurl
...
....
But this hardcodes the url as 'acn_dcm' instead of picking the defined url up from karate.config.
Can someone kindly suggest the best way to implement this?
Here is a hint. JSON is actually a pretty useful data-structure (think hash-map or dictionary) and you can lookup a value without needing an if statement.
* def data =
"""
{
qa: {
sos: 'https://sos.qa.tceu.net',
acn: 'https://acn.qa.tceu.net'
}
}
"""
* def env = 'qa'
* def urls = data[env]
* def action = 'sos'
* def actionUrl = urls[action]
* match actionUrl == 'https://sos.qa.tceu.net'
This should get you on your way :)
EDIT - also see this: https://stackoverflow.com/a/67868935/143475
I've finally used Peter's most elegant solution and it works like a charm!
Here's what I've finally implemented that does not need hardcoding of the endpoint URL's and is driven by endpoints in the karate.config file.
* def data =
"""
{
qa: {
sos: '#(sos_dcm)', # sos_dcm endpoint defined in karate.config file
acn: '#(acn_dcm)',
svt: '#(svt_dcm)'
}
}
"""
* def env = karate.properties['env']; # Driven by maven commandline arg -Denv=qa as an example
* def urls = data[env]
* def action = act # act comes from the calling feature file and has values - sos/acn/svt
* def myUrl = urls[action]
Given url myUrl
...
...
I would suggest looking into using javascript for your conditional logic
So the javascript function takes a param of action and then the if and else statements returns the variable of the url that you need.
Perform the javascript function before you make the request call. and use the variable that is returned by js to determine the logic.
make that js file a common function that can be accessed by multiple feature files.
function determineUrl(action) {
var url = "${urDefaultUrl}";
if (action == "sos") url == "${full url}";
else if (action == "acn") url == "${full url}";
return url;
}
Then in your feature file
* def urlDecider = 'classpath to your js function'
* myUrl = urlDecider(action)
* url myUrl
* Given path ....
For the sake of the community learning, there is one other way I figured out on similar lines of Jawad's solution is using a Java function. They do exactly the same thing as Jawad's solution but just that its a java class doing it. If a project has java class files, then to maintain consistency this solution can be used too.
Here is the how the class file looks:
public class DCMUrlDecider {
static String dcmURL="";
public static String getDCMUrl(String action) {
if (action.matches("sos"))
{
dcmURL = "https://sos.qa.tceu.net";
}
else if (action.matches("acn"))
{
dcmURL = "https://acn.qa.tceu.net";
}
else if (action.matches("svt"))
{
dcmURL = "https://svt.qa.tceu.net";
}
return dcmURL;
}
}
And here is the associated code snippet from the feature file:
* def dcmURLDecider = Java.type('com.TCEU.KarateTests.DCMUrlDecider')
* def myUrl = dcmURLDecider.getDCMUrl(act)
Given url myUrl
....
.....
Once again this means we are still hardcoding URLs in the java class files. Need to learn a way of doing it via karate.config files if possible.

Incoming Express parameters are not the same as whats passed in

I have a strange issue where I have passed in parameters from a URL, into my Express server,
When I get the req.params.code & req.params.mode variables, they are different than what is passed in through the URL.
Allow me to show you...
Here is the Express code:
router.get('/verify/:user/:mode/:code', function(req,res){
console.log("STARTING VERIFICATION");
var code = req.params.code;
console.log('code: ' + code);
var user = req.params.user;
console.log('user: ' + user);
var mode = req.params.mode;
console.log('mode: ' + mode);
console.log('req.params: ' + JSON.stringify(req.params));
var regex = new RegExp(["^", req.params.user, "$"].join(""), "i");
console.log('REGEX: ' + regex);
var verified = false;
console.log('req.params: ' + req.params);
console.log('req.body: ' + req.body);
console.log("rx: "+ regex);
console.log('req.params.code: ' + req.params.code);
console.log('req.params.user: ' + req.params.user);
etc... etc... etc...
Here is the output in the console:
STARTING VERIFICATION
code: background-cycler.js
user: admin
mode: js
req.params: {"user":"admin","mode":"js","code":"background-cycler.js"}
REGEX: /^admin$/i
req.params: [object Object]
req.body: [object Object]
rx: /^admin$/i
req.params.code: background-cycler.js
req.params.user: admin
Here is the URL that is passed into the browser:
https://examplesite.com/verify/admin/sms/9484
I want to say that this code worked prior to dusting it off and moving an instance to google's cloud compute...
As you can see, the parameters passed in to the verify, code should be 9484 and mode should be sms. Instead i'm getting an unintended js filename, and a js mode instead.
UPDATE: As requested I added this within the Express route function:
console.log(req.originalUrl);
and I get this result:
/verify/admin/js/background-cycler.js
I can verify the URL that sent this was:
https://examplesite.com/verify/admin/sms/9484

Paypal Php Sdk - NotifyUrl is not a fully qualified URL Error

I have this code
$product_info = array();
if(isset($cms['site']['url_data']['product_id'])){
$product_info = $cms['class']['product']->get($cms['site']['url_data']['product_id']);
}
if(!isset($product_info['id'])){
/*
echo 'No product info.';
exit();
*/
header_url(SITE_URL.'?subpage=user_subscription#xl_xr_page_my%20account');
}
$fee = $product_info['yearly_price_end'] / 100 * $product_info['fee'];
$yearly_price_end = $product_info['yearly_price_end'] + $fee;
$fee = ($product_info['setup_price_end'] / 100) * $product_info['fee'];
$setup_price_end = $product_info['setup_price_end'] + $fee;
if(isset($_SESSION['discountcode_amount'])){
$setup_price_end = $setup_price_end - $_SESSION['discountcode_amount'];
unset($_SESSION['discountcode_amount']);
}
$error = false;
$plan_id = '';
$approvalUrl = '';
$ReturnUrl = SITE_URL.'payment/?payment_type=paypal&payment_page=process_agreement';
$CancelUrl = SITE_URL.'payment/?payment_type=paypal&payment_page=cancel_agreement';
$now = $cms['date'];
$now->modify('+5 minutes');
$apiContext = new \PayPal\Rest\ApiContext(
new \PayPal\Auth\OAuthTokenCredential(
$cms['options']['plugin_paypal_clientid'], // ClientID
$cms['options']['plugin_paypal_clientsecret'] // ClientSecret
)
);
use PayPal\Api\ChargeModel;
use PayPal\Api\Currency;
use PayPal\Api\MerchantPreferences;
use PayPal\Api\PaymentDefinition;
use PayPal\Api\Plan;
use PayPal\Api\Patch;
use PayPal\Api\PatchRequest;
use PayPal\Common\PayPalModel;
use PayPal\Api\Agreement;
use PayPal\Api\Payer;
use PayPal\Api\ShippingAddress;
// Create a new instance of Plan object
$plan = new Plan();
// # Basic Information
// Fill up the basic information that is required for the plan
$plan->setName($product_info['name'])
->setDescription($product_info['desc_text'])
->setType('fixed');
// # Payment definitions for this billing plan.
$paymentDefinition = new PaymentDefinition();
// The possible values for such setters are mentioned in the setter method documentation.
// Just open the class file. e.g. lib/PayPal/Api/PaymentDefinition.php and look for setFrequency method.
// You should be able to see the acceptable values in the comments.
$setFrequency = 'Year';
//$setFrequency = 'Day';
$paymentDefinition->setName('Regular Payments')
->setType('REGULAR')
->setFrequency($setFrequency)
->setFrequencyInterval("1")
->setCycles("999")
->setAmount(new Currency(array('value' => $yearly_price_end, 'currency' => $cms['session']['client']['currency']['iso_code'])));
// Charge Models
$chargeModel = new ChargeModel();
$chargeModel->setType('SHIPPING')
->setAmount(new Currency(array('value' => 0, 'currency' => $cms['session']['client']['currency']['iso_code'])));
$paymentDefinition->setChargeModels(array($chargeModel));
$merchantPreferences = new MerchantPreferences();
// ReturnURL and CancelURL are not required and used when creating billing agreement with payment_method as "credit_card".
// However, it is generally a good idea to set these values, in case you plan to create billing agreements which accepts "paypal" as payment_method.
// This will keep your plan compatible with both the possible scenarios on how it is being used in agreement.
$merchantPreferences->setReturnUrl($ReturnUrl)
->setCancelUrl($CancelUrl)
->setAutoBillAmount("yes")
->setInitialFailAmountAction("CONTINUE")
->setMaxFailAttempts("0")
->setSetupFee(new Currency(array('value' => $setup_price_end, 'currency' => $cms['session']['client']['currency']['iso_code'])));
$plan->setPaymentDefinitions(array($paymentDefinition));
$plan->setMerchantPreferences($merchantPreferences);
// ### Create Plan
try {
$output = $plan->create($apiContext);
} catch (Exception $ex){
die($ex);
}
echo $output->getId().'<br />';
echo $output.'<br />';
Been working with paypal php sdk for some days now and my code stop working.
So i went back to basic and i am still getting the same damn error.
I am trying to create a plan for subscription but getting the following error:
"NotifyUrl is not a fully qualified URL"
I have no idea how to fix this as i dont use NotfifyUrl in my code?
Could be really nice if anyone had an idea how to fix this problem :)
Thanks
PayPal did a update to their API last night which has caused problem within their SDK.
They are sending back null values in their responses.
I MUST stress the error is not on sending the request to PayPal, but on processing their response.
BUG Report : https://github.com/paypal/PayPal-PHP-SDK/issues/1151
Pull Request : https://github.com/paypal/PayPal-PHP-SDK/pull/1152
Hope this helps, but their current SDK is throwing exceptions.
Use below simple fix.
Replace below function in vendor\paypal\rest-api-sdk-php\lib\PayPal\Api\MerchantPreferences.php
public function setNotifyUrl($notify_url)
{
if(!empty($notify_url)){
UrlValidator::validate($notify_url, "NotifyUrl");
}
$this->notify_url = $notify_url;
return $this;
}
If you get the same error for return_url/cancel_url, add the if condition as above.
Note: This is not a permanent solution, you can use this until getting the update from PayPal.
From the GitHub repo for the PayPal PHP SDK, I see that the error you mentioned is thrown when MerchantPreferences is not given a valid NotifyUrl. I see you're setting the CancelUrl and ReturnUrl, but not the NotifyUrl. You may simply need to set that as well, i.e.:
$NotifyUrl = (some url goes here)
$obj->setNotifyUrl($NotifyUrl);
Reason behind it!
error comes from.
vendor\paypal\rest-api-sdk-php\lib\PayPal\Validation\UrlValidator.php
line.
if (filter_var($url, FILTER_VALIDATE_URL) === false) {
throw new \InvalidArgumentException("$urlName is not a fully qualified URL");
}
FILTER_VALIDATE_URL: according to this php function.
INVALID URL: "http://cat_n.domain.net.in/"; // IT CONTAIN _ UNDERSCORE.
VALID URL: "http://cat-n.domain.net.in/"; it separated with - dash
here you can dump your url.
vendor\paypal\rest-api-sdk-php\lib\PayPal\Validation\UrlValidator.php
public static function validate($url, $urlName = null)
{
var_dump($url);
}
And then check this here: https://www.w3schools.com/PHP/phptryit.asp?filename=tryphp_func_validate_url
you can check here what character will reason for invalid.

Pentaho - upload file using API

I need to upload a file using an API.
I tried REST CLIENT and didn't find any options.
Tried with HTTP POST and that responded with 415.
Please suggest how to accomplish this
Error 415 is “Unsupported media type”.
You may need to change the media type of the request or check whether that type of file us accepted by the remote server.
https://en.m.wikipedia.org/wiki/List_of_HTTP_status_codes
This solution uses only standard classes of jre 7. Add a step Modified Java Script Value in your transformation. You will have to add two columns in the flow: URL_FORM_POST_MULTIPART_COLUMN and FILE_URL_COLUMN, you can add as many files as you want, you will just have to call outputStreamToRequestBody.write more times.
try
{
//in this step you will need to add two columns from the previous flow -> URL_FORM_POST_MULTIPART_COLUMN, FILE_URL_COLUMN
var serverUrl = new java.net.URL(URL_FORM_POST_MULTIPART_COLUMN);
var boundaryString = "999aaa000zzz09za";
var openBoundary = java.lang.String.format("\n\n--%s\nContent-Disposition: form-data\nContent-Type: text/xml\n\n" , boundaryString);
var closeBoundary = java.lang.String.format("\n\n--%s--\n", boundaryString);
// var netIPSocketAddress = java.net.InetSocketAddress("127.0.0.1", 8888);
// var proxy = java.net.Proxy(java.net.Proxy.Type.HTTP , netIPSocketAddress);
// var urlConnection = serverUrl.openConnection(proxy);
var urlConnection = serverUrl.openConnection();
urlConnection.setDoOutput(true); // Indicate that we want to write to the HTTP request body
urlConnection.setRequestMethod("POST");
//urlConnection.addRequestProperty("Authorization", "Basic " + Authorization);
urlConnection.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundaryString);
var outputStreamToRequestBody = urlConnection.getOutputStream();
outputStreamToRequestBody.write(openBoundary.getBytes(java.nio.charset.StandardCharsets.UTF_8));
outputStreamToRequestBody.write(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(FILE_URL_COLUMN)));
outputStreamToRequestBody.write(closeBoundary.getBytes(java.nio.charset.StandardCharsets.UTF_8));
outputStreamToRequestBody.flush();
var httpResponseReader = new java.io.BufferedReader(new java.io.InputStreamReader(urlConnection.getInputStream()));
var lineRead = "";
var finalText = "";
while((lineRead = httpResponseReader.readLine()) != null) {
finalText += lineRead;
}
var status = urlConnection.getResponseCode();
var result = finalText;
var time = new Date();
}
catch(e)
{
Alert(e);
}
I solved this by using the solution from http://www.dietz-solutions.com/2017/06/pentaho-data-integration-multi-part.html
Thanks Ben.
He's written a Java class for Multi-part Form submission. I extendd by adding a header for Authorization...