How to mock Athena query results values with Moto3 for a specific table? - python-3.x

I am using pytest and moto3 to test some code similar to this:
response = athena_client.start_query_execution(
QueryString='SELECT * FROM xyz',
QueryExecutionContext={'Database': myDb},
ResultConfiguration={'OutputLocation': someLocation},
WorkGroup=myWG
)
execution_id = response['QueryExecutionId']
if response['QueryExecution']['Status']['State'] == 'SUCCEEDED':
response = athena_client.get_query_results(
QueryExecutionId=execution_id
)
results = response['ResultSet']['Rows']
...etc
In my test I need that the values from results = response['ResultSet']['Rows'] are controlled by the test. I am using some code like this:
backend = athena_backends[DEFAULT_ACCOUNT_ID]["us-east-1"]
rows = [{"Data": [{"VarCharValue": "xyz"}]}, {"Data": [{"VarCharValue": ...}, etc]}]
column_info = [
{
"CatalogName": "string",
"SchemaName": "string",
"TableName": "xyz",
"Name": "string",
"Label": "string",
"Type": "string",
"Precision": 123,
"Scale": 123,
"Nullable": "NOT_NULL",
"CaseSensitive": True,
}
]
results = QueryResults(rows=rows, column_info=column_info)
backend.query_results[NEEDED_QUERY_EXECUTION_ID] = results
but that is not working as I guess NEEDED_QUERY_EXECUTION_ID is not known before from the test. How can I control it?
UPDATE
Based on suggestion I tried to use:
results = QueryResults(rows=rows, column_info=column_info)
d = defaultdict(lambda: results.to_dict())
backend.query_results = d
to force a return of values, but it seems not working as from the moto3's models.AthenaBackend.get_query_results, I have this code:
results = (
self.query_results[exec_id]
if exec_id in self.query_results
else QueryResults(rows=[], column_info=[])
)
return results
which will fail as the if condition won't be satifsfied.

Extending the solution of the defaultdict, you could create a custom dictionary that contains all execution_ids, and always returns the same object:
class QueryDict(dict):
def __contains__(self, item):
return True
def __getitem__(self, item):
rows = [{"Data": [{"VarCharValue": "xyz"}]}, {"Data": [{"VarCharValue": "..."}]}]
column_info = [
{
"CatalogName": "string",
"SchemaName": "string",
"TableName": "xyz",
"Name": "string",
"Label": "string",
"Type": "string",
"Precision": 123,
"Scale": 123,
"Nullable": "NOT_NULL",
"CaseSensitive": True,
}
]
return QueryResults(rows=rows, column_info=column_info)
backend = athena_backends[DEFAULT_ACCOUNT_ID]["us-east-1"]
backend.query_results = QueryDict()

An alternative solution to using custom dictionaries would to be seed Moto.
Seeding Moto ensures that it will always generate the same 'random' identifiers, which means you always know what the value of NEEDED_QUERY_EXECUTION_ID is going to be.
backend = athena_backends[DEFAULT_ACCOUNT_ID]["us-east-1"]
rows = [{"Data": [{"VarCharValue": "xyz"}]}, {"Data": [{"VarCharValue": "..."}]}]
column_info = [...]
results = QueryResults(rows=rows, column_info=column_info)
backend.query_results["bdd640fb-0667-4ad1-9c80-317fa3b1799d"] = results
import requests
requests.post("http://motoapi.amazonaws.com/moto-api/seed?a=42")
# Test - the execution id will always be the same because we just seeded Moto
execution_id = athena_client.start_query_execution(...)
Documentation on seeding Moto can be found here: http://docs.getmoto.org/en/latest/docs/configuration/recorder/index.html#deterministic-identifiers
(It only talks about seeding Moto in the context of recording/replaying requests, but the functionality can be used on it's own.)

Related

How to extract two value from json under one condition and randomize it in jmeter?

Below is the server response in a JSON format which I need to work on, In this JSON some of the objects I need to pass in the next request, which I am successfully able to do for the first occurrence, but the problem is coming on randomization in the below JSON
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sword of Honour",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "test_title",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Sword of Honour",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "India",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}
if I apply $..book[0][?(#.title == 'Sword of Honour')] condition I am seeing successful output
test_1={"category":"reference","title":"Sword of Honour","author":"Nigel Rees","price":8.95}
test_2={"category":"fiction","title":"Sword of Honour","author":"Herman Melville","price":8.99,"isbn":"0-553-21311-3"}
if I apply $..book[0][?(#.title == 'Sword of Honour')].author condition I am seeing successful output
test_1=Nigel Rees
test_2=Herman Melville
But I want to extract both author's name and price for the book that has the title == 'Sword of Honour'
Actual output = able to retrieve one value with the condition $..book[0][?(#.title == 'Sword of Honour')].author
Expected output = I want to receive two values from the same list randomly. and it should look like this
Iteration 1-
test_1={"author":"Nigel Rees","price":8.95}
Iteration 2-
test_2={"author":"Herman Melville","price":8.99,}
Iteration 3-
test_2={"author":"Herman Melville","price":8.99,}
I had tried with the V function as well but got only one value. (book{__V([?(#.title == 'Sword of Honour')]_${__Random(1,${title_matchNr},)},)})
Add JSR223 PostProcessor as a child of the request which returns the above JSON
Put the following code into "Script" area:
def swords = new groovy.json.JsonSlurper().parse(prev.getResponseData()).store.book.findAll { book -> book.title == 'Sword of Honour' }
swords.eachWithIndex { def sword, int index ->
def payload = [:]
payload.put('author', sword.author)
payload.put('price', sword.price)
vars.put('test_' + (index + 1), new groovy.json.JsonBuilder(payload).toPrettyString())
}
vars.put('test_matchNr', swords.size() as String)
Now you should be able to refer random author/price pair using __V() and __Random() functions combination like: ${__V(test_${__Random(1,${test_matchNr},)},)}
More information:
Apache Groovy - Parsing and producing JSON
Apache Groovy: What Is Groovy Used For?

I am prepared permit EIP712, but tx "Reverted", function selector 'Reverted 0x08c379a

I'm trying to send a permit for a transaction, but I get a function call mismatch return if I call through the smartcontract token function. If I call via encodeABI and add the same arguments, I don't see the message in the transaction. I use the following code
def build_permit(owner, spender, value, deadline, web3):
data = {
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"},
],
"Permit": [
{"name": "owner", "type": "address"},
{"name": "spender", "type": "address"},
{"name": "value", "type": "uint256"},
{"name": "nonce", "type": "uint256"},
{"name": "deadline", "type": "uint256"},
],
},
"domain": {
"name": "TestToken",
"version": "1",
"chainId": 4,
"verifyingContract": address_token,
},
"primaryType": "Permit",
"message": {
"owner": owner,
"spender": spender,
"value": value,
"nonce": web3.eth.getTransactionCount(my_account.address),
"deadline": deadline,
},
}
return encode_structured_data(data)
def test_permit():
signer = my_account
owner = my_account.address
holder = signer.address
address_token = SC.USDT("RINKEBY")
spender = SC.ROUTER('RINKEBY')
deadline = int(time.time()) + 3600
#print(deadline)
amount = web3.toWei(1, 'ether')
usdt = token_contract(address_token,web3n(RINKEBY))
permit = build_permit(owner, spender, amount, deadline,web3n(RINKEBY))
signed = signer.sign_message(permit)
tx = usdt.functions.permit(holder, spender, 1000000000000000000, int(time.time()) + 3600, signed.v, Web3.toBytes(signed.r), Web3.toBytes(signed.s)).call()
Why does the beginning of the data start with a different function and how do I get the correct signature?
../PycharmProjects/1/venv/lib/python3.9/site-packages/web3/contract.py:957: in call
return call_contract_function(
../PycharmProjects/1/venv/lib/python3.9/site-packages/web3/contract.py:1501: in call_contract_function
return_data = web3.eth.call(
../PycharmProjects/1/venv/lib/python3.9/site-packages/web3/module.py:57: in caller
result = w3.manager.request_blocking(method_str,
../PycharmProjects/1/venv/lib/python3.9/site-packages/web3/manager.py:198: in request_blocking
return self.formatted_response(response,
../PycharmProjects/1/venv/lib/python3.9/site-packages/web3/manager.py:170: in formatted_response
apply_error_formatters(error_formatters, response)
../PycharmProjects/1/venv/lib/python3.9/site-packages/web3/manager.py:70: in apply_error_formatters
formatted_resp = pipe(response, error_formatters)
cytoolz/functoolz.pyx:667: in cytoolz.functoolz.pipe
???
cytoolz/functoolz.pyx:642: in cytoolz.functoolz.c_pipe
???
response = {'error': {'code': 3, 'data': '0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000...207369676e61747572650000', 'message': 'execution reverted: ERC20Permit: invalid signature'}, 'id': 1, 'jsonrpc': '2.0'}
def raise_solidity_error_on_revert(response: RPCResponse) -> RPCResponse:
"""
Reverts contain a `data` attribute with the following layout:
"Reverted "
Function selector for Error(string): 08c379a (4 bytes)
Data offset: 32 (32 bytes)
String length (32 bytes)
Reason string (padded, use string length from above to get meaningful part)
See also https://solidity.readthedocs.io/en/v0.6.3/control-structures.html#revert
"""
if not isinstance(response['error'], dict):
raise ValueError('Error expected to be a dict')
data = response['error'].get('data', '')
# Ganache case:
if isinstance(data, dict) and response['error'].get('message'):
raise ContractLogicError(f'execution reverted: {response["error"]["message"]}')
# Parity/OpenEthereum case:
if data.startswith('Reverted '):
# "Reverted", function selector and offset are always the same for revert errors
prefix = 'Reverted 0x08c379a00000000000000000000000000000000000000000000000000000000000000020' # noqa: 501
if not data.startswith(prefix):
raise ContractLogicError('execution reverted')
reason_length = int(data[len(prefix):len(prefix) + 64], 16)
reason = data[len(prefix) + 64:len(prefix) + 64 + reason_length * 2]
raise ContractLogicError(f'execution reverted: {bytes.fromhex(reason).decode("utf8")}')
# Geth case:
if 'message' in response['error'] and response['error'].get('code', '') == 3:
> raise ContractLogicError(response['error']['message'])
E web3.exceptions.ContractLogicError: execution reverted: ERC20Permit: invalid signature
../PycharmProjects/1/venv/lib/python3.9/site-packages/web3/_utils/method_formatters.py:576: ContractLogicError

How to insert a python dictionary data to google cloud bigquery

I am trying to insert a python dictionary data to bigbuery.
The below is the data I use
data = {
'columnID':'123156',
'deviceID':'156',
'describle':{
'name':'car',
'freq':'10',
'period':'3',
}
}
I also define the bigquery table schema below
table_schema = {
'fields':[
{'name':'columnID', 'type':'STRING', 'mode':'REQUIRED'},
{'name':'deviceID', 'type':'STRING', 'mode':'REQUIRED'},
{'name':'describle', 'type':'RECORD', 'mode':'NULLABLE', 'fields':[
{'name':'name', 'type':'STRING', 'mode':'NULLABLE'},
{'name':'freq', 'type':'STRING', 'mode':'NULLABLE'},
{'name':'period', 'type':'STRING', 'mode':'NULLABLE'}]
},
]
}
It seems can not insert the data into bigquery table, anyone has an idea about this?
I've tested in my machine and it worked. Try the following script please.
from google.cloud import bigquery
PROJECT_ID = "your-project"
DATASET_ID = "your_dataset"
TABLE_ID = "your_table"
client = bigquery.Client()
# 1) create table
schema = [
bigquery.SchemaField("columnID", "STRING", mode="REQUIRED"),
bigquery.SchemaField("deviceID", "INTEGER", mode="REQUIRED"),
bigquery.SchemaField(
"describle",
"RECORD",
mode="NULLABLE",
fields=[
bigquery.SchemaField("name", "STRING", mode="NULLABLE"),
bigquery.SchemaField("freq", "STRING", mode="NULLABLE"),
bigquery.SchemaField("period", "STRING", mode="NULLABLE"),
],
),
]
table = bigquery.Table(f"{PROJECT_ID}.{DATASET_ID}.{TABLE_ID}", schema=schema)
table = client.create_table(table)
print(
"Created table {}.{}.{}".format(
table.project, table.dataset_id, table.table_id
)
)
# 2) insert data
rows_to_insert = [
{
"columnID": "123156",
"deviceID": "156",
"describle": {
"name": "car",
"freq": "10",
"period": "3",
},
}
]
errors = client.insert_rows_json(
f"{PROJECT_ID}.{DATASET_ID}.{TABLE_ID}", rows_to_insert
)
if errors == []:
print("New rows have been added.")
else:
print("Encountered errors while inserting rows: {}".format(errors))

How can I return all nested objects using python?

I wrote an Elastic query which will check the condition (status="APPROVED") and Gets all approved_by objects.
This is my index (portfolio):
{
"settings": {},
"mappings": {
"portfolio": {
"properties": {
"status": {
"type": "keyword",
"normalizer": "lcase_ascii_normalizer"
},
"archived_at": {
"type": "date"
},
"approved_by": {
"id": "text",
"name":"text"
}
}
}
}
}
Currently I have 60 objects whose status are approved , so when i run the query it will show 60 objects,but in my case i am getting only one object(I debugged the code, total 60 objects are coming as expected, but still returning only single object), please help guys.
My query:
profiles = client.search(index='portfolio', doc_type='portfolio',
scroll='10m', size=1000,
body={
"query": {"match": {"status": "APPROVED"}}
})
sid = profiles['_scroll_id']
scroll_size = len(profiles['hits']['hits'])
while scroll_size > 0:
for info in profiles['hits']['hits']:
item = info['_source']
approved_by_obj = item.get('approved_by')
if approved_by_obj:
return (jsonify({"approved_by": approved_by_obj}))
Expected o/p format:
{
"approved_by": {
"id": "system",
"name": "system"
}
}
You're getting only one result because you're returning from your loop, effectively breaking out of it completely.
So, instead of returning from it, append the found approved_by_object to a list of your choice and then return that 60-member list:
profiles = client.search(index='portfolio', doc_type='portfolio',
scroll='10m', size=1000,
body={
"query": {"match": {"status": "APPROVED"}}
})
sid = profiles['_scroll_id']
scroll_size = len(profiles['hits']['hits'])
approved_hits_sources = [] # <-- add this
while scroll_size > 0:
for info in profiles['hits']['hits']:
item = info['_source']
approved_by_obj = item.get('approved_by')
if approved_by_obj:
approved_hits_sources.append({"approved_by": approved_by_obj}) # <--- append and not return
return jsonify({"approved_hits_sources": approved_hits_sources})

Filter Out Data in Json

I have the below json file, where i need to filter the City data based on flag value equals to true
"attributes": { "State":
[ { "type": "sc", "ov": true, "value": "TN" } ],
"City": [ { "type": "c", "flag": true, "value": "Chennai" },
{ "type": "c", "flag": false, "value": "Coimbatore" } ],
}
Expecting the output as below
State: TN
City: Chennai
you can write something like below to only filter city based on flag is True.
import json
data = json.loads(open("/home/imtiaz/tmp/data1.json").read())
data1 = [city for city in data['attributes']['City'] if city['flag'] is True]
data['attributes']['City'] = data1
Just load the json file into memory, then use a list comprehension to filter for where the flag is true.
import json
with open('yourfile.json', 'r') as citydata:
cities_dict = json.load(citydata)
true_cities = [city for city in cities_dict['attributes']['City'] if city['flag']]
This won't mutate the original data, and will return a separate list of cities where the flag is true. You can just set the same list to the list comprehension's return value to mutate the original data in memory, such as:
cities_dict['attributes']['City'] = [city for city in cities_dict['attributes']['City'] if city['flag']]

Resources