When using the python EventHubConsumerClient to read data from an Azure EventHub. The receive (and receive_batch) function return an EventData object. Normally you could just use the body_as_json() or body_as_str() function to return the body of the object.
But if you body is compressed these methods will return an exception.
One way to get the byte array of the event body is to turn the body property of the EventData object into a list and access the first element, like this.
bytearray = list(msg.body)[0]
zlib.decompress(bytearray)
I know this is very hacky, but I have not found any other way to receive the raw body. I hope this help somebody out there and if you know of a better way, feel free to share!
Related
I'm using Python with the Django Rest framework and am trying to receive webhook events correctly from stripe.
However I constantly get this error:
stripe.error.SignatureVerificationError: No signatures found matching the expected signature for payload
This is the code:
WEBHOOK_SECRET = settings.STRIPE_WEBHOOK_SK
#csrf_exempt
def webhook(request):
sig_header = request.headers.get('Stripe-Signature', None)
payload = request.body
try:
event = stripe.Webhook.construct_event(
payload=payload,
sig_header=sig_header,
secret=WEBHOOK_SECRET
)
except ValueError as e:
raise e
except stripe.error.SignatureVerificationError as e:
raise e
return HttpResponse(status=200)
I have also tried modifying the request body format like so:
payload = request.body.decode('utf-8')
# and also
payload = json.loads(request.body)
And yet no luck.
The error is coming from the verify_header() class method inside the WebhookSignature class.
This is the part of the method where it fails:
if not any(util.secure_compare(expected_sig, s) for s in signatures):
raise error.SignatureVerificationError(
"No signatures found matching the expected signature for payload",
header,
payload,
)
So I printed out exptected_sig and signatures before this line and found that regardless of what format request.body is in, signatures is always there (which is good), but they never match the signature from the header.
Why is this?
When Stripe calculates the signature for the Event it sends you, it uses a specific "payload" representing the entire Event's content. The signature is done on that exact payload and any change to it such as adding a new line, removing a space or changing the order of the properties will change the payload and the corresponding signature.
When you verify the signature, you need to make sure that you pass the exact raw payload that Stripe sent you, otherwise the signature you calculate won't match the Stripe one.
Frameworks can sometimes try to be helpful when receiving a request and they detect JSON and automatically parse it for you. This means that you think you are getting the "raw payload/body" but really you get an alternate version. It has the same content but it doesn't match what Stripe sent you.
This is fairly common with Express in Node.js for example. So, as the developer, you have to explicitly request the exact raw/original payload Stripe sent you. And how to do this can differ based on a variety of factors. There are 2 issues on the stripe-node github with numerous potential fixes here and here.
With Django, the same can happen and you need to make sure that your code requests the raw payload. You seem to use request.body as expected but that's one thing you want to dig into further.
Additionally, another common mistake is using the wrong Webhook secret. If you use the Stripe CLI for example, it creates a new secret for you that is different from the one you see in the Dashboard for this Webhook Endpoint. You need to make sure you use the correct secret based on the environment you're in.
I want to write a list of alert policies in a json file in cloud storage. I have the script below:
def my_function(request):
alert_client = monitoring_v3.AlertPolicyServiceClient()
storage_client = storage.Client()
project_name = 'projects/my_project'
bucket_name = 'test'
policies = alert_client.list_alert_policies(name=project_name)
for policy in policies:
print(policy)
destination_blob_name = 'test'
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(destination_blob_name)
blob.upload_from_string("{}".format(json.dumps(policy)))
This script is not working and returns me the following error: TypeError: Object of type AlertPolicy is not JSON serializable"
Now a couple of things:
Using the API explorer or looking at this documentation the response from the list method should be easy to handle. However I'm writing my cloud functions in Python and It seems that the response is different.
I understand that there is something around the way pagination is handle but I don't understand how to deal with it.
I can print(policies) but the log output is kind of weird with a line for each element of the json object. Why is that? What does it mean?
How can I handle this response? Is there a generic approach here or is this specific to the API?
Still I'm able to access each variable independently policy.name, policy.conditions etc... does it means that I have to rebuild the json object I want manually?
According to the googleapis documentation of the alert policy service, iterating over the list of alert policies using list_alert_policies() automatically resolves subsequent pages of the response. You should not worry about implementing pagination logic according to the documentation:
Returns: The protocol for the ListAlertPolicies response. Iterating over this object will yield results and resolve additional pages automatically. source
As for the type AlertPolicy, it does not appear to natively be able to convert to JSON. You might have to build the JSON objects by calling on the respective properties of the AlertPolicy objects that are returned, or you can also implement something similar to this ProtoEncoder class which appears to return JSON from AlertPolicy types. As for the available properties on the AlertPolicy objects, here is the source.
class ProtoEncoder(json.JSONEncoder):
"""Encode protobufs as json."""
def default(self, obj):
if type(obj) in (monitoring_v3.AlertPolicy, monitoring_v3.NotificationChannel):
text = proto.Message.to_json(obj)
return json.loads(text)
return super(ProtoEncoder, self).default(obj)
The following works for json whose outermost container is an object like { ... }
#service.json
def index():
data = request.vars
#fields are now accessible via data["fieldname"] or data.fieldname
#processing must be done to turn Storage object into dict()
return data_as_dict
If you post a list however, it does not work
POST:
[
{"test": 1}
]
data will be an empty Storage object and data[0] will be None
The workaround is simple:
#service.json #so output is still returned as json
def index():
data = json.loads(request.body.read())
return data
data is now a dict in cases of object style JSON (easier to work with than a Storage object imo) and a native list when the JSON is a list.
My question is why is this not the default behaviour? Why should a JSON service not accept valid JSON?
The #service.json decorator simply registers a function so it can be accessed via a controller that returns a Service object. The decorator ensures that the service controller returns a JSON response when the decorated function is called, but it does nothing regarding the processing of JSON input.
In any case, your problem is not with the #service.json decorator but with a misunderstanding regarding request.vars. request.vars is a dictionary-like object that is populated with keys and values from the query string and/or the request body (if the request body includes form variables or a JSON object of keys and values). It is not intended to simply be a copy of any arbitrary data structure that is posted in the request body. So, if you post a JSON array in the request body, it would not make sense to copy that array to request.vars, as it is not the appropriate type of data structure. If you want to post a JSON array, the correct way to process it is to read the request body, as you have done.
Also, note that because your index function does not take any arguments and therefore does not take advantage of the #service decorator's ability to map parameters from the HTTP request into function arguments, you could simplify your code by foregoing the #service decorator and accessing the index function more directly:
def index():
data = json.loads(request.body.read())
return data
Assuming index is in the default.py controller, you could post JSON to /yourapp/default/index.json (note the .json extension), and you will get back a JSON response.
I'm working on an api with django rest framework, and I'm writing some unit tests to check critical operations. I'm trying to read the contents of a file after doing a get request
downloaded_file = self.client.get(textfile)
The problem is that the object returned is of type: django.http.response.FileResponse which inherits from StreamingHttpResponse.
I'm trying to iterate over the streaming_content attribute, which supposedly is an iterator, but I cannot iterate, no method next().
I inspected this object and what i get is map object. Any ideas on how to get the content from this request?
Edit:
Problem Solved
The returned object is a map, a map takes a function and a iterable:
https://docs.python.org/3.4/library/functions.html#map
What I had to do was to cast the map to a list, access the first element of the list and convert from bytes to string. Not very elegant but it works.
list(response.streaming_content)[0].decode("utf-8")
Here is how you extract the content from a streaming_content map:
content = downloaded_file.getvalue()
Looking at the code of the getvalue() method, we see that it just iterates over the answer content:
class StreamingHttpResponse(HttpResponseBase):
...
def getvalue(self):
return b''.join(self.streaming_content)
The HTTPRequest class in the tornado* web framework helpfully maps GET and POST arguments to lists. I understand why -- in case a given argument name is used multiple times. But for some RequestHandlers, this is a pain. For instance, if I want to pass a json object and parse it as-is on the server.
What's the most straightforward way to disable the map-to-list behavior so that I can send unaltered json to a tornado/cyclone server?
*Cyclone, actually, in case there's an implementation difference here.
Instead of accessing self.request.arguments directly you should use the accessor functions:
self.get_argument("ID", default=None, strip=False)
This returns a single item.
If you want to turn the arguments into a JSON object you can quite easily do so:
json.dumps({ k: self.get_argument(k) for k in self.request.arguments })
I'm going to go with "you're out of luck." You could re-write the class in question (looks like that would not be fun), but aside from that I don't see many options.
I would just use a dict comprehension.
{k:''.join(v) for k,v in self.request.arguments.iteritems()}