Google Glass callbackUrl POST from Mirror API is empty? - python

Apologies because the only web development I know is of the django/python kind and am probably guilty of mixing my code idioms ( REST vs django URL dispatch workflow)
I have a URL handler which serves as a callbackUrl to a subscription for my Glassware. I am getting a POST to the handler , but the request object seems empty.
I am sure I am understanding this wrong but can someone point me in the direction of getting the "REPLY" information from a POST notification to a callbackURL.
My URL Handler is
class A600Handler(webapp2.RequestHandler):
def post(self):
"""Process the value of A600 received and return a plot"""
# I am seeing this in my logs proving that I am getting a POST when glass replies
logging.info("Received POST to logA600")
# This is returning None
my_collection = self.request.get("collection")
logging.info(my_collection)
# I also tried this but self.sequest.POST is empty '[]' and of type UnicodeMultiDict
# json_request_data = json.loads(self.request.POST)
#util.auth_required
def get(self):
"""Process the value of A600 received and return a plot"""
logging.info("Received GET to this logA600")
I have the following URL Handler defined and can verify that the post function is getting a "ping" when the user hits reply by looking at the app-engine logs.
MAIN_ROUTES = [
('/', MainHandler),('/logA600',A600Handler),
]
How do I extract the payload in the form of the voice transcribed text sent by the user?. I am not understanding The "parse_notification" example given in the docs

Did you try request.body? The docs for request.POST state
"If you need to access raw or non-form data posted in the request, access this through the HttpRequest.body attribute instead."
If the API isn't using form data in its post, you'll likely find the contents in request.body. The docs to which you linked indicate that the content will be placed as JSON in the body instead of form data ("containing a JSON request body"). I would try json.loads(request.body).

I am also having this issue of Mirror API calling my application for notifications, and those notifications are empty. My app runs on tomcat so its a java stack. All the samples process the notification like this:
BufferedReader notificationReader = new BufferedReader(
new InputStreamReader(request.getInputStream()));
String notificationString = "";
// Count the lines as a very basic way to prevent Denial of Service
// attacks
int lines = 0;
while (notificationReader.ready()) {
notificationString += notificationReader.readLine();
lines++;
// No notification would ever be this long. Something is very wrong.
if (lines > 1000) {
throw new IOException(
"Attempted to parse notification payload that was unexpectedly long.");
}
}
log.info("got raw notification " + notificationString);
For me this is always logging as empty. Since a notification url must be https, and for testing I could not use an IP address, I have setup dyndns service to point to my localhost:8080 running service. This all seems to work but I suspect how dyndns works is some type of forward or redirect here post data is removed.
How can I work around this for local development?
Updated:
Solved for me.
I found closing the response before reading request caused issue that request.inputStream was already closed. MOving this
response.setContentType("text/html");
Writer writer = response.getWriter();
writer.append("OK");
writer.close();
To after I fully read in request notification into a String solved the issue.

Related

Stripe: No signatures found matching the expected signature for payload using flask

newbie here. I'm working on the Stripe payment method using flask and it all works well on my local machine but when I deploy my code on the server and listen to webhook events in the stripe dashboard, I get this error"No signatures found matching the expected signature for payload". Already tried so many solutions but nothing worked. Any help will be appreciated.
def webhook_received(self, user_id):
payload = request.data
endpoint_secret = 'my_secret_key'
sig_header = request.headers.get('stripe-signature')
try:
event = stripe.Webhook.construct_event(
json.loads(payload), sig_header, endpoint_secret
)
data = event['data']
except Exception as e:
return str(e)
event_type = event['type']
if event_type == 'checkout.session.completed':
self.handle_checkout_session(data, user_id)
elif event_type == 'invoice.paid':
pass
Okay I think I see the problem but I'll try to cover both potential issues.
(Most Likely): Stripe requires the raw, unmodified request body to form the webhook signature. In your try: block you are using json.loads(payload) which converts it to a Python dict object. Try using the raw payload data instead.
If the problem only occurs when you deploy your code to a remote server then the most likely problem is with the endpoint_secret value. I would add some logging in your webhook_received() function to log the value after it's loaded and make sure the value matches the webhook signing secret you can view in your Stripe dashboard.
Lastly, it's important to return proper responses to avoid webhook delivery retries. I know Flask does some stuff implicitly (a pet peeve of mine) but I'm not seeing a 200 or 500 response being returned here. You'll want to make sure you respond appropriately to avoid headaches later. You can check the best practices here. There's also a handy webhook builder here so you can check your implementation against Stripe's Flask code.

flask: hashes "#" in the routing

I'm working with google API lately and use simple flask method to retrieve some id_token.
here is my code with explanations in comment:
#app.route('/afterlogin/id_token')
def afterlogin(id): # get the id
print(id) # print it
return render_template(r'creds_view.html', data=id) # and render the template with 'id' in it (for test purposes)
So what happens is that after the user logins, the api redirects the id_token to http://localhost:8000/afterlogin/#id_token=some_id_token.
but for some reason it is showing me 404 error.
i think it is because of the '#' in the url , i want the id_token. i know that '#' in html means for path linking or routing in 'href'.
so for that i tried.
#app.route('/afterlogin/<path:id>')
but the error still persists.
any guesses?
Everything after # is processed locally by the browser, it's not sent to the server, so you can't use it in routing. Leave out the #:
http://localhost:8000/afterlogin/some_id_token

Implementing Google Directory API users watch with Python

I'm having some trouble understanding and implementing the Google Directory API's users watch function and push notification system (https://developers.google.com/admin-sdk/reports/v1/guides/push#creating-notification-channels) in my Python GAE app. What I'm trying to achieve is that any user (admin) who uses my app would be able to watch user changes within his own domain.
I've verified the domain I want to use for notifications and implemented the watch request as follows:
directoryauthdecorator = OAuth2Decorator(
approval_prompt='force',
client_id='my_client_id',
client_secret='my_client_secret',
callback_path='/oauth2callback',
scope=['https://www.googleapis.com/auth/admin.directory.user'])
class PushNotifications(webapp.RequestHandler):
#directoryauthdecorator.oauth_required
def get(self):
auth_http = directoryauthdecorator.http()
service = build("admin", "directory_v1", http=auth_http)
uu_id=str(uuid.uuid4())
param={}
param['customer']='my_customer'
param['event']='add'
param['body']={'type':'web_hook','id':uu_id,'address':'https://my-domain.com/pushNotifications'}
watchUsers = service.users().watch(**param).execute()
application = webapp.WSGIApplication(
[
('/pushNotifications',PushNotifications),
(directoryauthdecorator.callback_path, directoryauthdecorator.callback_handler())],
debug=True)
Now, the receiving part is what I don't understand. When I add a user on my domain and check the app's request logs I see some activity, but there's no usable data. How should I approach this part?
Any help would be appreciated. Thanks.
The problem
It seems like there's been some confusion in implementing the handler. Your handler actually sets up the notifications channel by sending a POST request to the Reports API endpoint. As the docs say:
To set up a notification channel for messages about changes to a particular resource, send a POST request to the watch method for the resource.
source
You should only need to send this request one time to set up the channel, and the "address" parameter should be the URL on your app that will receive the notifications.
Also, it's not clear what is happening with the following code:
param={}
param['customer']='my_customer'
param['event']='add'
Are you just breaking the code in order to post it here? Or is it actually written that way in the file? You should actually preserve, as much as possible, the code that your app is running so that we can reason about it.
The solution
It seems from the docs you linked - in the "Receiving Notifications" section, that you should have code inside the "address" specified to receive notifications that will inspect the POST request body and headers on the notification push request, and then do something with that data (like store it in BigQuery or send an email to the admin, etc.)
Managed to figure it out. In the App Engine logs I noticed that each time I make a change, which is being 'watched', on my domain I get a POST request from Google's API, but with a 302 code. I discovered that this was due to the fact I had login: required configured in my app.yaml for the script, which was handling the requests and the POST request was being redirected to the login page, instead of the processing script.

Is my API a REST architekture?

I'm currently designing a webservice in Python with Flask. I now got very confused if it is a RESTful service or just a regular webservice. I've been reading quite a few sources about RESTful services, but still I'm not able to say if my service is a REST architekture or not.
The requests to my API are stateless.
Here is what I have:
from flask import Flask,request
if __name__ == "__main__":
appLogger.info("RestFul service initialized and started")
app.run(host="0.0.0.0",port=int("80"),debug=True)
#app.route('/',methods = ['POST'])
def add():
"""
This function is mapped to the POST request of the REST interface
"""
#check if a JSON object is declared in the header
if request.headers['Content-Type'] == 'application/json; charset=UTF-8' and request.data:
try:
data = json.dumps(request.json)
#check if recieved JSON object is valid according to the scheme
#if (validateJSON(data)):
try:
saveToMongo(data)
appLogger.info("Record saved to MongoDB")
return "JSON Message saved in MongoDB"
except:
appLogger.error("Could not write to MongoDB")
except:
appLogger.error("Recieved invalid JSON")
else:
appLogger.error("Content-Type not defined or empty content")
raise FailedRequest
I non of the possible responses, I'll return a json, which is actually the payload of a request. It's always a regular http-response with a custome text as a result description.
Is that right, that because of this fact, it is not a RESTful service, and if I want to call it a RESTful service, that I would need to return back a json object? Or am I completely wrong? Is my API just a simple RPC?
I see only one resource / to which a POST request can be made. There is no way to GET a collection of objects or a single object saved in this way.
One could argue that suche a trivial system does not violate any REST principle. But I think this is not enough to call a system RESTful. It is a trivial RPC system with a single anonymous 'save' method.

Django: Testing auto-generated (error) emails

I'm having some trouble testing that automated emails are being sent. Specifically, I've set the SEND_BROKEN_LINK_EMAILS to True and I am able to see the email output to console when I run the project on my localhost and attempt to visit a non-existing page, thereby generating a 404 and triggering an email.
I'm trying to write a test for it and during testing, Django uses the locmem Email Backend which should put messages in a list under django.core.mail.outbox. However that is not happening so my test fails because there are no messages in the list, despite using the client to get to a 404 page.
Using django 1.5 and I've referenced the docs, here: Django 1.5 django.core.mail.outbox
I've copied my test code below. Any help on how I can verify that an email was sent after the test client GETs a 404 response and examining that email would be incredibly helpful. Thanks!
class BadLinkEmailTest(TestCase):
def setUp(self):
self.client.login(username='user', password='pass')
def test_for_bad_link_email_sent(self):
response = self.client.get('/jibberish/')
self.assertEqual(int(response.status_code), 404)
self.assertEqual(len(mail.outbox), 1) # <<<--- RETURNS FALE, 0 != 1, meaning the list is empty and no mail was sent
I found the answer to your problem at this line, in the django code:
https://github.com/django/django/blob/stable/1.5.x/django/middleware/common.py#L114
Apparently the request must have HTTP_REFERER set for the email to be sent. This should fix your text:
class BadLinkEmailTest(TestCase):
def setUp(self):
self.client.login(username='user', password='pass')
def test_for_bad_link_email_sent(self):
extra = {
'HTTP_REFERER': 'http://somesite.com/'
}
response = self.client.get('/jibberish/', **extra)
self.assertEqual(int(response.status_code), 404)
self.assertEqual(len(mail.outbox), 1)

Categories

Resources