Byron / google-apis-rs

A binding and CLI generator for all Google APIs
http://byron.github.io/google-apis-rs
Other
1.03k stars 135 forks source link

Issue with upload_resumable() Returning 400 Bad Request for Sending Emails #532

Open GsnMithra opened 1 week ago

GsnMithra commented 1 week ago

I am working with the google_gmail1 Rust library and encountering an issue while attempting to send emails. Since the doit() method for messages_send is private, I’ve been using the upload_resumable() method. However, this approach consistently results in a 400 (Bad Request) response, and the email is not sent even for simple plain-text emails.

Here’s what I’m doing:

  1. I use the lettre library to construct a MIME-compliant email message.
  2. The email message is Base64-encoded and stored in a file.
  3. I then read the file as a stream and pass it to the upload_resumable() function:
let result = hub.users()
    .messages_send(gmail_msg, "me")
    .add_scope("https://www.googleapis.com/auth/gmail.send")
    .upload_resumable(stream, mime_type)
    .await;

Despite multiple adjustments to the request body, headers, and encoding, the 400 Bad Request persists. This is the only email I’m sending during my tests, so I don’t believe it’s related to rate limits or quotas.

Steps to Reproduce:

  1. Construct a MIME-compliant email using the lettre library or manually.
  2. Encode the email message in Base64 and save it to a file.
  3. Use the file as a stream and invoke the upload_resumable() method as shown in the code snippet above.

Expected Behavior: The email should be successfully sent using the Gmail API without a 400 Bad Request error.

Actual Behavior: The API consistently returns a 400 Bad Request response.

Could you confirm if this is a known issue or guide me on what might be wrong in the implementation?

Byron commented 1 week ago

Could the _resumable part be the issue? Is there a way to send it without streaming?

I recommend vendoring the crate source code so it's possible to make local modifications - in the end it only generates an HTTP request but won't offer full control over it. Looking at the source usually helps gaining the control that's needed to adjust the request and see what's causing the issues.

GsnMithra commented 1 week ago

I recently updated my code to use the upload() method instead of upload_resumable(). However, I am now receiving the following error response when attempting to send an email:

Error sending email: BadRequest(Object {"error": Object {"code": Number(400), "errors": Array [Object {"domain": String("global"), "message": String("Recipient address required"), "reason": String("invalidArgument")}], "message": String("Recipient address required"), "status": String("INVALID_ARGUMENT")}})

The error message indicates that the recipient address is required, but I am already providing it in my MIME message. Here is the updated code I’m using:

let response = hub .users() .messages_send(raw_email, "me") .add_scopes(&[ Scope::Send, Scope::Compose, Scope::Gmai, Scope::AddonCurrentActionCompose, Scope::Modify, ]) .upload(file, mime_type) .await;

Problem: Despite passing the required scopes and MIME content, I’m getting the “Recipient address required” error. The recipient address is included in the raw email message, so I’m unsure why it’s not being recognized.

Question: Is there an easier or more direct way to send an email using the google_gmail1 library, similar to the following curl command, which worked with the token returned by the library and stored in token_cache.json?

curl --request POST \ 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send?key=(xxx)' \ --header 'Authorization: Bearer (xxx)' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --data '{"raw":"()"}' \ --compressed

This curl command sends a message by passing the raw email content as a string in the request body.

Also, the documentation mentions using the doit() method on messages_send(), but it seems to be private. Could you clarify if there’s an intended public method for this, or is there another recommended approach to send the email directly?

Additional Context: I have included the necessary scopes (Send, Compose, Modify, etc.), but the issue persists. Any guidance on what might be missing or incorrect would be greatly appreciated!

Byron commented 1 week ago

If the documentation is advertising a private method, this would definitely be a bug. Independently of that, maybe there is a way to use a proxy and see what kind of request it's actually sending. If not, maybe there is a way to add debug statements in the vendored version of the gmail library.

There is also a CLI that could be tried to achieve the same - can it work? I never tried, the only library I ever used was the youtube one, through the youtube-cli.

If it seems too cumbersome to use, maybe you can just use reqwest directly as well.