Uploading to Google Drive API using Ruby RestClient - ruby

I'm trying to upload a small image file to Google Drive using RestClient. I already have an access token (being requested in earlier code) but I am not sure how to form the payload.
The API docs state the request should look like this:
POST /upload/drive/v3/files?uploadType=media HTTP/1.1
Host: www.googleapis.com
Content-Type: image/jpeg
Content-Length: number_of_bytes_in_file
Authorization: Bearer your_auth_token
JPEG data
I have tried the following but it results in an error:
require 'rest-client'
file = File.open("./uploaded-by-api.jpg")
response = RestClient::Request.execute(method: :post, url: 'https://www.googleapis.com/upload/drive/v3/files', payload: { uploadType: "media", file: file, }, headers: { Authorization: "Bearer #{access_token}", "Content-Type" => "image/jpeg", "Content-Length" => "1000" })
I'm pretty sure there is a mistake in the way I'm including the actual file data but I can't find any examples of this with RestClient.

Ok solved it with the following:
response = RestClient.post(
'https://www.googleapis.com/upload/drive/v3/files',
{ 'uploadType' => "media", 'upload' => file },
"Authorization" => "Bearer #{access_token}",
"Content-Type" => "image/jpeg",
"Content-Length" => "1000"
)

Related

Post request detected as bot with HTTPARTY but not with postman (same headers)

I am trying to fetch a public API. When I do it from the postman everything works fine however when I do it from my app I get and error message: <META NAME=\"robots\" CONTENT=\"noindex,nofollow\"
I do not understand how this is possible?
Here is are the headers variables I adjust when I make my request with postman:
Cookie:"some cookie"
Cache-Control: no-cache
Content-Type:application/json
Host:"some host"
Here is my httparty request:
response = HTTParty.post(url,
:body => body_request (same as with postman),
:headers => {
'Content-Type' => 'application/json',
'cookie' => 'same cookie as above',
'Host' => 'same host as above',
'Cache-Control' => 'no-cache'
}
)
Why would it work with postman but not with a httparty request?
Thank you
I would look into User-Agent, even if you don't explicitely set the header, your http client is still sending one.
Postman uses :
"User-Agent": "PostmanRuntime/7.26.8",
while HTTParty is simply
"User-Agent": "Ruby"
Maybe your public API (could be more precise if we knew which) has a whitelist of 'non-bot' user agents and HTTParty is not among them
Try overriding it
resp = HTTParty.get 'https://httpbin.org/headers' , headers: {'User-Agent': 'xx'}

Users_not_found in HTTParty request to SlackAPI when curl works

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

Using Gmail API to Send Attachments Larger than 10mb

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

Header parameter missing when making POST request using Rest-Client Ruby Gem

Hi I am successfully able to post from Post man , but unable to do so from Ruby Rest client.
Post details
Post Man Request
POST /endpoint HTTP/1.1
Host: host:11400
Accept: application/json
HTTP_USER: userid
fname: fname
lname: lname
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 589a5345-e384-bd71-d690-60987165487b
{ "rtdt":"09/08/2016","jobs":[{"pid":53} , {"pid":54}]}
the rest code I ve tried multiple ways including below
method tried
p RestClient.post 'http://url_details',
Accept: 'application/json',
'HTTP_USER'.to_sym => 'userid',
fname: 'name',
lname: '',
'Content-Type'.to_sym => 'application/json',
payload: JSON.parse('{ "rtdt":"09/08/2016","jobs":[{"pid":53} , {"pid":54}]}')
method tried
p RestClient.post 'http://url_details/job', http_user: 'userid', content_type: :json, accept: :json
method tried
p #uber_ride = (RestClient::Request.execute(
:method => :post,
:url => 'http://url_details/job',
'HTTP_USER' => 'userid',
:headers => {:content_type => 'application/json', :accept => 'application/json', :HTTP_USER => 'userid', :fname => 'name', :lname => 'lname'}
))
method tried
p '++++++++++++++++++++++++++++++++'
p RestClient.post($Current_Api_Endpoint,
$Current_Payload,
:http_user =>'userid',
headers:
{:accept => 'application/json',
:content_type => 'application/json',
:http_user =>'userid',
:fname => 'name',
:lname => 'lname'}
)
p '++++++++++++++++++++++++++++++++'
method tried
p env1 = ENV['http_proxy']
# p a = {:method => :post, :url => 'http://url_details/job', :headers => { :accept => 'application/json', :content_type => 'application/json', :http_user => 'userid', :fname => 'name', :lname => 'lname' }, :payload => { :rtdt => "06/10/2016",:jobs => [{:pid => 53} , {:pid => 54}]} }
p a = {
method: 'post',
url: 'http://url_details/job',
proxy: ENV['http_proxy'],
headers: {accept: 'application/json', content_type: 'application/json', http_user: 'userid', fname: 'name', lname: 'lname'},
payload: { rtdt: '06/10/2016', jobs: {pid: 53}}
}
I am getting error
HTTP Status 500 - HTTP_USER header not found in request
torg.springframework.security.web.authentication
I Found out the issue since then. All headers are able to be sent, except this one - This one in CAPS. rest of them in small characters. How to solve it
Ruby's Net::HTTP implementation does not transmit ALL_CAPS_UNDERSCORED header names. RestClient relies on Net::HTTP. So, for example, the HTTP_USER header is actually transformed into Http_user when transmitted.
Alertnative: curb
Curl, however, does not have this limitation. It transmits header names verbatim. Therefore instead of using RestClient, you could use the curb gem, which is a wrapper around libcurl. This seems to work:
require "curb"
Curl::Easy.http_post("http://URL_REDACTED") do |http|
http.headers["Accept"] = "application/json"
http.headers["Content-Type"] = "application/json"
http.headers["HTTP_USER"] = "userid"
http.post_body = '{ "rtdt":"09/08/2016","jobs":[{"pid":53} , {"pid":54}]}'
end
Alternative: excon
The excon gem is another possibility. It implements HTTP in pure Ruby, without relying on Net::HTTP. It does its own header processing and allows ALL_CAPS headers. So this should work:
require "excon"
Excon.post(
"http://URL_REDACTED",
:headers => {
"Accept" => "application/json",
"Content-Type" => "application/json",
"HTTP_USER" => "userid"
},
:body => '{ "rtdt":"09/08/2016","jobs":[{"pid":53} , {"pid":54}]}'
)
Anyway, I would argue that the server you are trying to connect to is ultimately at fault here, because headers are supposed to be evaluated as case-insensitive: Are HTTP headers case-sensitive?. If the server requires a header be in all uppercase, that is a bug.
The RestClient README examples indicate that the correct usage is:
RestClient.post(url, payload, headers)
So all you have to do is:
RestClient.post(
"http://URL_REDACTED",
'{ "rtdt":"09/08/2016","jobs":[{"pid":53} , {"pid":54}]}',
content_type: :json,
accept: :json,
"HTTP_USER" => "userid"
)
I tested against http://requestb.in and got the expected headers and body:
HEADERS
Total-Route-Time: 0
Connection: close
Via: 1.1 vegur
Connect-Time: 0
Http-User: userid
Content-Type: application/json
Content-Length: 55
User-Agent: rest-client/2.0.0 (darwin15.4.0 x86_64) ruby/2.3.1p112
X-Request-Id: c7cd819f-8901-41b9-8cbb-3b6d1895cd67
Accept-Encoding: gzip
Host: requestb.in
Accept: application/json
RAW BODY
{ "rtdt":"09/08/2016","jobs":[{"pid":53} , {"pid":54}]}

Passing metadata with payload for Google Drive Rest API

I have been facing an issue in passing metadata with the Payload using the google drive api
I have been trying the following but title, description, etc is not applied to the uploaded file on drive.
upload_url = "https://www.googleapis.com/upload/drive/v2/files"
access_token = "---secret_access_token---"
fileitem = File.open('<filename>.pptx')
fileinfo = {"title": "insertedFromApi.pptx", "description": "Test description of file being uploaded from the API"}
params = {convert: true}
response = RestClient::Request.execute(
:method => :post,
:url => upload_url,
:headers => {"params": params, "uploadType": "multipart", content_type: 'multipart/related', accept: 'application/json', 'Authorization': "Bearer " + access_token},
:payload => {:content => fileitem, metadata: {title: 'insertedfromapi.pptx'}}
)
Although the google api says they accept metadata in multipart requests only, I have been doing that still all the files pushed are UNTITLED
Example request given here :-
POST /upload/drive/v2/files?uploadType=multipart HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer your_auth_token
Content-Type: multipart/related; boundary="foo_bar_baz"
Content-Length: number_of_bytes_in_entire_request_body
--foo_bar_baz
Content-Type: application/json; charset=UTF-8
{
"title": "My File"
}
--foo_bar_baz
Content-Type: image/jpeg
JPEG data
--foo_bar_baz--

Resources