How to edit a value (list of entries) from an api response to use in a request body in Gatling/Scala - scala

I have an issue that I'm hoping someone can help me with. I'm pretty new to coding and Gatling, so I'm not sure how to proceed.
I'm using Gatling (with Scala) to create a performance test scenario that contains two API-calls.
GetInformation
SendInformation
I'm storing some of the values from the GetInformation response so I can use it in the body for the SendInformation request. The problem is that some information from the GetInformation response needs to be edited/removed before it is included in the body for SendInformation.
Extract of the GetInformation response:
{
"parameter": [
{
"name": "ResponseFromGetInfo",
"type": "document",
"total": 3,
"entry": [
{
"fullUrl": "urn:uuid:4ea859d0-daa4-4d2a-8fbc-1571cd7dfdb0",
"resource": {
"resourceType": "Composition"
}
},
{
"fullUrl": "urn:uuid:1b10ed79-333b-4838-93a5-a40d22508f0a",
"resource": {
"resourceType": "Practitioner"
}
},
{
"fullUrl": "urn:uuid:650b8e7a-2cfc-4b0b-a23b-a85d1bf782de",
"resource": {
"resourceType": "Dispense"
}
}
]
}
]
}
What I want is to store the list in "entry" and remove the entries with resourceType = "Dispense" so I can use it in the body for SendInformation.
It would have been ok if the entry list always had the same number of entries and order, but that is not the case. The number of entries can be several hundred and the order of entries varies. The number of entries are equal to the "total" value that is included in the GetInformation response.
I've thought about a few ways to solve it, but now I'm stuck. Some alternatives:
Extract the entire "entry" list using .check(jsonPath("$.parameter[0].entry").saveAs("entryList")) and then iterate through the list to remove the entries with resourceTypes = "Dispense".
But I don't know how to iterate over a value of type io.gatling.core.session.SessionAttribute, or if this is possible. It would have been nice if I could iterate over the entry list and check if parameter[0].entry[0].resourceType = "Dispense", and remove the entry if the statement is true.
I'm also considering If I can use StringBuilder in some way. Maybe if I check one entry at the time using .check(parameter[0].entry[X].resourceType != dispense, and if true then append it to a stringBuilder.
Does someone know how I can do this? Either by one of the alternatives that I listed, or in a different way? All help is appreciated :)
So maybe in the end it will look something like this:
val scn = scenario("getAndSendInformation")
.exec(http("getInformation")
.post("/Information/$getInformation")
.body(ElFileBody("bodies/getInformtion.json"))
// I can save total, så I know the total number of entries in the entry list
.check(jsonPath("$.parameter[0].total").saveAs("total"))
//Store entire entry list
.check(jsonPath("$.parameter[0].entry").saveAs("entryList"))
//Or store all entries separatly and check afterwards who have resourceType = "dispense"? Not sure how to do this..
.check(jsonPath("$.parameter[0].entry[0]").saveAs("entry_0"))
.check(jsonPath("$.parameter[0].entry[1]").saveAs("entry_1"))
//...
.check(jsonPath("$.parameter[0].entry[X]").saveAs("entry_X"))
)
//Alternativ 1
.repeat("${total}", "counter") {
exec(session => {
//Do some magic here
//Check if session("parameter[0]_entry[counter].resourceType") = "Dispense" {
// if yes, remove entry from entry list}
session})}
//Alternativ 2
val entryString = new StringBuilder("")
.repeat("${total}", "counter") {
exec(session => {
//Do some magic here
//Check if session("parameter[0]_entry[counter].resourceType") != "Dispense" {
// if yes, add to StringBuilder}
// entryString.append(session("parameter[0]_entry[counter]").as[String] + ", ")
session})}
.exec(http("sendInformation")
.post("/Information/$sendInformation")
.body(ElFileBody("bodies/sendInformationRequest.json")))

I'm pretty new to coding
I'm using Gatling (with Scala)
Gatling with Java would probably be an easier solution for you.
check(jsonPath("$.parameter[0].entry").saveAs("entryList"))
This is going to capture a String, not a list. In order to be able to iterate, you have to use ofXXX/ofType[], see https://gatling.io/docs/gatling/reference/current/core/check/#jsonpath
Then, in order to generate the next request's body, you could consider a templating engine such as PebbleBody (https://gatling.io/docs/gatling/reference/current/http/request/#pebblestringbody) or indeed use StringBody with a function with a StringBuilder.

Related

Arbitrary HTTP API Call to Enter Cell Value into a MUTL_PICKLIST Column

I am quite new new to Smartsheets and to programming.
I am using Integromat to update various stuff in Smartsheets - 99% operations are done via a nice interface for dummies.
But I have an issue with one column which is MULTI_PICKLIST and which cannot be processed with native dummy-friendly UI.
Basically, I'm adding a new row and one of the columns on the way is the MULTI_PICKLIST one. In order to enter value into this cell, I need to make an arbitrary HTTP API call.
I know row ID, I know column ID. I just need to construct the body of the HTTP request.
The possible picklist value are: John or Maya or Paul. Assume I need to enter "John" into the column.
Attached, you will find my "progress". I obviously, I'm stuck with the BODY part. Can someone give me a little push, please? I think it's gotta be like 5 lines of code.
This is what I have:
DZ
A few things...
First, the value that you're using for URL doesn't look quite right. It should be in the following format, where {sheetId} is replaced with the ID of the sheet you're updating:
sheets/{sheetId}/rows
Second, I don't think you need the key/value that you've specified for Query String -- I'd suggest that you delete this info.
Next, I'm not sure what the other possible values are for Type (based on your screenshot, it looks like a picklist) -- but if JSON is an option, I'd suggest choosing that option instead of Text.
Finally, here's any example of the correct structure/contents for Body to update a MULTI_PICKLIST cell with the value John -- replace the value of the id property (5225480965908356) with your Row ID and replace the value of the columnId property (8436269809198980) with your Column ID:
[
{
"id": "5225480965908356",
"cells": [
{
"columnId": "8436269809198980",
"objectValue": {
"objectType": "MULTI_PICKLIST",
"values": ["John"]
}
}
]
}
]
If you want to select multiple values for a MULTI_PICKLIST cell, here's an example that specifies two values for the cell (John and Maya):
[
{
"id": "5225480965908356",
"cells": [
{
"columnId": "8436269809198980",
"objectValue": {
"objectType": "MULTI_PICKLIST",
"values": ["John", "Maya"]
}
}
]
}
]
** UPDATE **
My initial answer answer above assumed you wanted to update a cell value in a MULTI-PICKLIST column (b/c you've selected PUT for the Method value in your screenshot -- which is the verb used to update a row). Having re-read your question just now though, it sounds like maybe you want to add a new row...is that correct? If so, then the value for Method should be POST (not PUT), and Body will need to include additional objects within the cells array to specify values of other cells in the new row. The following example request (when used with the verb POST) adds a new row and populates 3 cells in that row, the first of which is a MULTI_PICKLIST cell:
[
{
"cells": [
{
"columnId": "8436269809198980",
"objectValue": {
"objectType": "MULTI_PICKLIST",
"values": ["John"]
}
},
{
"columnId": 6101753539127172,
"value": "test value"
},
{
"columnId": 4055216160040836,
"value": 10
}
]
}
]
More info about the Add Rows request can be found in the Smartsheet API docs: Add Rows.

REST API Multiple PUT or DELETE in one time

Greeting everyone, I have a datatable in my html page that I populated using REST API. I can create new row and also update or delete by selecting a row and clicking the edit or delete button.
But currently I am unable to delete update or delete multiple row at once due to url error,
e.g : PUT http://127.0.0.1:8000/dashboard/content_detail/5,7,9/ 404 (Not Found)
how can I split this this into several separate url with respective id when I update or delete.
e.g :
/dashboard/content_detail/5
/dashboard/content_detail/7
/dashboard/content_detail/9
Below is my code, any help is much appreciated thank you.
idSrc: 'id',
ajax: {
create: {
type: 'POST',
url: content_path,
data: function (content_data) {
var create_data = {};
$.each(content_data.data, function (id, value) {
create_data['name'] = value['name'];
create_data['description'] = value['description'];
create_data['category'] = value['category'];
});
return create_data;
},
success: function () {
content_table.api().ajax.reload();
}
},
edit: {
type: 'PUT',
url: '/dashboard/content_detail/_id_/',
data: function (content_data) {
var updated_data = {};
$.each(content_data.data, function (id, value) {
updated_data['description'] = value['description'];
updated_data['category'] = value['category'];
updated_data['name'] = value['name'];
});
return updated_data;
},
success: function () {
content_table.api().ajax.reload();
}
},
remove: {
type: 'DELETE',
url: '/dashboard/content_detail/_id_/',
data: function (content_data) {
var deleted_data = {};
$.each(content_data.data, function (id, value) {
deleted_data['id'] = id;
});
return deleted_data;
},
success: function () {
content_table.api().ajax.reload();
}
}
},
If you're going to allow the update of a large number of items at once, then PATCH might be your friend:
Looking at the RFC 6902 (which defines the Patch standard), from the client's perspective the API could be called like
PATCH /authors/{authorId}/book
[
{ "op": "replace", "path": "/dashboard/content_detail/5", "value": "test"},
{ "op": "remove", "path": "/dashboard/content_detail", "value": [ "7", "9" ]}
]
From a design perspective you don't want several ids in your url.
I would prefer single calls for each change, thinking in resources you only manipulate one at a time.
In case this is a perfomance issue, I recommend a special url marked with action or something simliar, to make clear this ist not REST.
In HTTP it is not required for information to only exist on a single resource. It is possible to have multiple resources that represent the same underlying data.
It's therefore not out of the question to create a resource that 'represents' a set of other resources that you wish to DELETE or PUT to.
I do agree that it might not be the most desirable. I think we tend to prefer having information only exist in a single part of tree, and I think we like to avoid situations where updating a resource effects a secondary resource's state. However, if you are looking for a strictly RESTful solution to solve this problem, I think it's the right way.
Therefore a url design such as:
/dashboard/content_detail/5,7,9/
Is not necessarily non-RESTful or goes against the HTTP protocol. The fact that you're getting a 404 on that URL currently has to do with your application framework, not the protocol (HTTP) or architecture (REST) of your API.
However, for cases such as these I feel I would personally be inclined to sometimes create a separate POST endpoint that, acting outside of REST like an RPC endpoint. Specifically for these types of batch requests.

How to iterate over json response array

I have a question about Gatling.
I need to get the following response:
[
{
"id": 1,
"name": "Jack"
},
{
"id": 2,
"name": "John"
}
]
grab those ids, iterate over them and make a new request for each of them.
So far I have this:
.exec(
http("Image list")
.get("/api/img")
.headers(headers_0)
.check(
jsonPath("$..id").findAll.saveAs("imgs")
)
)
It successfuly saves ids to "imgs" which is session variable but I am not able to iterate over them or process it at all. How can I process it? I am new to Gatling and Scala so I have no idea how to approach this. Please help.
You can treat the imgs session variable as a Scala List:
val ids = session("imgs").as[List[Int]]
ids.foreach(id => ...)
An update to reflect the fact that the internal implementation is now a Vector, as OP has discovered:
val ids = session("imgs").as[Seq[Int]]
I found a solution.
The only possible format is Seq. In my case this solves the problem:
val imageIds = session("imgs").as[Seq[String]]

How to document response fields for an object as Map(HashMap)

I am doing documentation for a REST service returning an object like this:
Map<String, HashMap<Long, String>>
and i find no way to describe response fields for such object.
Let's have a look at my code.
The service:
#RequestMapping(value = "/data", method = RequestMethod.GET)
public Map<String, HashMap<Long, String>> getData()
{
Map<String, HashMap<Long, String>> list = dao.getData();
return list;
}
My unit-test-based documentation:
#Test
public void testData() throws Exception
{
TestUtils.beginTestLog(log, "testData");
RestDocumentationResultHandler document = document(SNIPPET_NAME_PATTERN ,preprocessResponse(prettyPrint()));
document.snippets(
// ,responseFields(
// fieldWithPath("key").description("key description").type("String"),
// fieldWithPath("value").description("value as a Hashmap").type("String"),
// fieldWithPath("value.key").description("value.key description").type("String"),
// fieldWithPath("value.value").description("value.value description").type("String"),
// )
String token = TestUtils.performLogin(mockMvc, "user", "password");
mockMvc
.perform(get(APP_BUILD_NAME + "/svc/data").contextPath(APP_BUILD_NAME)
.header("TOKEN", token)
)
.andExpect(status().is(200))
.andExpect(content().contentType("application/json;charset=UTF-8"))
.andExpect(jsonPath("$").isMap())
.andDo(document);
TestUtils.endTestLog(log, "testData");
}
As you can see the code for response fields is commented out since I haven't had any solution for it yet. I am working on that but i really appreciate your help. Thank you in advance.
Your JSON contains a huge number of different fields. There looks to be over 1000 different entries in the map. Each of those entries is itself a map with a single key-value pair. Those keys all appear to vary as well. Potentially, that gives you over 2000 fields to document:
cancel
cancel.56284
year
year.41685
segment_de_clientele
segment_de_clientele.120705
…
This structure is making it hard to document and is also a strong indicator that it will be hard to consume by clients. Ideally, you would restructure the JSON so that each entry has the same keys and it's only the values the vary from entry to entry. Something like this, for example:
{
"translations": [ {
"name": "cancel",
"id": 56284,
"text": "Exit"
}, {
"name": "year",
"id": 41685,
"text": "Year"
}, {
"name": "segment_de_clientele",
"id": 120705,
"text": "Client segment"
}]
}
This would mean that you only have a handful of fields to document:
translations[]
translations[].name
translations[].id
translations[].text
If that's not possible, then I'd stop trying to use the response fields snippet to document the structure of the response. Instead, you should describe its structure manually in your main Asciidoctor source file.
There are two options:
1> Changing MAP to LIST of objects so that the response fields can be described easily.
2> Putting description manually to index.adoc file.
In my case, I go for option 2 because I have to stick to MAP.

Apigility: How to render embedded objects?

How do I render embedded objects in Apigility? For example, if I have a 'user' object and it composes a 'country' object, should I be rendering the 'country' object as an embedded object? And how should I do this?
I am using the Zend\Stdlib\Hydrator\ArraySerializable. My getArrayCopy() method simply returns an array of properties that I want exposed. The array keys are the property names. The array values are the property values. In the case of user->country, the value is an object, not a scalar.
When I return the user object from UserResource->fetch(), here's how it is rendered:
{
"id": "1",
"firstName": "Joe",
"lastName": "Bloggs",
"status": "Active",
"email": "test#example.com",
"country": {
"code": "AU",
"name": "Australia"
},
"settings": "0",
"_links": {
"self": {
"href": "http://api.mydomain.local/users/1"
}
}
}
Note that 'country' is not in an _embedded field. If it is supposed to be in _embedded, I would have thought that Apigility would automatically do that (since it automatically adds the _links object).
As a related issue, how do I go about returning other rel links, such as back, forward, etc?
The easiest way to get Apigility to render embedded resources is when there is an API/resource associated to the embedded object. What I mean for your example is that you'd have an API resource that has a country entity. In that case, if your getArrayCopy returned the the CountryEntity, Apigility would render it automatically as an embedded resource.
If your getArrayCopy is returning country as an array with code and name, you'll end up with what you saw.
For the other part, the rel links for first, last, prev and next will come from the fetchAll method when you return a Paginator. Your collection extends from this already, but it needs an adapter. The code could look something like this:
public function fetchAll($params)
{
// Return a \Zend\Db\Select object that will retrieve the
// stuff you want from the database
$select = $this->service->fetchAll($params);
$entityClass = $this->getEntityClass();
$entity = new $entityClass();
$hydrator = new \Zend\Stdlib\ArraySerializable();
$prototype = new \Zend\Db\ResultSet\HydratingResultSet($hydrator, $entity);
$paginator = new \Zend\Paginator\Adapter\DbSelect($select, $this->sql, $prototype);
$collectionClass = $this->getCollectionClass();
return new $collectionClass($paginator);
}
There are other paginator adapters as well - an ArrayAdapter which will take in an array of however big and then paginate it so you only get the desired number of results. The downside to this if you use it with database results, you'll potentially be retrieving and discarding a lot of results. The DbSelect paginator will modify the $select object to add the limit and order clause automatically so you only retrieve the bits you need. There are also adapters if you're using DbTableGateway, Iterators or even callbacks. You can also implement your own of course.
Hope this helps. If you have more specific needs or clarification, please comment and I'll do my best.
I posted this example on github.
https://github.com/martins-smb/apigility-renderCollection-example
Hope this helps.