Intro
I wrote some code for a Jenkins pipeline in Groovy that looks like this:
def sendNotifications (args) {
// ... use args ...
sendSlack(
failOnError: true,
color: '#00FF00',
message: "foo",
channel: "#mychan"
)
}
// slackMessage not shown returns a string based on the args.
This code seemed to be working just fine in my pipeline. Now I want to test it. So I create a function that mocks out sendSlack because I don't want my test to send a Slack message, I want to check to see if the arguments passed to sendSlack are the expected ones. So I did this:
void testStubbedSendNotifications(Map inputArgs, ArrayList slackArgs) {
def n = new Notifier()
def sendSlackArgs = []
n.metaClass.sendSlack = { Map x -> sendSlackArgs << x }
n.sendNotifications(inputArgs)
assert sendSlackArgs == slackArgs
}
What is happening here is simple. Instead of calling sendSlack, I'm pushing the arguments that would be called onto an ArrayList and then I check to make sure that list looks right in an assert. So this function takes arguments to call sendNotifications with as well as a list of expected values to see sendSlack being called with as a result.
The Problem
My expected values get mangled only in my tests. Instead of the arguments being a hash map like this:
[
failOnError: true,
color: '#00FF00',
message: "foo",
channel: "#mychan"
]
I instead get this:
[
failOnError: true,
color: '#00FF00',
"message:foo" : null,
channel: "#mychan"
]
Why does the message's key and value get merged into the key?
Related
Use Case: The Iterating a Loop Using Lambda example offers a solution that allows one to loop through an array using Choice state. I would like to use the index of the iterator as a means of accessing a specific element in an array.
AWS CDK Code:
this.iterator = new LambdaInvoke(this, "iterator", {
lambdaFunction: stepFunctionTasksLambdaStack.iterator,
payloadResponseOnly: true,
comment: "Manage iterating over the array of configurable product skus",
retryOnServiceExceptions: true,
timeout: Duration.minutes(3),
});
this.countReached = new Choice(this, "Count Reached?");
this.encodeSku = new LambdaInvoke(this, "Encode Simple Product SKU", {
lambdaFunction: stepFunctionTasksLambdaStack.encodeURIComponent,
payloadResponseOnly: true,
comment: "Encode the SKU to make it suitable for querying the Magento API with",
retryOnServiceExceptions: true,
timeout: Duration.minutes(3),
});
Where could I concatenate the index value the iterator is on for this.encodeSku, plus the array of skus? It'd have to be something like input: "$.arrayOfSkus"["$.iterator.index"].
In the CDK, the solution is to create a payload where we use TaskInput.fromObject to access the information.
this.pickSku = new LambdaInvoke(this, "Choose The SKU", {
lambdaFunction: stepFunctionTasksLambdaStack.arrayElementSelector,
inputPath: "$.augmentArrayAndIndex",
payloadResponseOnly: true,
comment: "Manage iterating over the array of configurable product skus",
retryOnServiceExceptions: true,
timeout: Duration.minutes(3),
payload: TaskInput.fromObject({
"array": JsonPath.stringAt("$.[LOCATION_OF_INFORMATION]"),
"index": JsonPath.numberAt("$.[LOCATION_OF_INFORMATION]")
})
});
I'm struggling to find the correct syntax to use in a Serilog filter expression to find a particular key/value pair in a dictionary within the event properties.
This is a contrived example, but illustrates the issue.
The relevent setup looks like this:
string exceptionFilter = "SomeDictionary.Other = 'Nope'";
LoggerConfiguration config = new LoggerConfiguration()
.Destructure.JsonNetTypes()
.MinimumLevel.ControlledBy(LevelSwitch)
.Enrich.FromLogContext()
.Enrich.With(new ExceptionEnricher())
.Filter.ByExcluding(exceptionFilter)
.WriteTo.MSSqlServer(
connectionString: ConnectionString,
sinkOptions: SinkOptions,
columnOptions: ColumnOptions
);
Logger = config.CreateLogger();
Then I log an event like this:
Log.Fatal(
ex,
"Real Bad {#SomeDictionary}",
new Dictionary<string, string>() {
{"Test", "Test"},
{"Other", "Nope"}
}
)
What should the expression to exclude SomeDictionary['Other'] = 'Nope' be?
If I pass in the event details as an object:
Log.Fatal(
ex,
"Real Bad {#SomeObject}",
new {
Test = "Test",
Other = "Nope"
}
)
Then the following expression works as expected:
SomeObject.Other = 'None'
Unfortunately, I am enriching our logs with a number of dictionaries that I need to be able to filter by and that syntax doesn't seem to work with them.
EDIT
If I serialize the log event properties, it appears that the issue is probably due to escaped quotation marks within the dictionary keys:
"SomeDictionary": {
"Elements": {
"\"Test\"": {
"Value": "Test"
},
"\"Other\"": {
"Value": "Nope"
}
}
}
Still not sure what I need to do to be able to filter on one of the elements.
Resolution
I switched from using Serilog.Filters.Expressions to Serilog.Expressions and the issue resolved itself. Now I can filter on dictionaries using expected syntax:
SomeDictionary['Other'] = 'Nope'
Restassured: How Can we compare each element in Json array to one particular Same value in Java using Hemcrest Matchers, not using Foreach loop.
{
"id": 52352,
"name": "Great Apartments",
"floorplans": [
{
"id": 5342622,
"name": "THE STUDIO",
"fpCustomAmenities": [
{
"displaySequence": 2,
"amenityPartnerId": "gadasd",
"display": true,
"leased": true
},
{
"displaySequence": 13,
"amenityPartnerId": "sdfsfd",
"display": true,
"leased": true
}
]
},
{
"id": 4321020,
"name": "THE First Bed",
"fpCustomAmenities": [
{
"displaySequence": 4,
"amenityPartnerId": "gadasd",
"display": true,
"leased": true
},
{
"displaySequence": 15,
"amenityPartnerId": "hsfdsdf",
"display": true,
"leased": true
}
]
}
]
}
I want to compare that Leased=true for all the leased nodes at all the levels in the json response...
I have working code...
List<List<Boolean>> displayedvaluesfpStandardAmenities =
when().get(baseUrl + restUrl).
then().statusCode(200).log().ifError().
extract().body().jsonPath().getList("floorplans.fpCustomAmenities.display");
for (List<Boolean> displayedStandardList : displayedvaluesfpStandardAmenities) {
for (Boolean isDisplayedTrue : displayedStandardList) {
softAssert.assertTrue(isDisplayedTrue);
}
}
But the issue is I need the code to be in simple format using either Hemcrest Matchers or Restaussred Matchers and try simplistic way like Below, ( which is not working)
when().get(baseUrl + restUrl).
then().assertThat().body("floorplans.fpCustomAmenities.display",equalTo("true"));
The error I am getting is
java.lang.AssertionError: 1 expectation failed.
JSON path floorplans.fpCustomAmenities.display doesn't match.
Expected: true
Actual: <[[true, true], [true, true]]>
So what I need is the that all thes 'display' nodes in the json response where ever it is need to compared with "true", so that my test can Pass.
I have an alternate solution like mentioned above, but All I need is working solution using matchers.
Assuming fpCustomAmenities arrays are not empty, you can use the following solution;
when().get(baseUrl + restUrl).then()
.body("floorplans.findAll { it }.fpCustomAmenities" + // 1st line
".findAll { it }.leased.each{ a -> println a }" + // 2nd line
".grep{ it.contains(false) }.size()", equalTo(0)); // 3rd line
Here from the 1st line, we return each object in fpCustomAmenities array.
From the 2nd line we get boolean value of leased in each fpCustomAmenities object to a boolean array ([true, true]).
Each boolean array is printed from .each{ a -> println a }. I added it only to explain the answer. It is not relevant to the solution.
From 3rd line we check whether, if there is a false in each boolean array. grep() will return only the arrays which has a false. And then we get the filtered array count. Then we check whether it is equal to 0.
Check groovy documentation for more details.
Or
This solution does not use any Matchers. But this works.
String responseBody = when().get(baseUrl + restUrl).
then().extract().response().getBody().asPrettyString();
Assert.assertFalse(responseBody.contains("\"leased\": false"));
I'm trying to parse a JSON data and assign it to a POJO in Grails.
I started with
obj.param=jsonRequest.jsonWrap.attrib.something.jsonParam
After some experimenting and refactoring, it looks like this now.
jsonRequest.jsonWrap.attrib.something.with {
obj.param1=jsonParam1
obj.param2=jsonParam2
//...
}
}
Now, can I avoid the repeated use of obj reference?
I'm imagining that your actual starting point is something like the following. On the JSON side:
import groovy.json.JsonSlurper
String jsonText = '''{
"jsonWrap":{
"attrib":{
"something":{
"jsonParam1": "value1",
"jsonParam2": "value2",
"jsonParam3": "value3",
"jsonParam4": "value4",
"jsonParam5": "value5"
}
}
}
}'''
def jsonRequest = new JsonSlurper().parseText(jsonText)
On the Groovy side:
class ObjectType {
def param1, param2, param3, param4, param5
}
def obj = new ObjectType()
Now, if I had any control over how either the JSON side or the Groovy side are defined then I would do my darnedest to ensure that the property names of the JSON "something" object are exactly the same as the property names in the Groovy "ObjectType" class. For example, like this:
class ObjectType {
def jsonParam1, jsonParam2, jsonParam3, jsonParam4, jsonParam5
}
Then, unmarshalling the "something" object into Groovy is as simple as this:
def obj = new ObjectType(jsonRequest.jsonWrap.attrib.something)
Only one reference to the JSON object. Only one reference to the Groovy object. And the former is used to instantiate the latter. And furthermore, notice that there is no need to reference the properties at all. That is, JSON objects from the slurper are instances of Map, so if the property names match up, you can use the default "Map constructor" syntax.
If, however, you do not control property naming in either set of objects, I would still recommend a different Map-based short-cut. First define a constant Map from one set of property names to the other, like so:
def map = [param1:"jsonParam1", param2:"jsonParam2", param3:"jsonParam3",
param4:"jsonParam4", param5:"jsonParam5"]
Then I would use something like this for the object unmarshalling:
def obj = new ObjectType().with { o ->
jsonRequest.jsonWrap.attrib.something.with { j ->
map.each { oParam, jParam -> o[oParam] = j[jParam] }
}
o
}
i don't think there is a trivial way to trick groovy into "use objectA, if getting is needed and objectB for setting". If obj above is a map or you can apply a map to this object, then you could produce a map in your with block and use this. If you have to have nested structures then more work is needed.
def jsonParam = new Expando([ p1: 'p1', p2: 'p2', p3: 'p3', ])
def obj = new Expando(
jsonParam.with{
[
param1: p1,
param3: p3,
] // `with` will return this map
})
assert obj.param1==jsonParam.p1
assert obj.param3==jsonParam.p3
I use expandos for simple code.
I'm using XMLSlurper. My code is below (but does not work). The problem is that it fails when it hits a node that does not have the attribute "id". How do I account for this?
//Parse XML
def page = new XmlSlurper(false,false).parseText(xml)
//Now save the value of the proper node to a property (this fails)
properties[ "finalValue" ] = page.find {
it.attributes().find { it.key.equalsIgnoreCase( 'id' ) }.value == "myNode"
};
I just need to account for nodes without "id" attribute so it doesn't fail. How do I do that?
You could alternatively use the GPath notation, and check if "#id" is empty first.
The following code snippet finds the last element (since the id attribute is "B" and the value is also "bizz", it prints out "bizz" and "B").
def xml = new XmlSlurper().parseText("<foo><bar>bizz</bar><bar id='A'>bazz</bar><bar id='B'>bizz</bar></foo>")
def x = xml.children().find{!it.#id.isEmpty() && it.text()=="bizz"}
println x
println x.#id
Apprently I can get it to work when I simply use depthFirst. So:
properties[ "finalValue" ] = page.depthFirst().find {
it.attributes().find { it.key.equalsIgnoreCase( 'id' ) }.value == "myNode"
};