This question is resulting from a previous question here
Lets says our implemented server v1 and v2 response looks as follows
* def v1Response = { id: "1", name: "awesome" }
* def v2Response = { id: "2", name: "awesome", value: "karate" }
Similarly we define the client schema for v1 and v2 like as follows
* def v1Schema = { id: "#string", name: "#string }
* def v2Schema = { id: "#string", name: "#string, value: "#string" }
From the above given data, all I want is to test following three cases in a single generic line and they must pass
1. * match v1Response == v1Schema
2. * match v2Response == v2Schema
3. * match v2Response contains v1Schema
using a single generic line as follows
* match response ==/contains schema <--- should be able to test all above three cases above and they must pass.
See my proposed suggestion in previous question for maybe possible ways to achieve this.
I have already tried the solution noted in previous question using karate.filterKeys(), however the third case will fail because it focuses on filtering the keys not the comparison itself so the below last line will not be able to test all three cases above.
* def response = { id: "2", name: "awesome", value: "karate" }
* def schema = { id: "#string", name: "#string" }
* match response == karate.filterKeys(schema, response) <--- This will fail
For an accepted answer all three case must pass
Looks like you over-engineered so much you forgot about contains :P
* def schemas =
"""
{
v1: { id: "#string", name: "#string" },
v2: { id: "#string", name: "#string", value: "#string" }
}
"""
* def env = 'v1'
* def response = { id: "1", name: "awesome" }
* match response contains karate.filterKeys(schemas[env], response)
* def response = { id: "2", name: "awesome", value: "karate" }
* match response contains karate.filterKeys(schemas[env], response)
* def env = 'v2'
* def response = { id: "1", name: "awesome" }
* match response contains karate.filterKeys(schemas[env], response)
* def response = { id: "2", name: "awesome", value: "karate" }
* match response contains karate.filterKeys(schemas[env], response)
Related
here is my mutation . that was working fine . from yesterday it throw error on condition key
condition: "{\"data\":{\"a\":{\"source\":\"ENTITY_STATE\",\"id\":\"4b026027-7458-4ba4-9079-f3dd4f16b65b\"}},\"rules\":{\"==\":[{\"var\":\"a.on\"},false]}}"
here is my full mutation :
mutation {
rule {
create(
input: {
name: "After every minute ?"
description: "Will turn on device after every minute"
triggers: [
{
name: "TimeTrigger"
options: {
cron: "* * * * 1"
# time: "after 10 seconds"
}
}
]
condition: "{\"data\":{\"a\":{\"source\":\"ENTITY_STATE\",\"id\":\"4b026027-7458-4ba4-9079-f3dd4f16b65b\"}},\"rules\":{\"==\":[{\"var\":\"a.on\"},false]}}"
actions: [
{
name: "EntitySetStateAction"
options: {
entityId: "4b026027-7458-4ba4-9079-f3dd4f16b65b"
state: "{\"on\": true,\"thermostat.mode\": \"cool\",\"thermostat.setpoint\": 20,\"fanSpeed.mode\": \"high\"}"
# state: {on: true}
}
}
]
}
) {
id
embedded
name
description
active
actions {
options
} # triggers
# condition {rulenes data}
# triggers
}
}
}
I have a Person object as follows
{
name: 'John Doe'
properties:
[
{
name: 'eyeColor',
value: 'brown'
},
{
name: 'age',
value: 25
},
{
name: 'interest',
value: 'reading'
},
{
name: 'interest',
value: 'diving'
}
]
}
Now I want to be able to filter my object based on multiple properties. In pseudocode:
Return all people for which
there exists any p.property such that
p.property.propertyname == 'interest'
AND p.property.propertyvalue == 'reading'
AND there exists any p.property such that
p.property.propertyname == 'age'
AND p.property.propertyvalue < 30
What is the most concise and extensible (I want to be able to apply N of these filters) of doing this without having too many intermediate results?
Have JSON received from some REST API:
{
"advertiser_id": {
"8253":{
"name":"Signify",
"id":8253
},
"2920":{
"name":"Hyundai",
"id":2920
}
}
}
I wan't to extract maps inside numbers like 8253, 2920 but without hard mapping these numbers, they can be different at any time. Anyway these numbers just duplicates id inside.
Expected output after transformation:
[
{
"name":"Signify",
"id":8253
},
{
"name":"Hyundai",
"id":2920
}
]
Tried with:
import groovy.json.*
def json = '''
'''
def p = new JsonSlurper().parseText(json)
def result = p["advertiser_id"].collectEntries{ k, v ->
[
id: v.id,
name: v.name
]
}
But it returns only one "object":
{
"id": 8905,
"name": "Spotify"
}
Also achieved wrong result with next code:
def a = p["advertiser_id"].collectMany {
it.value.collect{ k, v ->
[
id: k,
name: v
]
}
}
you want to build a list - so you need collect instead of collectEntries
def p = new JsonSlurper().parseText(json)
def result = p["advertiser_id"].collect{ k, v ->
[
id: v.id,
name: v.name
]
}
and you are not doing any transformation to nested objects - so, you could simplify the code to this:
def result = p.advertiser_id.collect{ k, v -> v }
I am trying to illicit certain key values out of an API JSON output from GCP Recommendations API using Python, and am newer to using Python. Most of the values that I am trying to illicit I can fetch without an issue, however, when I try to illicit certain values within a more deeply nested code block in the JSON, it fails with the error: TypeError: 'OperationGroup' object is not subscriptable
The full output of the JSON response is here (some of the values are changed to protect company information):
name: "projects/12345678910/locations/us-central1-a/recommenders/google.compute.instance.MachineTypeRecommender/recommendations/abcd-efg-hijk-lmnop-qrstuv-123456"
description: "Save cost by changing machine type from e2-medium to e2-small."
last_refresh_time {
seconds: 1623222401
}
primary_impact {
category: COST
cost_projection {
cost {
currency_code: "USD"
units: -12
nanos: -98539964
}
duration {
seconds: 2592000
}
}
}
content {
operation_groups {
operations {
action: "test"
resource_type: "compute.googleapis.com/Instance"
resource: "//compute.googleapis.com/projects/xyz/zones/us-central1-a/instances/abcname123"
path: "/machineType"
value_matcher {
matches_pattern: ".*zones/us-central1-a/machineTypes/e2-medium"
}
}
operations {
action: "replace"
resource_type: "compute.googleapis.com/Instance"
resource: "//compute.googleapis.com/projects/xyz/zones/us-central1-a/instances/abcname123"
path: "/machineType"
value {
string_value: "zones/us-central1-a/machineTypes/e2-small"
}
}
}
}
state_info {
state: ACTIVE
}
etag: "\"abc-123-def-456\""
recommender_subtype: "CHANGE_MACHINE_TYPE"
name: "projects/12345678910/locations/us-central1-a/recommenders/google.compute.instance.MachineTypeRecommender/recommendations/abcdefg-hijklmnop-123-456"
description: "Save cost by changing machine type from e2-medium to e2-small."
last_refresh_time {
seconds: 1623222401
}
primary_impact {
category: COST
cost_projection {
cost {
currency_code: "USD"
units: -12
nanos: -99648292
}
duration {
seconds: 2592000
}
}
}
content {
operation_groups {
operations {
action: "test"
resource_type: "compute.googleapis.com/Instance"
resource: "//compute.googleapis.com/projects/xyz/zones/us-central1-a/instances/instance-example1"
path: "/machineType"
value_matcher {
matches_pattern: ".*zones/us-central1-a/machineTypes/e2-medium"
}
}
operations {
action: "replace"
resource_type: "compute.googleapis.com/Instance"
resource: "//compute.googleapis.com/projects/xyz/zones/us-central1-a/instances/instance-example1"
path: "/machineType"
value {
string_value: "zones/us-central1-a/machineTypes/e2-small"
}
}
}
}
state_info {
state: ACTIVE
}
etag: "\"abcdefg12345\""
recommender_subtype: "CHANGE_MACHINE_TYPE"
name: "projects/12345678910/locations/us-central1-a/recommenders/google.compute.instance.MachineTypeRecommender/recommendations/abcd1234"
description: "Save cost by changing machine type from e2-medium to e2-small."
last_refresh_time {
seconds: 1623222401
}
primary_impact {
category: COST
cost_projection {
cost {
currency_code: "USD"
units: -11
nanos: -568971875
}
duration {
seconds: 2592000
}
}
}
content {
operation_groups {
operations {
action: "test"
resource_type: "compute.googleapis.com/Instance"
resource: "//compute.googleapis.com/projects/abcd/zones/us-central1-a/instances/instance-example2"
path: "/machineType"
value_matcher {
matches_pattern: ".*zones/us-central1-a/machineTypes/e2-medium"
}
}
operations {
action: "replace"
resource_type: "compute.googleapis.com/Instance"
resource: "//compute.googleapis.com/projects/abcd/zones/us-central1-a/instances/instance-example2"
path: "/machineType"
value {
string_value: "zones/us-central1-a/machineTypes/e2-small"
}
}
}
}
state_info {
state: ACTIVE
}
etag: "\"abcd1234\""
recommender_subtype: "CHANGE_MACHINE_TYPE"
Here is my code in Python:
from google.cloud import recommender
import os
client = recommender.RecommenderClient()
def main():
name = client.list_recommendations(parent='projects/xyzproject/locations/us-central1-a/recommenders/google.compute.instance.MachineTypeRecommender')
for element in name:
# print(element)
print(element.description)
print(element.primary_impact.category)
print(element.primary_impact.cost_projection.cost.currency_code)
print(element.primary_impact.cost_projection.cost.units)
print(element.state_info.state)
print(element.content.operation_groups)
for item in element.content.operation_groups:
print(item['resource_type'])
main()
The following portions of the above work:
print(element.description)
print(element.primary_impact.category)
print(element.primary_impact.cost_projection.cost.currency_code)
print(element.primary_impact.cost_projection.cost.units)
print(element.state_info.state)
print(element.content.operation_groups)
but the one that I'm having the error and trouble with is:
for item in element.content.operation_groups:
print(item['resource_type'])
Whenever I try to use that portion of the python script it fails with the error:
TypeError: 'OperationGroup' object is not subscriptable
So, can someone help me understand how I can properly tap into the JSON response and illicit the information within the below block (e.g. the 'resource_type')?
content {
operation_groups {
operations {
action: "test"
resource_type: "compute.googleapis.com/Instance"
resource: "//compute.googleapis.com/projects/abcd/zones/us-central1-a/instances/instance-example2"
path: "/machineType"
value_matcher {
matches_pattern: ".*zones/us-central1-a/machineTypes/e2-medium"
}
}
operations {
action: "replace"
resource_type: "compute.googleapis.com/Instance"
resource: "//compute.googleapis.com/projects/abcd/zones/us-central1-a/instances/instance-example2"
path: "/machineType"
value {
string_value: "zones/us-central1-a/machineTypes/e2-small"
}
}
}
}
I took the code in your question and rewrote it to match the Python Recommender API. The primary issue is that you are accessing internal member and field names. I have changed the code to use public interfaces.
Review the type definitions for the API:
Types for Recommender API Client
GitHub Gist: Python example demonstrating the Google Cloud Compute Recommender list recommendations api
# pip install google-cloud-recommender
from google.cloud import recommender
import os
# Enter values for your Project ID and Zone
PROJECT=
LOCATION=
RECOMMENDER = 'google.compute.instance.MachineTypeRecommender'
def print_recommendations():
client = recommender.RecommenderClient()
parent = client.recommender_path(PROJECT, LOCATION, RECOMMENDER)
recommendations = client.list_recommendations(parent=parent)
for recommendation in recommendations:
print('Recommendation:')
print(' Description: ', recommendation.description)
print(' Impact Category: ', recommendation.primary_impact.category)
print(' Currency Code: ', recommendation.primary_impact.cost_projection.cost.currency_code)
print(' Units: ', recommendation.primary_impact.cost_projection.cost.units)
print(' State: ', recommendation.state_info.state)
print('')
for op_group in recommendation.content._pb.operation_groups:
for operation in op_group.operations:
print(' Operation:')
print(' Action: ', operation.action)
print(' Resource Type: ', operation.resource_type)
print(' Path: ', operation.path)
print(' Value: ', operation.value.string_value)
print('')
print_recommendations()
Example output:
Recommendation:
Description: Save cost by changing machine type from e2-standard-4 to e2-highmem-2.
Impact Category: Category.COST
Currency Code: USD
Units: -31
State: State.ACTIVE
Operation:
Action: test
Resource Type: compute.googleapis.com/Instance
Path: /machineType
Value:
Operation:
Action: replace
Resource Type: compute.googleapis.com/Instance
Path: /machineType
Value: zones/us-east1-b/machineTypes/e2-highmem-2
Given the list:
list: [
object1: {
id: 22,
name: "Tom"
},
object2: {
id: 12,
name: "Mary"
},
object3: {
id: 44,
name: "Tom"
}
]
Instead of using nested loops, is there any other one-liner that would get ONLY duplicated names from this list ? So the return list would be ["Tom"]
Another way would be to group them with a count, and find all the ones where the count is greater than one:
list.name.countBy { it }.findResults { it.value > 1 ? it.key : null }
Or indeed as #Daniel says:
list.values().name.countBy { it }.findResults { it.value > 1 ? it.key : null }
Depending on your structure...
Close to a oneliner is the below code. The idea is to create a list of unique named elements, and remove them from the original list.
def list = [
[ id: 22, name: "Tom"],
[ id: 12, name: "Mary"],
[ id: 44, name: "Tom"]
]
def unique = list.toUnique { a, b -> a.name <=> b.name }
list.removeAll(unique)
list*.name
removeAll returns booleon, so that complicates it if you need 1 line of code, the unique variable can be foldet into the method call.
Notice, .toUnique() returns a new collection .unique() removes from the collection called on.