How to extract a specific value from Json response and use this value in the input payload json file in Rest Assured Automation Testing - api

I am not using any POJO Classes. Instead i am using External json file as payload file for 2 of my API's (Get API and Delete API).
Add API >> Adds the book name, book shelf, other book details along with unique place_id.
Delete API >> Used to delete a book from specific rack using the unique place_id from above.
Since I am using external json payload input file, please let me know the way to pass the "place_id" grabbed from GET API and send this place_id to DELETE API external json file and then use it
Add Place API below: This API returns unique Place_ID in the response
{
"location": {
"lat": -38.383494,
"lng": 33.427362
},
"accuracy": 50,
"name": "Frontline house 1",
"phone_number": "(+91) 983 893 3937",
"address": "2951, side layout, cohen 09",
"types": [
"shoe park",
"shop"
],
"website": "http://google.com",
"language": "French-IN"
}
Delete Place API below:
{
"place_id": "<need to insert place_id from above response>"
}

You can do this using json-simple library in java. json-simple maven repository
imports
import io.restassured.RestAssured;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
You have to get to the following;
Retrieve place_id from Add Place API json response
Read request body of Delete Place API from json file
Replace the place_id value in request body of Delete Place API
Write the new request body to Delete Place API json file
// Retrieve place_id from Add Place API json response
Response response = RestAssured
.when()
.get("add_place_api_url")
.then()
.extract()
.response();
JsonPath jsonPath = response.jsonPath();
String placeId = jsonPath.get("place_id");
// Read request body of Delete Place API from json file
JSONObject jsonObject = new JSONObject();
JSONParser parser = new JSONParser();
try {
Object obj = parser.parse(new FileReader("path/DeletePlaceRequestBody.json"));
jsonObject = (JSONObject) obj;
// Replace the place_id value in request body of Delete Place API
jsonObject.replace("place_id", placeId);
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
// Write the new request body to Delete Place API json file
try {
FileWriter file = new FileWriter("DeletePlaceRequestBody.json");
file.write(jsonObject.toString());
file.flush();
file.close();
} catch (IOException e) {
e.printStackTrace();
}

Related

Upload Multiple Files With Ktor

I am Using ktor with kmm to upload a list of images to server
But there is no available guide in the docs to make me upload list of files
i am converting my files to byte array and uploading them
I tried to upload it this way
mainClient.post {
setBody(
MultiPartFormDataContent(
formData {
append("attachments[]", listOf(toBytes(kmmFile),toBytes(kmmFile)) )
}
)
)
}
but got connection refused
You can iterate through all byte arrays in a collection and call the append method for each of them. Here is an example:
val images: List<ByteArray> // assigned externally
val response = client.post("https://httpbin.org/post") {
setBody(MultiPartFormDataContent(
formData {
for (bytes in images) {
append("attachments[]", bytes)
}
}
))
}
I use below code for uploading single file and run forEach when call this method(for each n append doesn't work for me). I think your serve must be supported upload multiple file at same time.
override suspend fun upload(
uploadFiles: Map<String, File>,
texts: Map<String, String>
): ResultWrapper<ResponseData<List<UploadFileDto>>> {
return {
httpClient.submitForm {
url(BASE_URL + "api/v1/static/upload-file")
method = HttpMethod.Post
setBody(MultiPartFormDataContent(
formData {
headers {
append(
"document",
uploadFiles.entries.first().value.readBytes(),
Headers.build {
append(
HttpHeaders.ContentDisposition,
"filename=${uploadFiles.entries.first().value.name}"
)
})
}
}
))
}.body()
}

How to include metadata on a requests API query

I need to specify the page and offset to retrieve all the data from a CRM. On the API documentation it's specified:"All pagination related query parameters begin with page. List endpoints typically limit the number of entities returned. This is considered a “page” of results. There is a meta root property in the response, containing pagination metadata;
{
"deals": [...],
"meta": {
"page": {
"pageOffset": 0,
"pageSize": 20,
"count": 11394
}
}
}
". But I don't know where to palce this infomation. I tried this:
import requests
headers = {
'Authorization': 'Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxx',
}
params = {
'pageSize':'40',
'pageOffset':'2'
}
deals = requests.get('https://baud.teamwork.com/crm/api/v2/deals.json', headers=headers, params=params).json()
But he metadata "pageSize" and "pageOffset" are not properly specified

GraphQL stitch and union

I have a need to 'aggregate' multiple graphQl services (with same schema) into single read-only (query only) service exposing data from all services. For example:
---- domain 1 ----
"posts": [
{
"title": "Domain 1 - First post",
"description": "Content of the first post"
},
{
"title": "Domain 1 - Second post",
"description": "Content of the second post"
}
]
---- domain 2 ----
"posts": [
{
"title": "Domain 2 - First post",
"description": "Content of the first post"
},
{
"title": "Domain 2 - Second post",
"description": "Content of the second post"
}
]
I understand that 'stitching' is not meant for UC's like this but more to combine different micro-services into same API. In order to have same types (names) into single API, I implemented 'poor man namespaces' by on-the-fly' appending domain name to all data types. However, I'm able only to make a query with two different types like this:
query {
domain_1_posts {
title
description
}
domain_2_posts {
title
description
}
}
but, it results with data set consist out of two arrays:
{
"data": {
"domain_1_posts": [
{ ...},
],
"domain_2_posts": [
{ ...},
]
}
}
I would like to hear your ideas what I can do to combine it into single dataset containing only posts?
One idea is to add own resolver that can call actual resolvers and combine results into single array (if that is supported at all).
Also, as a plan B, I could live with sending 'domain' param to query and then construct query toward first or second domain (but, to keep initial query 'domain-agnostic', e.g. without using domain namses in query itself?
Thanks in advance for all suggestions...
I manage to find solution for my use-case so, I'll leave it here in case that anyone bump into this thread...
As already mentioned, stitching should be used to compose single endpoint from multiple API segments (microservices). In case that you try to stitch schemas containing same types or queries, your request will be 'routed' to pre-selected instance (so, only one).
As #xadm suggested, key for 'merging' data from multiple schemas into singe data set is in using custom fetch logic for Link used for remote schema, as explained:
1) Define custom fetch function matching your business needs (simplified example):
const customFetch = async (uri, options) => {
// do not merge introspection query results!!!
// for introspection query always use predefined (first?) instance
if( operationType === 'IntrospectionQuery'){
return fetch(services[0].uri, options);
}
// array fecth calls to different endpoints
const calls = [
fetch(services[0].uri, options),
fetch(services[1].uri, options),
fetch(services[2].uri, options),
...
];
// execute calls in parallel
const data = await Promise.all(fetchCalls);
// do whatever you need to merge data according to your needs
const retData = customBusinessLogic();
// return new response containing merged data
return new fetch.Response(JSON.stringify(retData),{ "status" : 200 });
}
2) Define link using custom fetch function. If you are using identical schemas you don't need to create links to each instance, just one should be enough.
const httpLink = new HttpLink(services[0].uri, fetch: customFetch });
3) Use Link to create remote executable schema:
const schema = await introspectSchema(httpLink );
return makeRemoteExecutableSchema({
schema,
link: httpLink,
context: ({ req }) => {
// inject http request headers into context if you need them
return {
headers: {
...req.headers,
}
}
},
})
4) If you want to forward http headers all the way to the fetch function, use apollo ContextLink:
// link for forwarding headers through context
const contextLink = setContext( (request, previousContext) => {
if( previousContext.graphqlContext ){
return {
headers: {
...previousContext.graphqlContext.headers
}
}
}
}).concat(http);
Just to mention, dependencies used for this one:
const { introspectSchema, makeRemoteExecutableSchema, ApolloServer } = require('apollo-server');
const fetch = require('node-fetch');
const { setContext } = require('apollo-link-context');
const { HttpLink } = require('apollo-link-http');
I hope that it will be helfull to someone...

How can I add a new tab to an existing sheet via the Google Sheets API?

I tried reading through the api docs but failed to find directions to perform queries regarding the existence of tabs and to create a tab if one does not exist.
Does anyone have a clue?
Apparently it is possible using a batchUpdate:
https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/batchUpdate
With one of the requests being of the form of:
https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddSheetRequest
For example, in order to add a new empty sheet with the name "FOO", one can send a batchUpdate like this one:
sheets.spreadsheets.batchUpdate(
{
auth: authClient,
spreadsheetId: spreadsheetId,
resource: {
requests: [
{
'addSheet':{
'properties':{
'title': 'FOO'
}
}
}
],
}
},
function(err, response) {
if (err) return callback('The API returned an error: ' + err);
console.log("success: ", response);
});
I got this to work using Python 3. I had been confused by the batchUpdate method used for values vs. spreadsheets, so once I corrected this, the sheet was created successfully. Here's the working snippet:
body = {
'requests': [{
'addSheet': {
'properties': {
'title': write_sheet_name,
'tabColor': {
'red': 0.44,
'green': 0.99,
'blue': 0.50
}
}
}
}]
}
result = service.spreadsheets().batchUpdate(
spreadsheetId=SPREADSHEET_ID,
body=body).execute()
See https://developers.google.com/sheets/api/guides/batchupdate
The above is not to be confused with batch update of values in sheet:
result = service.spreadsheets().values().batchUpdate(
spreadsheetId=SPREADSHEET_ID,
body=body).execute()
See https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values for batchUpdate of values.
If you are looking for how to do this in java, here's a function that does it.
/**
* Adds a new tab to an existing GoogleSheet document.
* #param service An authenticated GoogleSheet service
* #param sheetName The GoogleSheet name of the document
* #param tabName The name of the tab you wish to add to the GoogleSheet document
* #return The response from adding the sheet.
* #throws IOException Throws an IOException if anything goes wrong.
*/
public static BatchUpdateSpreadsheetResponse AddTabToGoogleSheet(Sheets service, String sheetName, String tabName)
throws IOException {
List<Request> requests = new ArrayList<>();
requests.add(new Request().setAddSheet(new AddSheetRequest().setProperties(new SheetProperties()
.setTitle(tabName))));
BatchUpdateSpreadsheetRequest body = new BatchUpdateSpreadsheetRequest().setRequests(requests);
return service.spreadsheets().batchUpdate(sheetName, body).execute();
}

API Automation - Is it possible to get response of the post call

I'm learning how to automate API with frisby.js on gmail.api.
I want to create a test where I create and delete(or send) a Draft message.
So I wrote a test which creates a Draft and my question is - can I write a code that gets at least ID of generated response from my Post call?
var frisby = require('frisby');
frisby.create('Create Draft Google')
.post('https://www.googleapis.com/gmail/v1/users/me/drafts?access_token=*my-token-here*', {
message: {
raw: "RGFuJ3MgVG9vbHMgYXJlIGNvb2wh",
id: "1547265285486966899"
}
}, { json: true })
.inspectJSON()
.inspectBody()
.expectStatus(200)
.toss();
So, to clarify, I want to write another part of THIS^ test with
.after(function(err, res, body){}
Steps:
I create a Draft message
I want my test to automatically get ID of just created Draft
So I could Delete it\Send it
Thanks!
When you create a draft, you will get the id of the newly created draft in the response:
Request
POST https://www.googleapis.com/gmail/v1/users/me/drafts?access_token={access_token}
{
"message": {
"raw": "RnJ..."
}
}
Response
{
"id": "r5019331921817638435",
"message": {
"id": "157948187e41b5bb",
"threadId": "157948187e41b5bb",
"labelIds": [
"DRAFT"
]
}
}
Then you can use this id to either send or delete the message.
.afterJSON(function(json){
callback(json.id);
})
I used this function and it worked. Thanks to my friend for help :D
Here're full tests if someone needs it:
This is how I get an ID of created Draft
var frisby = require('frisby');
var new_id = function(frisby, callback)
{
frisby.create('Create Draft Google')
.post('https://www.googleapis.com/gmail/v1/users/me/drafts?access_token=[my_token]', {
message: {
raw: "RGFu...",
}
}, { json: true })
.inspectJSON()
.inspectBody()
.expectStatus(200)
.afterJSON(function(json){
callback(json.id);
})
.toss();
};
module.exports = new_id;
This is how I used it to delete this Draft
var frisby = require('frisby');
var getid_spec = require("./getid_spec.js");
getid_spec(frisby,function(id){
frisby.create('Delete Google Draft Test')
.delete("https://www.googleapis.com/gmail/v1/users/me/drafts/" +id +"?access_token=[my_token]", {})
.expectStatus(204)
.toss();
})