I want to setup a push notification for the user's calendarID per the doc.
#blueprint.route("/notifications", methods={'GET','POST'})
def timeBlocker():
email = request.json.get('email')
user = User.query.filter_by(email=email).first()
acessToken = accessToken(email, user.refresh)
body = {
'id': '01234567-89ab-cdef-0123456789ab',
'type': 'web_hook',
'address': 'https://<registeredDomain>/notifications'
}
headers = {'Authorization': 'Bearer ' + acessToken,
'Content-Type': 'application/json'
}
calendarID = '...#group.calendar.google.com'
req = requests.post(url=f'https://www.googleapis.com/calendar/v3/calendars/{calendarID}#group.calendar.google.com/events/watch', data = json.dumps(body), headers=headers)
return str(req.json())
However, I keep on getting:
{'error': {'errors': [{'domain': 'global', 'reason': 'notFound', 'message': 'Not Found'}], 'code': 404, 'message': 'Not Found'}}
which isn't meaningful. At the same time, the doc gives such code as an example:
POST https://www.googleapis.com/calendar/v3/calendars/my_calendar#gmail.com/events/watch
Authorization: Bearer auth_token_for_current_user
Content-Type: application/json
with my_calendar#gmail.com taken a calendarID when in reality the calendarIDs I've been receiving all end with #group.calendar.google.com. Thus, I am not sure if this is causing the case but keen to know how I shall fix the error.
Related
I am trying to use google push notification for calendar to be notified if a user's event's start or/and end dateTime changes.
I have followed all the steps from push doc regarding registering and verifying my domain.
My code is as follows:
#blueprint.route("/notifications", methods={'GET','POST'})
def timeBlocker():
email = '....#gmail.com'
user = User.query.filter_by(email=email).first()
thecredentials = {
'client_id': os.getenv('client_id'),
'client_secret':os.getenv('client_secret'),
'refresh_token':user.refresh,
'grant_type': 'refresh_token',
}
req = requests.post(url='https://oauth2.googleapis.com/token', data = thecredentials)
req_ = req.json()
accessToken = req_["access_token"]
print('access token is ' + accessToken)
body = {
'id': '01234567-89ab-cdef-0123456789ab',
'type': 'web_hook',
'address': 'https://dayliii.com/notifications'
}
headers = {'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
}
calendar = '....#group.calendar.google.com'
req = requests.post(url='https://www.googleapis.com/calendar/v3/calendars/....#group.calendar.google.com/events/watch', data = body, headers=headers)
return str(req.json())
Error:
{'error': {'errors': [{'domain': 'global', 'reason': 'parseError', 'message': 'Parse Error'}], 'code': 400, 'message': 'Parse Error'}}
I tried converting from double quotes to single quote as this similar post suggested yet it didn't work.
Lastly, I was curious to know if I should register & verify domain ownership of 'http://localhost:5000/' when working with push notifications in dev mode? As that's the error I am expecting to get that not sure about the way around it.
The data parameter in request() function accepts json format.
You can try converting your body variable into a json string using json.dumps().
Your request code should look like this:
req = requests.post(url='https://www.googleapis.com/calendar/v3/calendars/....#group.calendar.google.com/events/watch', data = json.dumps(body), headers=headers)
I'm trying to use GET request using httparty gem to have info about specific user from SlackAPI. From curl it works well
curl --data "token=SLACK_TOKEN&email=user#example.com" https://slack.com/api/users.lookupByEmail
But my code below seems to be broken because I received an error {"ok":false,"error":"users_not_found"}
module Slack
class GetUserId
def call
response = HTTParty.get("https://slack.com/api/users.lookupByEmail", payload: payload, headers: headers)
end
def headers
{
'Content-Type' => 'application/json',
'Authorization' => 'Bearer SLACK_TOKEN'
}
end
def payload
{
email: "user#example.com"
}.to_json
end
end
end
If you check their documentation, it seems that API do not accept JSON-form but rather "application/x-www-form-urlencoded.
So something like:
headers: {
'Authorization' => 'Bearer SLACK_TOKEN,
"Content-Type" => "application/x-www-form-urlencoded"
},
body: {
"token=SLACK_TOKEN”,
”email=user#example.com"
...
Reference: https://api.slack.com/methods/users.lookupByEmail
I am trying to send request payload(like in a post call) with Typhoeus Delete call.
As far as I know, The latest update to the HTTP 1.1 specification (RFC 7231) explicitly permits an entity body in a DELETE request:
A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request.
I tried this code, but body/payload is not retrievable
query_body = {:bodyHash => body}
request = Typhoeus::Request.new(
url,
body: JSON.dump(query_body),
method: :delete,
ssl_verifypeer: false,
ssl_verifyhost: 0,
verbose: true,
)
request.run
response = request.response
http_status = response.code
response.total_time
response.headers
result = JSON.parse(response.body)
At the other side, It comes in an encoded way, where I can not retrieve it
Other side code is like :
def destroy
respond_to do |format|
format.json do
body_hash = params[:bodyHash]
#do stuff
render json: {msg: 'User Successfully Logged out', status: 200}, status: :ok
end
format.all {render json: {msg: 'Only JSON types are supported', status: 406}.to_json, status: :ok}
end
end
Let me cite the specification:
A payload within a DELETE request message has no defined semantics;
sending a payload body on a DELETE request might cause some existing
implementations to reject the request.
I would NOT say it can be called as explicit permission to send payload with DELETE request. It tells you MAY send a payload, but the processing of such a request remains entirely at the discretion of the server.
And this is what happens:
At the other side, it comes in an encoded way, where I can not retrieve it
Why can't you send your payload as a part of POST request, which is guaranteed to be processed by the server normally?
I finally looked at all my requests in which I was payload (POST and PUT) and observed that I was not sending headers along with this DELETE request.
It looks something like this:
query_body = {:bodyHash => body}
request = Typhoeus::Request.new(
url,
body: JSON.dump(query_body),
method: :delete,
ssl_verifypeer: false,
ssl_verifyhost: 0,
verbose: true,
headers: {'X-Requested-With' => 'XMLHttpRequest', 'Content-Type' => 'application/json; charset=utf-8', 'Accept' => 'application/json, text/javascript, */*', 'enctype' => 'application/json'}
)
request.run
response = request.response
http_status = response.code
response.total_time
response.headers
result = JSON.parse(response.body)
Just adding headers to it, made it work
I am trying to refresh the access token for a user following this tutorial.
However, I am getting
{
"error":"unauthorized",
"error_description":"Full authentication is required to access this resource"
}
and I do not see what's missing.
The following is how I am constructing the oauth/refresh request in my Angular application:
refreshToken() {
this.logger.info('Attempting to refresh access token');
const headers = new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')
// CLIENT_ID:CLIENT_SECRET
.set('Authorization', 'Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=');
const payload = {
refresh_token: AuthenticationService.getRefreshToken(),
grant_type: 'refresh_token'
};
return this.http.post(environment.apiUrl + '/oauth/refresh',
payload, {headers: headers})
.pipe(map(r => r));
}
What am I missing here?
Okay, I was almost right.
First, I did use the wrong endpoint /oauth/refresh - I don't know why I thought this existed. It has to be /oauth/token.
Also payload gets send via URL parameters:
const payload = `refresh_token=${AuthenticationService.getRefreshToken()}&grant_type=refresh_token`;
So in the end I got this working with:
refreshToken() {
this.logger.info('Attempting to refresh access token');
const headers = new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')
.set('Authorization', 'Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=');
const payload = `refresh_token=${AuthenticationService.getRefreshToken()}&grant_type=refresh_token`;
return this.http.post(environment.apiUrl + '/oauth/token',
payload, {headers: headers})
.pipe(map(r => r));
}
I'm struggling to find a good example of the full set of requests necessary to send an email through the Gmail API containing an attachment larger than 10mb.
I've seen https://developers.google.com/gmail/api/v1/reference/users/messages/send and https://developers.google.com/gmail/api/guides/uploads#resumable, but there's nothing that ties it all together.
We're using the ruby client, but we're unable to complete this flow. With the following code, we get the following error trying to make the second request: Google::APIClient::ClientError: Recipient address required
The full body of the response is the following:
{"error"=>{"errors"=>[{"domain"=>"global", "reason"=>"invalidArgument", "message"=>"Recipient address required"}], "code"=>400, "message"=>"Recipient address required"}}
Here's the code used to generate the request:
raw = Base64.urlsafe_encode64 message_string
result1 = api_client.execute!(
:api_method => gmail_api.users.messages.to_h['gmail.users.messages.send'],
:parameters => {
:uploadType => 'resumable',
:userId => 'me'
},
:headers => {
'Content-Type' => 'application/json',
'X-Upload-Content-Type' => 'message/rfc822',
'X-Upload-Content-Length' => raw.bytesize.to_s
}
)
upload_id = result1.headers['x-guploader-uploadid']
result2 = api_client.execute!(
:api_method => gmail_api.users.messages.to_h['upload.gmail.users.messages.send'],
:body_object => {
:raw => raw
},
:parameters => {
:uploadType => 'resumable',
:upload_id => upload_id,
:userId => 'me'
},
:headers => {
'Content-Type' => message.content_type,
'Content-Length' => raw.bytesize.to_s
}
)
So the issue (thank you to #tholle) was that when sending attachments greater than 5mb and less than 35mb (but also works on messages without attachments), you do NOT base64 encode the body of the request, and use multipart as the uploadType. Unfortunately the docs don't mention this at all, and the error messages don't indicate that either.
Here's a working example that was able to send a 20mb attachment. Hopefully this will help anyone else who has wasted countless hours trying to figure this one out!
result = api_client.execute!(
:api_method => gmail_api.users.messages.to_h['gmail.users.messages.send'],
:body => rfc822_message_string,
:parameters => {
:uploadType => 'multipart',
:userId => 'me'
},
:headers => {
'Content-Type' => 'message/rfc822',
}
)
I am working on a JavaScript client and I finally found a way to send email using resumable method. (Thank you #Tholle and #jwg2s). Although this is in JavaScript client it should work about the same in other client too.
This is what I did:
// MIME Mail Message data. Copied form above #Tholle 's message.
let mail = [
'Content-Type: multipart/mixed; boundary="foo_bar_baz"\r\n',
"MIME-Version: 1.0\r\n",
"to: to#gmail.com\r\n",
"from: from#gmail.com\r\n",
"subject: i am subject\r\n\r\n",
"--foo_bar_baz\r\n",
'Content-Type: text/plain; charset="UTF-8"\r\n',
"MIME-Version: 1.0\r\n",
"Content-Transfer-Encoding: 7bit\r\n\r\n",
"The actual message text goes here\r\n",
"--foo_bar_baz\r\n",
"Content-Type: application/json; name=package.json\r\n",
"Content-Transfer-Encoding: base64\r\n",
"Content-Disposition: attachment; filename=package.json\r\n\r\n",
"<base64 file data. data according to content type>",
"\r\n",
"--foo_bar_baz--",
].join("");
// get resumable upload link.
let resumableURL = "";
gapi.client
.request({
path: "/upload/gmail/v1/users/me/messages/send?uploadType=resumable",
headers: {
"X-Upload-Content-Type": "message/rfc822",
},
method: "post",
})
.then(
(res) => {
resumableURL = res.headers.location;
console.log(res);
},
(err) => console.log(err)
);
// send email
gapi.client
.request({
path: resumableURL,
headers: {
"Content-Type": "message/rfc822",
},
method: "post",
body: mail,
})
.then(
(res) => {
console.log(res);
},
(err) => console.log(err)
);
To convert gapi.client.request to Fetch API call you just need to add Authorization: Bearer <access_token> to header field. I have tried using Fetch API but response were blocked due to cors error so api client like Postman should be used.
To do more with resumable upload method check documentation: Upload Attachment