I am trying to send a en email with an attachment via the GmailV1 API. However it just isn't working due to Missing Draft Message errors.
According to RubyDoc I tried to create a draft message as follows:
The GmailV1:GmailService.create_user_draft() methods takes in an identifier and a draft_object (accepting 'me' for the authorized user). A draft object (Google::Apis::GmailV1::Draft) takes a message in the form of Google::Apis::GmailV1::Message which in turn takes a payload in the form of Google::Apis::GmailV1::MessagePart which has the desired filename method.
So I ran this code:
##assume client is an authorized instance of Google::Apis::GmailV1:GmailService
msg_part = Google::Apis::GmailV1::MessagePart.new(filename: 'path/to/file')
msg = Google::Apis::GmailV1::Message.new(payload: msg_part)
draft = Google::Apis::GmailV1::Draft.new(message: msg)
client.create_user_draft('me', draft)
>> Google::Apis::ClientError: invalidArgument: Missing draft message
How Come?
Versions:
google-api-client 0.9.9
googleauth 0.5.1
ruby 2.3.1p112
Using the GmailService class as described here I was able to save a draft using the code below. I think the key is that the raw keyword is required in the message.
result = service.create_user_draft(
user_id,
Google::Apis::GmailV1::Draft.new(
:message => Google::Apis::GmailV1::Message.new(
:raw => "To: test#test.com\r\nSubject: Test Message\r\n\r\nTest Body"
)
)
)
I solved this problem creating first a Mail object with the 'mail' gem in this way:
require 'mail'
mail = Mail.new
mail['from'] = 'pippo#pluto.it'
mail[:to] = 'me#mymail.it'
mail.subject = 'This is a test email'
mail.body 'this is the body'
mail.add_file("./path/to/file")
#... and other ...
then i converted this in a raw object:
raw_message = mail.to_s
then i create gmail message with this raw:
message = Google::Apis::GmailV1::Message.new(
:raw => raw_message
)
and finnaly:
draft = Google::Apis::GmailV1::Draft.new(message: message)
gmail.create_user_draft('me', draft)
Related
So this is related to an earlier post I made on this method. This is essentially what I am using to send files via hipchat:
#!/usr/bin/env ruby
require 'hipchat'
client = HipChat::Client.new('HIPCHAT_TOKEN', :api_version => 'v2', :server_url => 'HIPCHAT_URL')
client.user('some_username').send_file('message', File.open('./output/some-file.csv') )
client['some_hipchat_room'].send_file('some_user', 'message', File.open('./output/some-file.csv') )
Now for some reason the send_file method is invalid:
/path/to/gems/hipchat-1.5.4/lib/hipchat/errors.rb:40:in `response_code_to_exception_for': You requested an invalid method. path:https://hipchat.illum.io/v2/user/myuser#myemail/share/file?auth_token=asdfgibberishasdf method:Net::HTTP::Get (HipChat::MethodNotAllowed)
from /path/to/gems/gems/hipchat-1.5.4/lib/hipchat/user.rb:50:in `send_file'
I think this indicating that you should be using POST instead of GET, but I'm not sure because I haven't used this library nor Hipchat.
Looking at the question you referenced and the source posted by another user they're sending the request using self.class.post, and your debug output shows Net::HTTP::Get
To debug, could you try,
file = Tempfile.new('foo').tap do |f|
f.write("the content")
f.rewind
end
user = client.user(some_username)
user.send_file('some bytes', file)
The issue is that I was attempting to connect to the server via http instead of https. If the following client is causing issues:
client = HipChat::Client.new('HIPCHAT_TOKEN', :api_version => 'v2', :server_url => 'my.company.com')
Then try adding https:// to the beginning of your company's name.
client = HipChat::Client.new('HIPCHAT_TOKEN', :api_version => 'v2', :server_url => 'https://my.company.com')
Does anyone have a simple example as to how to send an email from scratch with the v0.9 API.
simply want an example of sending the following:
m = Mail.new(
to: "test1#test.com",
from: "test2#test.com",
subject: "Test Subject",
body:"Test Body")
Now to create that message object which is required to send,we can use:
msg = Base64.urlsafe_encode64 m.to_s
And then try to send (where message_object = msg):
client = Google::Apis::GmailV1::GmailService.new #Appropriately authorised
client.send_user_message("me", message_object)
The client wants an RFC822 compatible encoded string, which the above should be.
I've tried:
message_object = msg
=> Google::Apis::ClientError: invalidArgument: 'raw' RFC822 payload message string or uploading message via /upload/* URL required
message_object = raw:msg
=>ArgumentError: unknown keyword: raw
message_object = {raw:msg}
=>ArgumentError: unknown keyword: raw
message_object = Google::Apis::GmailV1::Message.new(raw:msg)
=> #<Google::Apis::GmailV1::Message:0x007f9158e5b4b0 #id="15800cd7178d69a4", #thread_id="15800cd7178d69a4">
#But then I get Bounce <nobody#gmail.com> - An error occurred. Your message was not sent.
i.e. None of them work...
Sending the basic encded string (msg above) through the Gmail API interface tester here works.
I'm obviously missing something obvious here as to how to construct that object required to make it work through the API.
Ok. So the answer... thanks for all your help Holger...
Was that the documentation is wrong. It asks you to encode to base64.
The base64 encoding is not required (it is done internally by the api client).
The correct way to send is
msg = m.encoded
# or m.to_s
# this doesn't base64 encode. It just turns the Mail::Message object into an appropriate string.
message_object = Google::Apis::GmailV1::Message.new(raw:m.to_s)
client.send_user_message("me", message_object)
Hope that saves someone else from being patronised by an overzealous mod.
Carpela‘s answer works fine, but for the message_object, it's missing "raw" in Message.new. The correct code should as following:
message_object = Google::Apis::GmailV1::Message.new(raw: m.encoded) # or m.to_s
client.send_user_message('me', message_object)
Working through sending gmail with the newer google-api-ruby-client in a rails 4 application.
require 'google/apis/gmail_v1'
Gmail = Google::Apis::GmailV1
class MailService
def initialize(params)
#params = params
end
def call
message = Gmail::Message.new
service = Gmail::GmailService.new
message.raw = (redacted)
service.request_options.authorization = current_user.token.fresh_token
result = service.send_user_message(current_user.email, message)
end
end
And this is the result from the call to the API:
Sending HTTP post https://www.googleapis.com/gmail/v1/users/me/messages/send?
200
#<Hurley::Response POST https://www.googleapis.com/gmail/v1/users/me/messages/send == 200 (63 bytes) 858ms>
Success - #<Google::Apis::GmailV1::Message:0x007fc9cf9b52dd
#id="15096369c05cdb1d",
#thread_id="15096369c05cdb1d">
The raw message sends without issue from the API explorer but when executed from my application I get a bounce email in my inbox. In the above example the redacted sample is a valid RFC 2822 formatted base-64 url safe string and fresh_token represents the oauth2 access token for the current user.
A look at the bounced mail
Bounce <nobody#gmail.com>
2:43 PM (19 minutes ago)
to me
An error occurred. Your message was not sent.
Anyone have any thoughts? It seems like perhaps my (sender) email is being picked up in the raw message but not the recipient... Though I suppose the API could be forwarding the bounce based on my oauth access token.
I very much appreciate any help. Thanks!
EDIT: Solution was to pass the RFC 2822 string as raw property without base64 encoding.
Steve Bazyl seems to be correct. The documentation on send_user_message is wrong as of (0.9.13). For raw, it says: "The entire email message in an RFC 2822 formatted and base64url encoded string. Returned in messages.get and drafts.get responses when the format=RAW parameter is supplied. Corresponds to the JSON property raw." As far as I can tell, this is simply incorrect.
I encountered this issue when updating from google-api-client 0.8 to 0.9 and removing the base64 encoding solved the problem. I.e. call in 0.8:
response = #service.execute(
api_method: api.users.messages.to_h['gmail.users.messages.send'],
body_object: {
raw: Base64.urlsafe_encode64(mail.to_s)
},
parameters: {
userId: 'me',
}
)
became
message = { raw: mail.to_s }
res = #service.send_user_message('me', message, {})
in 0.9.
Reported as https://github.com/google/google-api-ruby-client/issues/474.
In the soap response below (from SoapUI), under the parent SearchForReservationResponse tag, I am trying to pull out the values of Reservation id, Restaurant id and Location id with Savon 2.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<SearchForReservationResponse xmlns="http://schemas.livebookings.net/OneFormat/Aggregator/Internal/1/0/">
<Reservation id="34639536" status="Confirmed">
<DiningDateAndTime>2015-07-01T17:00:00</DiningDateAndTime>
<Restaurant id="25200">
<Name>Eat Food - UK Demo Website - Bookatable.com</Name>
<Location id="35839">
<Name>Bar</Name>
</Location>
</Restaurant>
<Size>2</Size>
<Created>2015-07-01T13:22:17.41</Created>
<SessionId>DINNER</SessionId>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
<ConfirmationNumber>JWRW5HR5</ConfirmationNumber>
<AllowedToCancelOnline>true</AllowedToCancelOnline>
<RestaurantPhoneNumber type="Main">+44 7951300529</RestaurantPhoneNumber>
</Reservation>
</SearchForReservationResponse>
</soap:Body>
</soap:Envelope>
Below is my attempt for trying to access Reservation id. After a lot of googling I found that the new Savon 2 syntax uses #attrib, but I keep getting errors as I think I am not using this Ruby nested hash syntax correctly - I find it very confusing and am fairly new to Ruby. If you could help me out here it would be much appreciated!
require 'savon'
class SearchReservation
attr_reader :reservation_id
def client
client = Savon.client(wsdl: "http://example-wsdl-url", follow_redirects: :follow_redirects)
end
def main_method(confirm_number, email)
message = {'ConfirmationNumber' => "JWRW5HR5", 'EMail' => "jon#" }
response = client.call(:search_for_reservation, message: message)
data = response.body(:search_for_reservation_response => { #attrib => {:reservation => :id} })
if data
#reservation_id = data[:id]
end
end
end
search = SearchReservation.new
puts search.main_method("JWRW5HR5", "jon#")
N.B. the email value jon# doesn't have to be a valid email address (used just for testing purposes) - it returns a valid response in SoapUI.
My last syntax error trace in the terminal/console:
/~/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/savon-2.11.1/lib/savon/response.rb:36:in `body': wrong number of arguments (1 for 0) (ArgumentError)
from search.rb:13:in `main_method'
from search.rb:22:in `<main>'
My final solution pulling out all the values I need. Of note: even though dining_date_and_time was presented as xml when putting response.body, when it is extracted using #dining_date_and_time = data[:dining_date_and_time] the date and time values get printed cleanly. This is key once porting to a fully-fledged Rails app.
Despite all the discussion online about attributes, ids within an xml tag can be pulled by just ensuring you are far enough down the "tree" (ie. location id falls under location tag) and then specifying the key: in this case the api displays it as :#id for each nested case.
N.B, this only returns the last value called each time in console. But all values should be pulled in views once integrated into Rails. (Hopefully!)
require 'savon'
class SearchClass
def client
client = Savon.client(wsdl: "http://wsdl-example-url", follow_redirects: :follow_redirects)
end
def return_data(confirm_number, email)
message = {'ConfirmationNumber' => confirm_number, 'EMail' => email }
response = client.call(:search_for_reservation, message: message)
data = response.to_hash[:search_for_reservation_response][:reservation]
#reservation_id = data[:#id]
#dining_date_and_time = data[:dining_date_and_time]
#size = data[:size]
#session_id = data[:session_id]
#first_name = data[:first_name]
#last_name = data[:last_name]
#confirm_number = data[:confirmation_number]
#allowed_to_cancel_online = data[:allowed_to_cancel_online]
#restaurant_phone_number = data[:restaurant_phone_number]
data2 = response.to_hash[:search_for_reservation_response][:reservation][:restaurant]
#restaurant_id = data2[:#id]
#restaurant_name = data2[:name]
data3 = response.to_hash[:search_for_reservation_response][:reservation][:restaurant][:location]
#location_id = data3[:#id]
#location_name = data3[:name]
end
end
search = SearchClass.new
puts search.return_data("JWRW5HR5", "jon#")
My first Ruby adventure is to try and get oauth2 token for web service app from google. There are many questions and answers on this topic, but none that resolved my issue.
I registered a dummy web service with google and am trying to get authorization code (even before trying to get a token). Doesn't matter what mandatory parameter I omit (e.g. client_id) when accessing https://accounts.google.com/o/oauth2/auth I always get 400 error with message 'Required parameter is missing: response_type'.
Here is the code snippet, following Google's guidelines in https://developers.google.com/accounts/docs/OAuth2WebServer#formingtheurl:
get '/' do
requestGoogleAccessToken(102030, "http://localhost:4567/oauth2callback/")
end
def requestGoogleAccessToken(auth_code, redirect_uri)
googleAuthHost = 'https://accounts.google.com/o/oauth2/auth'
googleClientID = "XXXXXXXXXXX.apps.googleusercontent.com"
scope = 'https://www.googleapis.com/auth/calendar'
url="#{googleAuthHost}?scope=profile&redirect_uri=#{CGI.escape(redirect_uri)}&client_id=#{googleClientID}&response_type=code"
uri=URI.parse(url)
https=Net::HTTP.new(uri.host,uri.port)
https.use_ssl=true
req=Net::HTTP::Post.new(uri.path)
res=https.request(req)
puts "\n res: ", res
puts "\n response body: ", res.body
end
Thanks in advance for your help.