I have a json as below and I am trying to get the list of all the "dev" hosts using Groovy.
How can I do that ? I have attached a sample code that I am using currently, but that does not work for obvious reasons. I am new to Groovy.
{
"app1": {
"dev": [
"host1",
"host2"
],
"qa": null,
"uat": [
"host11"
]
},
"app2": {
"qa": null,
"stable": null,
"dev": [
"host3",
"host4"
]
}
CODE:
apiResponse = <Code which returns the json as mentioned above>
def parser = new JsonSlurper()
def host_list = parser.parseText(apiResponse)
dev_hosts = host_list[]['dev']
print dev_hosts
Expected Result:
['host1','host2','host3','host4']
There are any number of ways to do it and the best way may depend on knowing some more about the common usage patterns with respect to how many objects are in the top level JSON document etc, but the following does function:
String jsonInput = """
{
"app1": {
"dev": [
"host1",
"host2"
],
"qa": null,
"uat": [
"host11"
]
},
"app2": {
"qa": null,
"stable": null,
"dev": [
"host3",
"host4"
]
}
}"""
def object = new JsonSlurper().parseText(jsonInput)
def results = object.collect { it.value.dev }.flatten()
assert results == ['host1', 'host2', 'host3', 'host4']
def dev_hosts = host_list.values().collectMany{it['dev']}
edit: OP adjusted the specs: dev should be a variable and the lists
need to be flattened (at first level presumably).
Related
I am in no way an expert with groovy so please don't hold that against me.
I have JSON that looks like this:
{
"metrics": [
{
"name": "metric_a",
"help": "This tracks your A stuff.",
"type": "GAUGE",
"labels": [
"pool"
],
"unit": "",
"aggregates": [],
"meta": [
{
"category": "CAT A",
"deployment": "environment-a"
}
],
"additional_notes": "Some stuff (potentially)"
},
...
]
...
}
I'm using it as a source for automated documentation of all the metrics. So, I'm iterating through it in various ways to get the information I need. So far so good, I'm most of the way there. The problem is this all needs to be organized per the deployment environment. Meaning, multiple metrics will share the same value for deployment.
My thought was I could create a map with deployment as the key and the metric name for any metric that has a matching deployment as the value. Once I have that map, it should be easy for me to organize things the way they should be. I can't figure out how to do that. The result is all the metric names are added which is expected since I'm not doing anything to filter them out. I was thinking that groupBy would make sense here but I can't figure out how to use it effectively and frankly I'm not sure it will solve my problem by itself. Here is my code so far:
parentChild = [:]
children = []
metrics.each { metric ->
def metricName = metric.name
def depName = metric.meta.findResult{ it.deployment }
children.add(metricName)
parentChild.put(depName, children)
}
What is the best way to create a new map where the values for each key are based off a specific condition?
EDIT: The desired result would be each key in the resulting map would be a unique deployment value from all the metrics (as a string). Each value would be name of each metric that contains that deployment (as an array).
[environment-a:
[metric_a,metric_b,metric_c,...],
environment-b:
[metric_d,metric_e,metric_f,...]
...]
I would use a combo of withDefault() to pre-fill each map-entry value with a fresh TreeSet-instance (sorted no-duplicates set) and standard inject().
I reduced your sample data to the bare minimum and added some new nodes:
import groovy.json.*
String input = '''\
{
"metrics": [
{
"name": "metric_a",
"meta": [
{
"deployment": "environment-a"
}
]
},
{
"name": "metric_b",
"meta": [
{
"deployment": "environment-a"
}
]
},
{
"name": "metric_c",
"meta": [
{
"deployment": "environment-a"
},
{
"deployment": "environment-b"
}
]
},
{
"name": "metric_d",
"meta": [
{
"deployment": "environment-b"
}
]
}
]
}'''
def json = new JsonSlurper().parseText input
def groupedByDeployment = json.metrics.inject( [:].withDefault{ new TreeSet() } ){ res, metric ->
metric.meta.each{ res[ it.deployment ] << metric.name }
res
}
assert groupedByDeployment.toString() == '[environment-a:[metric_a, metric_b, metric_c], environment-b:[metric_c, metric_d]]'
If your metrics.meta array is supposed to have a single value, you can simplify the code by replacing the line:
metric.meta.each{ res[ it.deployment ] << metric.name }
with
res[ metric.meta.first().deployment ] << metric.name
I have a JSON representation that looks like the following:
{
"results": [
{
"vulnerabilities": [
],
}
]
}
I tried to output just the vulnerabilities portion, but the following doesnt work:
for key, value in json_object.items():
print(key, ' : ', value)
This prints out the whole results but not just the vulnerabilities
Assuming multiple dicts in results, each with a vulnerabilities key, you can do:
json_object = {
"results": [
{
"vulnerabilities": [
],
}
]
}
for result in json_object['results']:
for vuln in result['vulnerabilities']:
print(vuln)
I have the issue that I'm not able to execute the following code. The syntax seems to be okay, but when I try to execute it, I get the response, that:
Expression.Error: We cannot convert a value of type Record to type "Text".
Details:
Value=[Record]
Type=[Type]
let
body="{
""page"": ""1"",
""pageSize"": ""100"",
""requestParams"": {
""deviceUids"": [
""xxx-yyy-xxx-yyyy-xxxx"",
""yyy-xxx-yyy-xxxx-yyyy"",
""aaa-bbb-aaa-bbbb-aaaa"",
""ccc-ddd-ccc-dddd-cccc""
],
""entityColumns"": [
{
""entityId"": ""144"",
""joinColumnName"": ""device_uid"",
""columnName"": ""device_random_date""
}
],
""columnNames"": [
""ts"",
""device_uid"",
""1"",
""32"",
""55"",
""203"",
""204""
],
""startUnixTsMs"": ""1583413637000"",
""endUnixTsMs"": ""1583413640000"",
""columnFilters"": [
{
""filterType"": ""eq"",
""columnName"": ""55"",
""value"": ""1234""
}
],
""sortOrder"": [
{
""column"": ""ts"",
""order"": ""DESC""
},
{
""column"": ""55"",
""order"": ""ASC""
}
],
""entityFilters"": [
{
""entityId"": ""144"",
""entityEntryIds"": [
""12345-221-232-1231-123456""
]
}
]
}
}",
Parsed_JSON = Json.Document(body),
BuildQueryString = Uri.BuildQueryString(Parsed_JSON),
Quelle = Json.Document(Web.Contents("http://localhost:8101/device-data-reader-api/read-paginated/xxx-xxx-yyyy-yyyy", [Headers=[#"Content-Type"="application/json"], Content = Text.ToBinary(BuildQueryString)]))
in
Quelle
I tried to remove the quotes of the numbers, but this leads to the same issue, as system complains it cannot convert numbers into text.
I need the body which needs to be handed over with the request in order to do a POST request. What I'm doing wrong?
Since you seem to want to send this as application/json, I think you would change this bit in your code:
Content = Text.ToBinary(BuildQueryString)
to:
Content = Text.ToBinary(body)
and then you'd also get rid of the lines below (since you don't need them):
Parsed_JSON = Json.Document(body),
BuildQueryString = Uri.BuildQueryString(Parsed_JSON),
I don't think you would need Uri.BuildQueryString unless you wanted to send as application/x-www-form-urlencoded (i.e. URL encoded key-value pairs).
Unrelated: If it helps, you can build the structure in M and then use JSON.FromValue to turn the structure into bytes which can be put directly into the POST body. Untested example is below.
let
body = [
page = "1",
pageSize = "100",
requestParams = [
deviceUids = {
"xxx-yyy-xxx-yyyy-xxxx",
"yyy-xxx-yyy-xxxx-yyyy",
"aaa-bbb-aaa-bbbb-aaaa",
"ccc-ddd-ccc-dddd-cccc"
},
entityColumns = {
[
entityId = "144",
joinColumnName = "device_uid",
columnName = "device_random_date"
]
},
columnNames = {
"ts",
"device_uid",
"1",
"32",
"55",
"203",
"204"
},
startUnixTsMs = "1583413637000",
endUnixTsMs = "1583413640000",
columnFilters = {
[
filterType = "eq",
columnName = "55",
value = "1234"
]
},
sortOrder = {
[
column = "ts",
order = "DESC"
],
[
column = "55",
order = "ASC"
]
},
entityFilters = {
[
entityId = "144",
entityEntryIds = {
"12345-221-232-1231-123456"
}
]
}
]
],
Quelle = Json.Document(
Web.Contents(
"http://localhost:8101/device-data-reader-api/read-paginated/xxx-xxx-yyyy-yyyy",
[
Headers = [#"Content-Type" = "application/json"],
Content = Json.FromValue(body)
]
)
)
in
Quelle
It might look a little weird (since M uses [] instead of {}, {} instead of [] and = instead of :), but just mentioning in case it helps.
I have a JSON file which looks like below. I am getting the parameters for name and product_version. Using both of them I need to get the relese_version and latest boolean value.
eg:- If var1 = section1 var2 = 2.6.0 then the release_version should be taken as 2.6.0.9 and latest as false in groovy.
file.json
{
"platforms": [
{
"name": "section1",
"versions": [
{
"product_version": "2.6.0",
"release_version": "2.6.0.9",
"latest": false
},
{
"product_version": "3.0.0",
"release_version": "3.0.0.3",
"latest": false
}
]
},
{
"name": "section2",
"versions": [
{
"product_version": "2.6.0",
"release_version": "2.6.0.9",
"latest": false
},
{
"product_version": "3.0.0",
"release_version": "3.0.0.3",
"latest": false
}
]
}
]
}
This is the code snippet I tried out.
filename = "file.json"
def jsonSlurper = new JsonSlurper()
parsed_json = jsonSlurper.parse(new File(filename))
release_tag = json.parsed_json.find {platforms.name == "section1".version[].product_version == "2.6.0".release_version}
println release_tag
But this didn't work. Please help me with this
You first have to find the platform by name (which could fail); next
find in the versions the product version. E.g.
def data = new groovy.json.JsonSlurper().parse("data.json" as File)
def name='section1'
def productVersion = '2.6.0'
// XXX
def result = data.platforms.find{ it.name == name }?.versions?.find{ it.product_version == productVersion }
assert result.release_version == '2.6.0.9'
assert result.latest == false
Note the use of the "elvis operator" after the first find to
short-circuit.
If you have to do many such lookups on the same data file, it might make
sense to shape the data into a better form for the lookup you are doing
(e.g. turn that into maps of maps for your two lookup keys)
Following the doc at Adding custom page fields, im trying to add Todo's to a Task object/page model for them to be queryable via the api (same thing for todos, make parent Task queryable), however, the related field always returns an empty array (I would have expected the related (parent or child) Pages to be in the response:
class Task(Page):
CHOICES = [(i, i) for i in range(11)]
sub_order = models.IntegerField(default=1, choices=CHOICES)
content_panels = Page.content_panels + [
...
]
api_fields = [
APIField('title'),
APIField('todos'),
]
class Todo(Page):
CHOICES = [(i, i) for i in range(1, 11)]
sub_order = models.IntegerField(default=1, choices=CHOICES)
parent_page_types = ['app.Task']
page = models.ForeignKey('app.Task', on_delete=models.SET_NULL, null=True, blank=True, related_name='todos')
content_panels = Page.content_panels + [
...
]
api_fields = [
APIField('parent_page_types'),
APIField('sub_order'),
]
endpoint: .../api/v2/pages/?format=json&type=app.Task&fields=*
Response:
{
"meta": {
"total_count": 1
},
"items": [
{
"id": 18,
"meta": {
"type": "app.Task",
"detail_url": ".../api/v2/pages/18/",
"html_url": "theURL",
"slug": "aasdasdsad",
"show_in_menus": false,
"seo_title": "",
"search_description": "",
"first_published_at": "2019-02-05T14:13:09.596817Z"
},
"title": "aasdasdsad",
"parent_page_types": [
"app.ParentPageType"
],
"sub_order": 1,
"todos": [] <-------- EMPTY ?
}
]
}
UPDATE:
the sqlite db doesn't seem to be creating/adding the todos or tasks in the table when I create the records =/ . The field is showing up as null, still troubleshooting