Google Drive Ruby Client Returning Different Responses - ruby

So I'm working with the Google-Drive-Ruby-Client API to export some data into spreadsheets. The problem is, when I'm trying to get the exportLink to download the spreadsheets into a CSV format, what I'm getting as a response in the Google API Explorer and what I'm getting when using in my application doesn't match up. Specifically, the entire exportLinks sections of the response is missing.
Has this way of downloading the spreadsheets been deprecated? Is there any other way to export spreadsheets into a CSV format? Or any other workable format (perhaps a multidimensional array?)
client = Google::APIClient.new(:application_name => APPLICATION_NAME)
client.authorization = authorize
drive_api = client.discovered_api('drive', 'v2')
result = client.execute!(
:api_method => drive_api.files.get,
:parameters => {:fileId => "1MBP9Q9Q-9ZgLoYnY8ExS-EcxHLESI_vcK4J91ngp6-Q"})
file = result.data
puts("Fetched #{file["title"]}")
puts("Getting downloadURL")
exportLinks = file['exportLinks']
From there, I should be able to call exportLinks['text/csv'] is my understanding, but the response does not include the exportLinks section like it does in the API Explorer.
Edit: I use the same authorization that Google uses in its quick start guide, and that all works fine, as well as other methods work fine, so I know I'm connecting to the Drive API correctly, I'm just not sure why my responses aren't matching the API Explorer.

Related

Laravel Passport; using OAuth2 for SSO with Freshdesk

I am trying to set up SSO from my Laravel 8.13 site to my related Freshdesk support portal.
What I want is for a user, who is logged into my site, then to be able to click a button and get seamlessly signed into my Freshdesk support portal so they can view the non-public documentation there and raise tickets if required.
Using Laravel Passport 10.1 I can create a token (tested using Postman) but get an error from Freshdesk when trying to authenticate.
An error occurred while attempting to retrieve the userinfo resource: could not extract response: no
suitable httpmessageconverter found for response type [java.util.map<java.lang.string,
java.lang.object>] and content type [text/html;charset=utf-8]
I have not used OAuth before and am having issues. It may of course be that my understanding of OAuth is just completely wrong but I am finding available documentation on Laravel Passport / Freshdesk OAuth connectivity hard to come by.
I have been through as many related SO questions as I have found but nothing so far seems to exactly fit my issue.
I have also got open tickets with Freshdesk and have had an online support session with a Freshdesk support member but they told me the issue was on my side and they couldn't help further.
I would have thought the client type to use was a Personal Access Client but I have tried that as well as a Password Grant Client and get the same message (as above) for both client types.
As far as the "User info URL*" field on Freshdesk goes,
I have tried
http://testsite.ngrok.io/oauth/tokens
and
http://testsite.ngrok.io/oauth/personal-access-tokens
and
http://testsite.ngrok.io/api/user
But no luck - same message regarding not finding userinfo resource as above.
If I browse directly to
http://testsite.ngrok.io/oauth/personal-access-tokens
I get the following returned:
[{"id":"e595f342722c1045e061f2f026fa7f07181b3d5c69016e9999c5640f1e65928ff6fe2621565ff666c5",
"user_id":43128,"client_id":7,"name":null,"scopes":"openid","email","profile"],"revoked":false,
"created_at":"2021-01-26 10:16:12","updated_at":"2021-01-26 10:16:12",
"expires_at":"2022-01-26T10:16:12.000000Z","client":
{"id":7,"user_id":null,"name":"freshworksPersonalAccessClient",
"provider":null,
"redirect":"https:\/\/testsite.freshworks.com\/sp\/OAUTH\/27402945223480041\/callback",
"personal_access_client":true,"password_client":false,"revoked":false,
"created_at":"2021-01-19T23:19:39.000000Z","updated_at":"2021-01-19T23:19:39.000000Z"}}]
so something is returned but I am not sure if that is in any way near what Freshdesk is looking for. The support documentation for Freshdesk states :
But I do not know where to create that info or in what format it needs to be sent to Freshdesk.
I have looked at this question and believe that (even though it is for older versions) there might be something in the code for "getUserEntityByUserCredentials" in the UserRepository.php file but I have put logger calls in that function and it doesn't seem to be called.
Any assistance would be absolutely great. If any further information is required please let me know.
Although I would prefer to keep it within the Laravel ecosystem, I am also open to any other way to set up SSO to Freshdesk without using a third party Identity Provider like ADFS, OneLogin, Okta, Azure and suchlike. I just need to get this done.
Solved it. Basically my issue was the format and content returned by the User info URL.
I set up a route for
/userinfo
to a function in a TestController. In that function I decoded the Bearer token to get the user_id and then used that to retrieve the user details.
Then I created an array and response as below:
$thisDecodedUser = User::query()->findOrFail($thisDecodedUserId);
if ($thisDecodedUser) {
$thisDecodedUserFirstName = $thisDecodedUser->first_name;
$thisDecodedUserLastName = $thisDecodedUser->last_name;
$thisDecodedUserEmail = $thisDecodedUser->email;
}
$user_info = array();
$user_info['sub'] = "$thisDecodedUserId";
$user_info['id'] = "$thisDecodedUserId";
$user_info['email'] = "$thisDecodedUserEmail";
$user_info['.id'] = "$thisDecodedUserId";
$user_info['.email'] = "$thisDecodedUserEmail";
$user_info['email_verified'] = "true";
return response(json_encode($user_info))
->withHeaders([
'Content-Type' => 'application/json',
]);
The code needs refactoring but at least it works for now.
Thank you for pointing me in the right direction in this answer : https://stackoverflow.com/a/65929987/920598
Even if the array you use contains unused keys.
Actually, the available fields and their format are described here https://support.freshdesk.com/en/support/solutions/articles/50000002088-configuring-custom-sso-policies-under-org
For example, if you want to sync the firstname, the lastname and the language, here's the keys you need to set :
<?php
$user_info = [
'sub' => (string)$user->id,
'unique_id' => (string)$user->id,
'email' => $user->email,
'FirstName' => (string)$user->firstname,
'LastName' => (string)$user->lastname,
'language' => $user->default_ln === 1 ? 'fr' : 'en'
];

Laravel download from google drive (filesystem) or (hide api key from link)

First way to download:
https://www.googleapis.com/drive/v3/files/1YIH4zfM0P1xa-_mfZNGiIY8qZrIEt-rF/?key=API_KEY&alt=media
Putting my API KEY in the link it gives me ability to download the file directly from my google drive.But I don't want to give my API_KEY to the users.
2.I can also access my drive another way: (using nao-pon/flysystem-google-drive)
Route::get('/download/{rest?}', function ($rest) {
$metadata = Storage::cloud()->getMetadata($rest);
$readStream = Storage::cloud()->getDriver()->readStream($rest);
return response()->stream(function () use ($readStream) {
fpassthru($readStream);
}, 200, [
'Content-Type' => $metadata['mimetype'],
'Content-disposition' => 'attachment; filename="'.$metadata['name'].'"', // force download?
]);
})->where('rest','(.*)');
This way I don't have to use api_key but the server has to download the whole file it's a stream but still that uses server resources.
On the other hand https://www.googleapis.com/drive/v3/files/1YIH4zfM0P1xa-_mfZNGiIY8qZrIEt-rF/?key=API_KEY&alt=media needs Content-Type and File name as it doesn't have on it.And also I have no idea how to hide the api key.
So what do you suggest.Is there any other way not to response()->stream to not download the whole file by stream through server and then send it to the user?Multiple users downloading the files would use all the bandwidth,so the download speed drops so fast.

Error "SignatureDoesNotMatch". Google Cloud Storage Bucket PUT

I'm loosing my mind.
I'm using Shrine (https://github.com/janko-m/shrine) with Google Cloud Storage (https://github.com/renchap/shrine-google_cloud_storage), but when I start the PUT call I get this:
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.
</Message>
<StringToSign>
PUT
image/jpeg
1518399402
/mybucket.appspot.com/7d5e4aad1e3a737fb8d2c59571fdb980.jpg
</StringToSign>
</Error>
I followed this info (http://shrinerb.com/rdoc/classes/Shrine/Plugins/PresignEndpoint.html) for presign_endpoint, but still nothing:
class FileUploader < Shrine
plugin :presign_endpoint, presign_options: -> (request) do
filename = request.params["filename"]
extension = File.extname(filename)
content_type = Rack::Mime.mime_type(extension)
{
content_type: content_type
}
end
end
I tried with and without this (restarting the Rails server everytime).
Where am I wrong?
I also tried with Postman with a PUT to that URL and withtout any content-type. But still nothing.
I read here: https://github.com/GoogleCloudPlatform/google-cloud-node/issues/1976 and here: https://github.com/GoogleCloudPlatform/google-cloud-node/issues/1695
How can I try without Rails?
Is there a REPL (or similar) to try with my credentials and with a file?
You have several options for just uploading a file to Google Cloud Storage as you can see in the official docs here.
For example if you want to use Ruby Client Library, you can use this code:
# project_id = "Your Google Cloud project ID"
# your-bucket-name = "Your Google Cloud Storage bucket name"
# local_file_path = "Path to local file to upload"
# storage_file_path = "Path to store the file in Google Cloud Storage"
require "google/cloud/storage"
storage = Google::Cloud::Storage.new(project: "your-project_id")
bucket = storage.bucket "your-bucket-name"
file = bucket.create_file "local_file_path", "storage_file_path"
puts "Uploaded #{file.name}"
You will need to:
Set the environment variable GOOGLE_APPLICATION_CREDENTIALS to the file path of the JSON file that contains your service account key.
as you can see in the Cloud Storage Client Libraries docs here.
However, it looks like the github repo you are using makes use of signed URLs. If you need to create a signed URL you have the instructions here. It exists already the signed_urls method for Ruby in the official repo.
Anyway, the error you were getting is because there was something wrong being made with the signed urls. And precisely there was a commit to the repo you were referencing (https://github.com/renchap/shrine-google_cloud_storage) 7 days ago with the name Fix presigned uploads. It looks like this commit should fix the "PUT" upload (before a "GET" was being signed, therefore it wouldn't work). Still I didn't try it so I don't know if it is actually working.

Requesting An Access Token from Google API Returns 302

I'm trying to get an access token from Google API in my Ruby on Rails app, as part of an overall goal of setting up a raketask. I am able to get an Auth Code fine, but when I make a post request to get an access token, I am getting a 302 error. I'll describe my current code first, and afterward list how I've tried to solve the problem so far.
Current code:
#users_controller
def auth_access
client = Signet::OAuth2::Client.new(
:authorization_uri => 'https://accounts.google.com/o/oauth2/auth',
:token_endpoint_uri => 'https://accounts.google.com/o/oauth2/token',
:client_id => ENV['OAUTH_CLIENT_ID'],
:client_secret => ENV['OAUTH_CLIENT_SECRET'],
:scope => 'https://www.googleapis.com/auth/analytics.readonly',
:redirect_uri => 'http://localhost:3000/google/auth_callback'
)
redirect_to client.authorization_uri.to_s
end
This part works fine so far. It redirects to the consent page, and when the user agrees it then redirects them to the page with the auth code in the url parameters. Next I take that auth code and try to make a POST request to API for an access token:
#users_controller
def auth_callback
http = Net::HTTP.new('accounts.google.com')
path = '/o/oauth2/token'
data = "code=#{params['code']}&client_id=#{ENV['OAUTH_CLIENT_ID']}&client_secret=#{ENV['OAUTH_CLIENT_SECRET']}&redirect_uri=http://localhost:3000/auth_final&grant_type=authorization_code"
response = http.post(path, data)
end
This when I run into a problem. The Google API returns a 302, and includes a message saying something akin to "we moved to 'https://accounts.google.com/o/oauth2/token'".
Here's how I've tried to fix the problem so far:
I assumed that the problem was that the http.post method is making a call to an http and not https.
I've tried including
http.use_ssl = true
http.ssl_version = :SSLv3
This returns the error "SSL_connect returned=1 errno=0 state=SSLv3 read server hello A: wrong version number".
I can take a guess at what this means, but I am still unsure of what the actual problem is and how to solve it. Googling the error message has not been a help.
In a similar vein, I tried using gems to make the https call for me, in particular HTTParty and Typheous, although I was not able to make any progress with them (and am still not even sure that it's an http/https problem).
I've tried using the Signet-Rails gem. This was the most productive method by far, making a successful API call and returning the information. However, it either wasn't saving the refresh token or I cannot find where it is being saved. As I need access to that token to run the rake tasks, I gave up on Signet-Rails.
I tried using Legato, and was constantly running into various problems. Overall, Legato left me with the impression that it did not integrate getting the auth code, consent and tokens into the app, instead requiring the developer to set those up in advance outside of the app's scope. I want to be able to set up the auth code as part of the app. If I am understanding Legato properly, then it is not the gem I need.
I've also tried other various odds and ends but to no avail. The above solutions were the tactics I kept coming back to. Primarily I'm looking for an answer to what is going wrong in my code, and what is the best avenue to fix it (and if I was going down the right track with any of my attempted solutions above, which one?)
Thanks for taking the time to read this and answer!
(on a complete sidenote, those last three list items should be 2, 3, 4, but the stackoverflow text editor thinks it knows better than me...)
Specify the port:
http = Net::HTTP.new('accounts.google.com', 443)
Source: SSL Error on HTTP POST (Unknown Protocol)

Why does Google's Custom Search API say that I'm missing an access token when using the Ruby client?

I'm trying to use Google's Custom Search API through the Google API Ruby client. I have setup my API key through the Google API console, and have also created my CSE. Based on the documentation, it seems that, as long as I provide an API key (which I am doing), I shouldn't need an OAuth2 authentication token to call the list method. However, when I try to execute the code below, I get the following error:
ArgumentError: Missing access token.
What am I missing? Here's my code:
# create client
client = Google::APIClient.new
# Fetch discovery doc
search = client.discovered_api('custom search')
# Call list method
response = client.execute(
search.cse.list, 'key' => '<my API key>', 'cx' => '<my CSE id>', 'alt' => 'json', 'q' => 'hello world'
)
I believe this is in fact a bug in the client (it's in alpha). After fiddling with it a little more, I've found a workaround:
just after creating the client object, assign it a "dummy" access token:
client.authorization.access_token = '123'
then you can call the search.cse.list method without getting the 'ArgumentError: Missing access token.' error.
If you're just after using Google CSE with ruby, try google-cse. I just built the gem, although I've been using it for a while privately. Much easier to work with than the alpha client
I found out that adding client.retries = 3 to my code solves this problem.
With the current version of the gem (0.7.1), you need to set the authorization to nil in addition to setting the key:
require 'google/api_client'
client = Google::APIClient.new
client.key = ENV['GOOGLE_API_KEY']
client.authorization = nil
client.execute ...

Resources