Robotframework POST multi object request - api

I have a multi object request which look like follow in postman
{ "affordability": {
"grossIncome": 100000,
"netIncome": 80000,
"capitalRequired": 25000,
"groceries": 200,
"utilities": 300,
"savings": 400,
"services": 500,
"transport": 600,
"support": 800,
"housing": 700,
"other": 900
},
"employment": {
"sector": "CentralGovernmentSocial",
"status": "Contract",
"startDate": "2010-08-01",
"endDate": "2030-08-01"
},
"declarations": {
"debtLiability": "None",
"pendingRetrenchment": "false",
"knownMedicalCondition": "false"
},
"bank": "ABSA"
}
I am doing some automation on our API's and this one got me. I have struggled for quite some time to get the request to look like the postman request. I have managed to get that right. Though I am not entirely sure if the method I went about to solve the problem is allowing for the POST request. Cause when I run the POST request I get the below error.
HTTPError: 400 Client Error: Bad Request for url: this is a url
This is what my code looks like in:
# &{baseURL}= this is a url
# ${offer}= /offer
# ${URL}= Catenate This is a url ${appID} /offer
# log to console ${URL}
# Create Session httpbin this is a url
&{affordability}= Create Dictionary grossIncome=60000 netIncome=30000 capitalRequired=25000 groceries=500 utilities=400 savings=300 services=200 transport=100 support=100 housing=500 other=100
${affordabilityobject}= Catenate {'affordability': ${affordability}
# log to console ${affordabilityobject}
&{employment}= Create Dictionary sector=CentralGovernmentSocial status=Contract startDate=2019-08-01 endDate=2023-08-01
${employmentobject}= Catenate 'employment': ${employment}
# log to console ${employmentobject}
&{declarations}= Create Dictionary debtLiability=None pendingRetrenchment=false knownMedicalCondition=false
${declarationsobject}= Catenate 'declarations': ${declarations}
# log to console ${declarationsobject}
# &{bank}= Create Dictionary bank=FirstNationalBank
${bank}= Catenate 'bank':'FirstNationalBank'}
log to console ${bank}
${fullrequest}= Catenate SEPARATOR=, ${affordabilityobject} ${employmentobject} ${declarationsobject} ${bank}
log to console ${fullrequest}
${resp}= Post this is a url/${appID}/offer json=${fullrequest}

Don't use Catenate for nested dicts,
You can use Create Dictionary like this :
Then, I'm not sure, but I think you need to transform the dict to json with :
${json_string}= evaluate json.dumps(${json}) json
&{baseURL}= this is a url
${offer}= /offer
${URL}= Catenate This is a url ${appID} /offer
log to console ${URL}
Create Session httpbin this is a url
&{affordability}= Create Dictionary grossIncome=60000 netIncome=30000 capitalRequired=25000 groceries=500 utilities=400 savings=300 services=200 transport=100 support=100 housing=500 other=100
&{employment}= Create Dictionary sector=CentralGovernmentSocial status=Contract startDate=2019-08-01 endDate=2023-08-01
&{declarations}= Create Dictionary debtLiability=None pendingRetrenchment=false knownMedicalCondition=false
&{fullrequest}= Create Dictionary affordability=${affordability} employment=${employment} declarations=${declarations} bank=FirstNationalBank
log to console ${fullrequest}
${fullrequestJson}= evaluate json.dumps(${fullrequest}) json
${resp}= Post this is a url/${appID}/offer json=${fullrequestJson}
A better way to do it, is to store your json object in a separate file like "myjsonToPost.json" and you load it from file like this :
*** Settings ***
Library JSONLibrary
*** Keywords ***
&{baseURL}= this is a url
${offer}= /offer
${URL}= Catenate This is a url ${appID} /offer
log to console ${URL}
Create Session httpbin this is a url
${fullrequest} Load Json From File file_name=${EXECDIR}/path/to/myjsonToPost.json
# This method, without Library JSONLibrary
# ${fullrequestJson} evaluate json.dumps(${fullrequest}) json
# Or this one, with JSONLibrary
${fullrequestJson} Convert String To Json ${fullrequest}
${resp} Post this is a url/${appID}/offer json=${fullrequestJson}

Post Request Create Digital Offer
Create Session httpbin this is a url
&{affordability}= Create Dictionary grossIncome=${60000} netIncome=${30000} capitalRequired=${25000} groceries=${500} utilities=${400} savings=${300} services=${200} transport=${100} support=${100} housing=${500} other=${100}
&{employment}= Create Dictionary sector=CentralGovernmentSocial status=Contract startDate=2019-08-01 endDate=2025-08-01
&{declarations}= Create Dictionary debtLiability=None pendingRetrenchment=false knownMedicalCondition=false
&{fullrequest}= Create Dictionary affordability=${affordability} employment=${employment} declarations=${declarations} bank=ABSA
${CreateOffer_response}= Post this is a url/${appID}/offer json=${fullrequest}
Should Be Equal As Strings ${CreateOffer_response.status_code} 201

Related

Karate: How to send variable to json file

I'm trying to create a dynamic request that should receive 2 parameters (username and password). To do so, I defined the below JSON file:
{
"grant_type": "password",
"client_id": "myClient",
"username": "#(username)",
"password": "#(password)"
}
To call my JSON, I set the below feature file where my intention is to define a variable for each parameter i want to send
Scenario: Get Sugar access token
Given url baseUrl
And path 'oauth2/token'
And def username = 'user'
And def password = 'password'
And def requestBody = read('classpath:jsonFiles/requests/authRequest.json')
When request requestBody
And method POST
Then status 200
* print 'Response: ', response
Unfortunately, when I run the scenario, i'm getting below error message
10:59:59.111 [main] INFO com.intuit.karate - Karate version: 1.4.0-SNAPSHOT
10:59:59.286 [main] INFO com.intuit.karate.Suite - backed up existing 'target\karate-reports' dir to: target\karate-reports_1672160399285
10:59:59.303 [main] INFO c.intuit.karate.core.FeatureRuntime - found scenario at line: 4
11:00:00.174 [main] ERROR com.intuit.karate - src/test/java/myCompany/testautomation/features/auth/getToken.feature:11
And def requestBody = read('classpath:jsonFiles/requests/authRequest.json')
js failed:
>>>>
01: read('classpath:jsonFiles/requests/authRequest.json')
<<<<
org.graalvm.polyglot.PolyglotException: not found: jsonFiles/requests/seugarAuth/authRequest.json
- com.intuit.karate.resource.ResourceUtils.getResource(ResourceUtils.java:126)
- com.intuit.karate.core.ScenarioFileReader.toResource(ScenarioFileReader.java:129)
- com.intuit.karate.core.ScenarioFileReader.readFileAsStream(ScenarioFileReader.java:99)
- com.intuit.karate.core.ScenarioFileReader.readFileAsString(ScenarioFileReader.java:95)
- com.intuit.karate.core.ScenarioFileReader.readFile(ScenarioFileReader.java:54)
- com.intuit.karate.core.ScenarioEngine.lambda$new$0(ScenarioEngine.java:120)
- <js>.:program(Unnamed:1)
src/test/java/myCompany/testautomation/features/auth/getToken.feature:11
The use of classpath: makes more sense for Java projects. It looks like you are using the Visual Studio code extension, in which case your workspace root folder will be the classpath unless to switch to "Maven mode" or equivalent: https://marketplace.visualstudio.com/items?itemName=karatelabs.karate#run-mode
Or another option is to use a Java "runner" and use the Java extension for VS Code.
If all that above doesn't make sense, for now just keep things simple and use relative paths. For example if authRequest.json is side-by-side with your feature file, this would work:
* def requestBody = read('authRequest.json')
Relative paths and the this: prefix can be used, but I leave that to you to research: https://github.com/karatelabs/karate#path-prefixes

Karate: JSON is converted to string ([object Object]) when matching

I'm trying to match the response of an API with a schema. I have the following setup.
Here is the feature file:
Feature: As a user, I would like to test APIs of member modules.
Background:
* def DIR_PATH = 'data/users/'
* def signup_request_helpers = read(<path_to_js_file>)
* def signup_response_helpers = read(<path_to_js_file>)
* def request_data = signup_request_data()
Scenario: Test signup of user
Given url my_url
And request request_data
When method POST
Then status 200
Then match response == signup_response_schema
Here is the JS file that holds the response schema
var signup_response_schema = {
"success": true,
"message": "Successfully signed up.",
"data": {
"user": user_schema,
"confirmation_url": "#string",
"token": "#string",
"role": "#array"
}
};
The problem is that karate seems to be converting the response schema into a string. This is the error message that I get:
actual: {<response>}, expected: [object Object]
Please note that I can match individual string fields fine. The problem only occurs when I try to match objects. Also, I have tried printing the schema and the variable seems to be holding the correct value which is the schema object.
I've tried the following:
Convert the schema into a string and then a JSON using the library's directives. This doesn't verify the schema
Convert directly to json which still renders the schema as [object Object]
NOTE: I'm running Karate on docker if that makes any difference. Following is the command that I use in my docker-compose file:
java -jar -Dkarate.config.dir=/app /app/karate-0.9.1.jar -T 5 path_to_file
JS in Karate is not that seamless. This will work:
var temp = {
"success": true,
"message": "Successfully signed up.",
"data": {
"user": user_schema,
"confirmation_url": "#string",
"token": "#string",
"role": "#array"
}
};
karate.set('signup_response_schema', temp);
Normally in Karate *.js files start with function and contain a single function block. And the result of reading (evaluating) that block is assigned to a Karate variable using def. But the recommended option for you is this, where you just have a JSON in the file, and the extension matters:
Then match response == read('signup_response_schema.json')
Note that read() will also evaluate embedded expressions, which can be useful. Although the JSON is not expected to be strictly well-formed, it is recommended.
The issue here wasn't of JS evaluation. The variable signup_response_schema holds the value that I assigned to it in the JS file.
The problem was with the actual matching of the schema because one of the keys was missing in the actual response. Karate usually throws specific error messages indicating which keys were missing but for some reason, this isn't the case here.

In Angular PWA How to Cache the API request( with paramter ) dynamically instead of manually adding each and every url in ngsw-config.json

I have some API like https://api-******y?apikey=sX4&q=london, Where q=london is a parameter that will keep changing, So how can i add this URL in the list of urls in ngsw-config.json file
"dataGroups": [
{
"name": "api-performance",
"urls": [
"https://api-***************y?apikey=sX******4&q=london",
"https://api-***************y?apikey=sX******4&q=manchester",
"https://api-***************y?apikey=sX******4&q=birmingham"
],
"cacheConfig": {
"strategy": "performance",
"maxSize": 100,
"maxAge": "3d"
}
},
I have two doubts:
Instead of adding same url again and again for all different parameters like london/manchester/birmingham is there a way that i can just add the base URL without parameter and it will store the responses for all different parameters when user will search for it?
Instead of adding the URL's manually, the URL's response should be stored dynamically when the user has searched for it when he was online. I mean i will initially not add any URL in list in the ngsw-config.json file, BUT when user has requested for that url, its response automatically get stored in cache.
I got this one, Its actually a little stupid of me to think that i have to give the complete url in the ngsw-config.json file,
Actually we just can simply give the base url, we neither have to give the apiKey parameter nor any other parameter like london/manchester/birmingham, ngsw.json file in dist folder after the build, stores the url as patterns, it just matches the base url and stores all the queries with all different parameters made to that url by user. above code can re-written like below. it will work fine by caching the data for all the stores london/manchester/birmingham, whenever user searches for them.
"dataGroups": [
{
"name": "api-performance",
"urls": [
"https://api-***************y
],
"cacheConfig": {
"strategy": "performance",
"maxSize": 100,
"maxAge": "3d"
}
},

apache nutch to index to solr via REST

newbie in apache nutch - writing a client to use it via REST.
succeed in all the steps (INJECT, FETCH...) - in the last step - when trying to index to solr - it fails to pass the parameter.
The Request (I formatted it in some website)
{
"args": {
"batch": "1463743197862",
"crawlId": "sample-crawl-01",
"solr.server.url": "http:\/\/x.x.x.x:8081\/solr\/"
},
"confId": "default",
"type": "INDEX",
"crawlId": "sample-crawl-01"
}
The Nutch logs:
java.lang.Exception: java.lang.RuntimeException: Missing SOLR URL. Should be set via -D solr.server.url
SOLRIndexWriter
solr.server.url : URL of the SOLR instance (mandatory)
solr.commit.size : buffer size when sending to SOLR (default 1000)
solr.mapping.file : name of the mapping file for fields (default solrindex-mapping.xml)
solr.auth : use authentication (default false)
solr.auth.username : username for authentication
solr.auth.password : password for authentication
at org.apache.hadoop.mapred.LocalJobRunner$Job.runTasks(LocalJobRunner.java:462)
at org.apache.hadoop.mapred.LocalJobRunner$Job.run(LocalJobRunner.java:522)
Was that implemented? the param passing to solr plugin?
You need to create/update a configuration using the /config/create/ endpoint, with a POST request and a payload similar to:
{
"configId":"solr-config",
"force":"true",
"params":{"solr.server.url":"http://127.0.0.1:8983/solr/"}
}
In this case I'm creating a new configuration and specifying the solr.server.url parameter. You can verify this is working with a GET request to /config/solr-config (solr-config is the previously specified configId), the output should contain all the default parameters see https://gist.github.com/jorgelbg/689b1d66d116fa55a1ee14d7193d71b4 for an example/default output. If everything worked fine in the returned JSON you should see the solr.server.url option with the desired value https://gist.github.com/jorgelbg/689b1d66d116fa55a1ee14d7193d71b4#file-nutch-solr-config-json-L464.
After this just hit the /job/create endpoint to create a new INDEX Job, the payload should be something like:
{
"type":"INDEX",
"confId":"solr-config",
"crawlId":"crawl01",
"args": {}
}
The idea is that need to you pass the configId that you created with the solr.server.url specified along with the crawlId and other args. This should return something similar to:
{
"id": "crawl01-solr-config-INDEX-1252914231",
"type": "INDEX",
"confId": "solr-config",
"args": {},
"result": null,
"state": "RUNNING",
"msg": "OK",
"crawlId": "crawl01"
}
Bottom line you need to create a new configuration with the solr.server.url setted instead of specifying it through the args key in the JSON payload.

HTTP Adapter Error "Runtime: Failed to parse JSON string"

I am using IBM Worklight Studio, and trying to create HTTP Adapter which retrieve JSON object from external http server.
When I just access target http server with HTTP Get access(with browser, for example), I know their response is like following array style JSON format:
[
{ "xxx":"aaa", "yyy":"bbb", ... },
{ "xxx":"ccc", "yyy":"ddd", ... },
:
{ "xxx":"eee", "yyy":"fff", ... }
]
And I had created HTTP Adapter which would retrieve above information
var input = {
method : 'get',
returnedContentType : 'json',
path : path
};
return WL.Server.invokeHttp(input);
Now I tried to invoke this adapter with "Run As -> Invoke Worklight Procedure", then I got this error message:
{
"errors": [
"Runtime: Failed to parse JSON string\n\n[\n {\n
(raw JSON data) } ],
"info": [],
"isSuccessful": false,
"warnings": []
}
And in my log console, worklight says following error messages:
FWLSE0101E: Caused by: java.io.IOException: Expecting '{' on line 2, column 2 instead, obtained token: 'Token: ['
From above information, it seems that worklight would expect that returned JSON object need to start with "{", not "[".
Is this my guess right? Are there any workaround for this?
Thanks for advance.
Worklight knows how to handle JSON objects that start with [ (JSON arrays). In such case Worklight will return the response as:
{ "array" : [*the json array*]}
Looking at the code of the HTTP Adapter, I see that there is a bug with parsing JSON arrays that do not start with [.
I do not see a workaround for this problem, except changing the response returned from the http server.
I opened an internal bug about this, thank you for helping us find this bug.
You can change returnedContentType to "plain", this will make WL server return content as a big string and not attempt to parse it.
Then in your adapter you can use var obj = JSON.parse(response.text)