Karate - Trouble passing correct headers for authorization - karate

I am have some problems passing in the correct headers for my graphql endpoints
The use case in Postman:
call requestToken endpoint to obtain sessionToken value
requestToken response contains Key Value " and Token Value.
For subsequent calls, I set postman headers as:
Key = X_SESSION_TOKEN Value = Token Value
The user case in Karate
1st feature 'requestToken.feature' successfully calls and stores key + tokenValue
2nd feature successfully defines and prints the token value
here is my 2nd request:
Feature: version
Background:
* url 'http://api-dev.markq.com:5000/'
* def myFeature = call read('requestToken.feature')
* def authToken = myFeature.sessionToken
* configure headers = { 'X_SESSION_TOKEN': authToken , 'Content-Type': 'application/json' }
Scenario: get version
Given path 'query'
Given text query =
"""
query {
version
}
"""
And request { query: '#(query)' }
When method POST
Then status 200
And print authToken
And print response
I am not sure I send the headers right. Its coming back 200, but I keep getting a error 'token malformed' in the response message
Any suggestions? New at this, thanks!

Honestly this is hard to answer, a LOT depends on the specific server.
EDIT: most likely it is this change needed, explained here: https://github.com/intuit/karate#embedded-expressions
* configure headers = { 'X_SESSION_TOKEN': '#(authToken)' , 'Content-Type': 'application/json' }
2 things from experience:
should it be X-SESSION-TOKEN
add an Accept: 'application/json' header
And try to hardcode the headers before attempting call etc.
Here is an example that works for me:
* url 'https://graphqlzero.almansi.me/api'
* text query =
"""
{
user(id: 1) {
posts {
data {
id
title
}
}
}
}
"""
* request { query: '#(query)' }
* method post
* status 200

Related

Getting Illegal character in query when doing a conditional GET with multiple parameters

Code:
Feature: GET API headers feature
Scenario: pass GetWatchList with headers
Given header x-apisignatures = '543aba07839'
And header ssotoken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ1bmlxdWUiOiJjYTM4MDAzZS0wYThiLTQ3YjktOWFjNS00YzMyN2MwNTUyMGYiLCJ1c2VyVHlwZSI6IlJJTHBlcnNvbiIsImF1dGhMZXZlbCI6IjIwIiwiZGV2aWNlSWQiOiJlZWIxOGRlOGRiNjE3MDg4MjViNTdjNGU5NDg1ZmFjYTU1MGE1OTBkY2Y1MjIzNzkyYzUwYjVhZWRjZGI5ZmUxNDQxZTFiMzhmZWI2NDFhZmUwNGI3NGY0NDA5OWMzZGQwZWI0OTRiZjgwMTYxOWYyNTAyNjI2YTJjZjdkMTZmZCIsImp0aSI6IjFmN2IwODkxLTJkMGUtNDBhZS04MWJiLWQwODVjY2NhOGYyZSIsImlhdCI6MTY2NDI3Njk0NH0.6Q7CPtQIN0uB1ZbVQBj5dshqioop3dJKEpla5DQS5K5qtRPw38SxTEJ1f1DJ_Ka_sgMp_fdh9EEABipTgtgMqg'
And header uniqueid = 'ca38003e-0a8b-47b9-9ac5-4c327c05520f'
And header x-page = 'Home'
When url 'https://jiocinemaqa-api.jio.ril.com/user/v1/watchlist?groups=[["Movie"],["Show"]]'
When method GET
Then status 200
* print response
Try this public API to test:
* url 'https://httpbin.org/anything'
* param groups = '[["Movie"],["Show"]]'
* method get
And you can verify in the response you see this:
"args": {
"groups": "[[\"Movie\"],[\"Show\"]]"
},
And also:
"url": "https://httpbin.org/anything?groups=[[\"Movie\"]%2C[\"Show\"]]"
This proves that Karate is sending the right thing. Keep in mind that your server may have a bug.

Karate Authentication only valid for the first request in scenario

My issue is that I am authorized for the first request (Create article) but not for the 2nd request (getById) although the authorization itself has not changed. What am I doing wrong?
Feature: Test Article Endpoint
Background:
* url 'http://localhost:8080/webapp/api/v1'
* header AuthenticationToken = 'sys-test-api-token'
Scenario: create article, get article ById, update article & delete article
And request {name: 'TestArtikel', unitName: 'Stk.', articleNumber: '0001'}
Given path 'article'
When method post
Then status 201
And match response.id == '#notnull'
* def articleId = response.id
Given path 'article/id/:id'
And param id = articleId
When method get
Then status 200
For headers that "span" requests, use configure headers:
* configure headers = { AuthenticationToken: 'sys-test-api-token' }
And refer the docs: https://github.com/intuit/karate#configure-headers

How to navigate and validate through all the pages of a api response

I have a scenario where the api returns payload response in pages if the payload has lot of data.
Request:
Background:
* url url
* call read('classpath:examples/common.feature')
And header accesstoken = accessToken
And header accept = '*/*'
And header Accept-Encoding = 'gzip, deflate, br'
Scenario: Get Scores
* param start = '2020-07-01'
Given path '/scores'
When method Get
Then status 200
* def totalPages = response.totalPages
* def response = {"requestId": "6a4287f35112",
"timestampMs": 1595228005245,
"totalMs": 51,
"page": 1,
"totalPages": 100,
"data": [.......]}
After this i am getting total pages, and need to navigate through all the pages by passing the same request with additional * param page = #page_number and validate response is 200. page_number has to be iterated from 2 to 100.
Thought of using Karate loop or calling feature file and building dynamic data and using dynamic data driven feature, but not sure how to proceed.
Please advise
I think the easiest option is to write a second feature file and call it in a loop.
* def totalPages = 10
* def pages = karate.repeat(totalPages, function(i){ return { page: i } })
* call read('second.feature') pages

Karate doesn't recognize dashes

I wrote a simple mock that checks if a specific header exists then return a specif response based on that, but karate doesn't understand dashes(-) in my headers for an example Client-ID gives an error of ReferenceError: "ID" is not defined in <eval> at line number 1 but header Accept work fine. i'm passing this header through postman.
and this how the code looks
* def fun = function(){ var test = requestHeaders; for(i in test) if(test.Client-ID) return true}
When you have characters like - part of the JSON key, you need to use quotes.
* def foo = { 'Content-Type': 'application/json' }
* match foo['Content-Type'] == 'application/json'
Also try if this works for you, it may be simpler:
Scenario: pathMatches('/v1/headers') && karate.get("requestHeaders['Client-ID']")
And in case you are testing a value, headerContains() can be used: https://github.com/intuit/karate/tree/master/karate-netty#headercontains

Why do I get HTTP 401 Unauthorized from my call the to Yahoo contacts API?

This is driving me crackers. I'm implementing a friend invite scheme on a website and need access to the user's Yahoo contacts list. To do this, I'm using OAuth and the yahoo REST api. Here's a complete rundown of the sequence of events:
I have a project set up on developers.yahoo.com which is configured to have read access to Contacts. It's on a made-up domain which I point to 127.0.0.1 in my hosts file (On the off-chance that localhost was causing my woes). For this reason, the domain is not verified though my understanding is that this simply means I have less restrictions, not more.
Firstly, on the server I get a request token:
https://api.login.yahoo.com/oauth/v2/get_request_token
?oauth_callback=http%3A%2F%2Fdev.mysite.com%2Fcallback.aspx
&oauth_consumer_key=MYCONSUMERKEY--
&oauth_nonce=xmaf8ol87uxwkxij
&oauth_signature=WyWWIsjN1ANeiRpZxa73XBqZ2tQ%3D
&oauth_signature_method=HMAC-SHA1
&oauth_timestamp=1328796736
&oauth_version=1.0
Which returns with (Formatted for vague attempt at clarity):
oauth_token=hxcsqgj
&oauth_token_secret=18d01302348049830942830942630be6bee5
&oauth_expires_in=3600
&xoauth_request_auth_url
=https%3A%2F%2Fapi.login.yahoo.com%2Foauth%2Fv2%2Frequest_auth
%3Foauth_token%3Dhxcsqgj
&oauth_callback_confirmed=true"
I then pop-up the xoauth_request_auth_url page to the user and receive a verifier code to my callback page. I then send that back to my server so that I can exchange it for an access token:
https://api.login.yahoo.com/oauth/v2/get_token
?oauth_consumer_key=MYCONSUMERKEY--
&oauth_nonce=yxhd1nymwd03x189
&oauth_signature=c%2F6GTcybGJSQi4TOpvueLUO%2Fgrs%3D
&oauth_signature_method=HMAC-SHA1
&oauth_timestamp=1328796878
&oauth_token=hxcqgjs
&oauth_verifier=b8ngvp <- verifier given via callback
&oauth_version=1.0
That seems to work, and I get an access token back:
oauth_token=MYVERYLONGACCESSTOKEN--
&oauth_token_secret=MYOATHTOKENSECRET
&oauth_expires_in=3600
&oauth_session_handle=ADuXM093mTB4bgJPKby2lWeKvzrabvCrmjuAfrmA6mh5lEZUIin6
&oauth_authorization_expires_in=818686769
&xoauth_yahoo_guid=MYYAHOOGUID
I then immediately attempt to get the contacts list with the access token and the GUID:
http://social.yahooapis.com/v1/user/MYYAHOOGUID/contacts
(HTTP Header added and formatted with line breaks for clarity...)
Authorization: OAuth
realm="yahooapis.com",
oauth_consumer_key="MYCONSUMERKEY--",
oauth_nonce="nzffzj5v82mgf4mx",
oauth_signature="moVJywesuGaPN5YHYKqra4T2ips%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1328796907",
oauth_token="MYVERYLONGACCESSTOKEN--",
oauth_version="1.0"
From this call I get a 401 Unauthorized, but it seems impossible to find out why. To sign these calls, I'm using this oath lib on github. I don't think it's doing anything extraordinary or incompatable. For the signature, I'm including the consumer key/secret and the access token/secret. I've looked at the signature base that's being hashed and it looks to be the same form as the examples visible on yahoo's documentation. I'm guessing that I'm missing something from the parameters that isn't being hashed. Is there a way to find out why the call is unauthorized, or does anyone know of an example showing exactly what form the signature base and authorization header must take?
Solved this myself. Adding the answer just in case it happens to help anyone who makes the same silly mistake I did. When I made the API call, I was using the token secret returned from the original request token call instead of the new one returned from the access token call.
Oops.
this is the code with which I solved, the trusted code to use if yahooapis returns 403 forbidden:
Reference:
https://developer.yahoo.com/yql/guide/yql-code-examples.html#yql_php
https://github.com/danzisi/YQLQueryYahooapis
init CODE
/**
* Call the Yahoo Contact API
*
* https://developer.yahoo.com/yql/guide/yql-code-examples.html#yql_php
*
* #param string $consumer_key obtained when you registered your app
* #param string $consumer_secret obtained when you registered your app
* #param string $guid obtained from getacctok
* #param string $access_token obtained from getacctok
* #param string $access_token_secret obtained from getacctok
* #param bool $usePost use HTTP POST instead of GET
* #param bool $passOAuthInHeader pass the OAuth credentials in HTTP header
* #return response string with token or empty array on error
*/
function call_yql($consumer_key, $consumer_secret, $querynum, $access_token, $access_token_secret, $oauth_session_handle, $usePost=false, $passOAuthInHeader = true){
global $godebug;
$response = array();
if ($consumer_key=='' || $consumer_secret=='' || $querynum=='' || $access_token=='' || $access_token_secret=='' || $oauth_session_handle) return array('0' => 'Forbidden');
if ($querynum == 1) {
$url = 'https://query.yahooapis.com/v1/yql';
// Show my profile
$params['q'] = 'select * from social.profile where guid=me';
} elseif ($querynum == 2) {
$url = 'https://query.yahooapis.com/v1/yql';
// here other query
}
$params['format'] = 'json'; //json xml
$params['Authorization'] = 'OAuth';
$params['oauth_session_handle'] = $oauth_session_handle;
$params['realm'] = 'yahooapis.com';
$params['callback'] = 'cbfunc';
$params['oauth_version'] = '1.0';
$params['oauth_nonce'] = mt_rand();
$params['oauth_timestamp'] = time();
$params['oauth_consumer_key'] = $consumer_key;
$params['oauth_callback'] = 'oob';
$params['oauth_token'] = $access_token;
$params['oauth_signature_method'] = 'HMAC-SHA1';
$params['oauth_signature'] = oauth_compute_hmac_sig($usePost? 'POST' : 'GET', $url, $params, $consumer_secret, $access_token_secret);
if ($passOAuthInHeader) {
$query_parameter_string = oauth_http_build_query($params, true);
$header = build_oauth_header($params, "yahooapis.com");
$headers[] = $header;
} else {
$query_parameter_string = oauth_http_build_query($params);
}
// POST or GET the request
if ($usePost) {
$request_url = $url;
logit("call_yql:INFO:request_url:$request_url");
logit("call_yql:INFO:post_body:$query_parameter_string");
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
$response = do_post($request_url, $query_parameter_string, 443, $headers);
} else {
$request_url = $url . ($query_parameter_string ? ('?' . $query_parameter_string) : '' );
logit("call_yql:INFO:request_url:$request_url");
$response = do_get($request_url, 443, $headers);
}
// extract successful response
if (! empty($response)) {
list($info, $header, $body) = $response;
if ($godebug==true) {
echo "<p>Debug: function call_yql info: <pre>" . print_r($info, TRUE) . "</pre></p>";
echo "<p>Debug: function call_yql header: <pre>" . print_r($header, TRUE) . "</pre></p>";
echo "<p>Debug: function call_yql body: <pre>" . print_r($body, TRUE) . "</pre></p>";
}
if ($body) {
$body = GetBetween($body, 'cbfunc(', ')');
$full_array_body = json_decode($body);
logit("call_yql:INFO:response:");
if ($godebug==true) echo "<p>Debug: function call_yql full_array_body: <pre>" . print_r($full_array_body, TRUE) . "</pre></p>";
}
}
// return object
return $full_array_body->query;
}
END code