google-drive-api method "drive.files.create" stopped working (V3 - 1.58.0.2859) and return nothing - google-api

We have a .Net app using Google Drive api to upload files to a g-drive. And it just stopped working days ago (Nov 29th). But we didn't remember doing anything changes during that time.
During the investigation, we could confirm the service account for calling the Google API are valid, since the same service account is also being used for calling other google APIs, and works fine. We also can confirm it's not a permission issue, since we even set the permission of the gdrive to allow "anyone" who has the link, to have edit permission, but the issue is still there.
Unfortunately, we cannot find any useful log, and the return message of the API call is NULL. No error code or error message returned.The only related info we saw is: on the chart of "Error by API method", it shows "drive.files.create" failed 100%.
One interesting thing is, if we disable the Google Drive API, then enable it again, it will work once, then will stop working again.
private string SaveFileToGoogleDrive(IFormFile file, string claimNumber)
{
try
{
var driveService = GetDriveServiceInstance();
var fileMetadata = new Google.Apis.Drive.v3.Data.File();
var mimeType = file.ContentType;
fileMetadata.Name = CreateFileName(file.FileName, claimNumber);
fileMetadata.MimeType = mimeType;
fileMetadata.Parents = new List { _googleSettings.GoogleDriveFolderId };
FilesResource.CreateMediaUpload request;
using (var stream = new MemoryStream())
{
file.CopyTo(stream);
request = driveService.Files.Create(fileMetadata, stream, mimeType);
request.Fields = "id";
request.Upload();
}
var googleFile = request.ResponseBody; \\The response body is always NULL, after the issue happened. :(
return googleFile.Id;
}
catch(Exception ex)
{
_logger.Error($"Google Drive exception {ex.Message} SACKTRACE: {(ex.StackTrace ?? "")} INNER EXCEPTION: {(ex.InnerException != null ? ex.InnerException.Message + "STACK TRACE:" + ex.InnerException.StackTrace ?? "" : "")}");
return string.Empty;
}
}
We found more details from the progress property in the response object, and saw the error message "The user's Drive storage quota has been exceeded.", but it does not make sense at all, since we are using "Enterprise edition" Google Workspace, which is supposed to have no limit. The service account and the key look good, GCP didn't complain at all. And that's the first thing we checked during troubleshooting.
Do you have any idea on what to do to solve the issue or what too look for when investigating this issue?

We found more details from the progress property in the response object, and saw the error message "The user's Drive storage quota has been exceeded.", but it's not make sense at all, since we are using "Enterprise editions" google workspace, which suppose has no limit. The service account and key look good, GCP didn't complain any thing. And that's the first thing we checked during troubleshooting. Anyway, the fix is: after create a new service account then use the new key of this new service account, the system back to work.

Related

Xamarin Android share PDF. Permission denied for the attachment [duplicate]

My app creates mails with attachments, and uses an intent with Intent.ACTION_SEND to launch a mail app.
It works with all the mail apps I tested with, except for the new Gmail 5.0 (it works with Gmail 4.9), where the mail opens without attachment, showing the error: "Permission denied for the attachment".
There are no useful messages from Gmail on logcat. I only tested Gmail 5.0 on Android KitKat, but on multiple devices.
I create the file for the attachment like this:
String fileName = "file-name_something_like_this";
FileOutputStream output = context.openFileOutput(
fileName, Context.MODE_WORLD_READABLE);
// Write data to output...
output.close();
File fileToSend = new File(context.getFilesDir(), fileName);
I'm aware of the security concerns with MODE_WORLD_READABLE.
I send the intent like this:
public static void compose(
Context context,
String address,
String subject,
String body,
File attachment) {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("message/rfc822");
emailIntent.putExtra(
Intent.EXTRA_EMAIL, new String[] { address });
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, body);
emailIntent.putExtra(
Intent.EXTRA_STREAM,
Uri.fromFile(attachment));
Intent chooser = Intent.createChooser(
emailIntent,
context.getString(R.string.send_mail_chooser));
context.startActivity(chooser);
}
Is there anything I do wrong when creating the file or sending the intent? Is there a better way to start a mail app with attachment? Alternatively - has someone encountered this problem and found a workaround for it?
Thanks!
I was able to pass a screenshot .jpeg file from my app to GMail 5.0 through an Intent. The key was in this answer.
Everything I have from #natasky 's code is nearly identical but instead, I have the file's directory as
context.getExternalCacheDir();
Which "represents the external storage directory where you should save cache files" (documentation)
GMail 5.0 added some security checks to attachments it receives from an Intent. These are unrelated to unix permissions, so the fact that the file is readable doesn't matter.
When the attachment Uri is a file://, it'll only accept files from external storage, the private directory of gmail itself, or world-readable files from the private data directory of the calling app.
The problem with this security check is that it relies on gmail being able to find the caller app, which is only reliable when the caller has asked for result. In your code above, you do not ask for result and therefore gmail does not know who the caller is, and rejects your file.
Since it worked for you in 4.9 but not in 5.0, you know it's not a unix permission problem, so the reason must be the new checks.
TL;DR answer:
replace startActivity with startActivityForResult.
Or better yet, use a content provider.
Use getExternalCacheDir() with File.createTempFile.
Use the following to create a temporary file in the external cache directory:
File tempFile = File.createTempFile("fileName", ".txt", context.getExternalCacheDir());
Then copy your original file's content to tempFile,
FileWriter fw = new FileWriter(tempFile);
FileReader fr = new FileReader(Data.ERR_BAK_FILE);
int c = fr.read();
while (c != -1) {
fw.write(c);
c = fr.read();
}
fr.close();
fw.flush();
fw.close();
now put your file to intent,
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tempFile));
You should implement a FileProvider, which can create Uris for your app's internal files. Other apps are granted permission to read these Uris. Then, simply instead of calling Uri.fromFile(attachment), you instantiate your FileProvider and use:
fileProvider.getUriForFile(attachment);
Google have an answer for that issue:
Store the data in your own ContentProvider, making sure that other apps have the correct permission to access your provider. The preferred mechanism for providing access is to use per-URI permissions which are temporary and only grant access to the receiving application. An easy way to create a ContentProvider like this is to use the FileProvider helper class.
Use the system MediaStore. The MediaStore is primarily aimed at video, audio and image MIME types, however beginning with Android 3.0 (API level 11) it can also store non-media types (see MediaStore.Files for more info). Files can be inserted into the MediaStore using scanFile() after which a content:// style Uri suitable for sharing is passed to the provided onScanCompleted() callback. Note that once added to the system MediaStore the content is accessible to any app on the device.
Also you can try set permissions for your file:
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
And finally you can copy/store your files in external storage - permissions not needed there.
I tested it and I found out that it was definitely private storage access problem.
When you attach some file to Gmail (over 5.0) do not use the file from private storage such as /data/data/package/. Try to use /storage/sdcard.
You can successfully attach your file.
Not sure why GMail 5.0 doesn't like certain file paths (which I've confirmed it does have read access to), but an apparently better solution is to implement your own ContentProvider class to serve the file. It's actually somewhat simple, and I found a decent example here: http://stephendnicholas.com/archives/974
Be sure to add the tag to your app manifest, and include a "android:grantUriPermissions="true"" within that. You'll also want to implement getType() and return the appropriate MIME type for the file URI, otherwise some apps wont work with this... There's an example of that in the comment section on the link.
I was having this problem and finally found an easy way to send email with attachment. Here is the code
public void SendEmail(){
try {
//saving image
String randomNameOfPic = Calendar.DAY_OF_YEAR+DateFormat.getTimeInstance().toString();
File file = new File(ActivityRecharge.this.getCacheDir(), "slip"+ randomNameOfPic+ ".jpg");
FileOutputStream fOut = new FileOutputStream(file);
myPic.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.flush();
fOut.close();
file.setReadable(true, false);
//sending email
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"zohabali5#gmail.com"});
intent.putExtra(Intent.EXTRA_SUBJECT, "Recharge Account");
intent.putExtra(Intent.EXTRA_TEXT, "body text");
//Uri uri = Uri.parse("file://" + fileAbsolutePath);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(Intent.createChooser(intent, "Send email..."),12);
}catch (Exception e){
Toast.makeText(ActivityRecharge.this,"Unable to open Email intent",Toast.LENGTH_LONG).show();
}
}
In this code "myPic" is bitmap which was returned by camera intent
Step 1: Add authority in your attached URI
Uri uri = FileProvider.getUriForFile(context, ""com.yourpackage", file);
Same as your manifest file provide name
android:authorities="com.yourpackage"
Step 2`; Add flag for allow to read
myIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

Google Drive SDK 1.8.1 RedirectURL

Is there any way to provide RedirectURL then using GoogleWebAuthorizationBroker?
Here is the sample code in C#:
Task<UserCredential> credential = GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, scopes, GoogleDataStore.User, cancellationToken, dataStore);
Or we have to use different approach?
I have an "installed application" that runs on a user's desktop, not a website. By default, when I create an "installed application" project in the API console, the redirect URI seems to be set to local host by default.
What ends up happening is that after the authentication sequence the user gets redirected to localhost and receives a browser error. I would like to prevent this from happening by providing my own redirect URI: urn:ietf:wg:oauth:2.0:oob:auto
This seems to be possible using Python version of the Google Client API, but I find it difficult to find any reference to this with .NET.
Take a look in the implementation of PromptCodeReceiver, as you can see it contains the redirect uri.
You can implement your own ICodeReceiver with your prefer redirect uri, and call it from a WebBroker which should be similar to GoogleWebAuthorizationBroker.
I think it would be great to understand why can't you just use PrompotCodeReceiver or LocalServerCodeReceiver.
And be aware that we just released a new library last week, so you should update it to 1.9.0.
UPDATE (more details, Nov 25th 2014):
You can create your own ICodeReceiver. You will have to do the following:
* The code was never tested... sorry.
public class MyNewCodeReceiver : ICodeReceiver
{
public string RedirectUri
{
get { return YOU_REDIRECT_URI; }
}
public Task<AuthorizationCodeResponseUrl> ReceiveCodeAsync(
AuthorizationCodeRequestUrl url,
CancellationToken taskCancellationToken)
{
// YOUR CODE HERE FOR RECEIVING CODE FROM THE URL.
// TAKE A LOOK AT THE FOLLOWING:
// PromptCodeReceiver AND LocalServerCodeReceiver
// FOR EXAMPLES.
}
}
PromptCodeReceiver
and LocalServerCodeReceiver.
Then you will have to do the following
(instead of using the GoogleWebAuthorizationBroker.AuthorizeAsync method):
var initializer = new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets,
Scopes = scopes,
DataStore = new FileDataStore("Google.Apis.Auth");
};
await new AuthorizationCodeInstalledApp(
new GoogleAuthorizationCodeFlow(initializer),
new MyNewCodeReceiver())
.AuthorizeAsync(user, taskCancellationToken);
In addition:
I'll be happy to understand further why you need to set a different redirect uri, so we will be able to improve the library accordingly.
When I create an installed application the current PromptCodeReceiver and LocalServerCodeReceiver work for me, so I'm not sure what's the problem with your code.

Google service object for Google Calendar API

I am trying to use the Google Calendar API in .NET, specifically I am trying to get a list of events. According to the examples here, in different programming languages I need to create a 'service' object and an 'event' object. However, I can't find a clear explanation of what either of these objects is or how to initiate them. Does anyone have an explanation? Or can anyone provide any information or give me a link to where this is explained? It doesn't necessarily have to be in .NET
Here is the example in Java:
String pageToken = null;
do {
events = service.events().list('primary').setPageToken(pageToken).execute();
List<Event> items = events.getItems();
for (Event event : items) {
System.out.println(event.getSummary());
}
pageToken = events.getNextPageToken();
} while (pageToken != null);
Following the advice answered, I am getting the following error:
Could not load file or assembly 'Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.16.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
Here is the code, the error occurs on the credentials = Await... line
Dim credential As UserCredential
Dim clientSecretsPath As String = Server.MapPath("~/App_Data/client_secret.json")
Dim scopes As IList(Of String) = New List(Of String)()
scopes.Add(CalendarService.Scope.Calendar)
Using stream = New System.IO.FileStream(clientSecretsPath, System.IO.FileMode.Open, System.IO.FileAccess.Read)
credential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, scopes, "user", CancellationToken.None)
End Using
The problem with GoogleWebAuthorizationBroker is that it tries to launch a new instance of a web browser to go and get authorization where you have to click the "Grant" button.
Obviously if you're running a MVC project under IIS it's just going to get confused when the code tries to execute a web browser!
My solution:
Download the .net sample projects: https://code.google.com/p/google-api-dotnet-client/source/checkout?repo=samples
Build and run one of the projects relevant to you (Eg Calendar or Drive). Dont forget to include your client_secret.json file downloaded from the cloud console.
Run the project and it will open a new browser on your computer where you will have to click the "Grant" button. Do this once and then your MVC code will work because it will not try to open a web browser to grant the permissions.
I'm not aware of any other way to grant this permission to the SDK but it worked for me just great!
Good luck. This took me a good 5 hours to figure out.
Just had the same issue running VS2013 (using .net45 for my project):
After fetching the CalendarV3 API via NuGet you just have to manually add the reference to:
...packages\Microsoft.Bcl.Async.1.0.165\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll
to the project (because it is not inserted automatically via the NuGet-Script)!
That's it! Maybe #peleyal is correcting the script somewhen in future ;)
Remember that this sample is for Java. My recommendation is to do the following:
Take a look in our VB sample for the Calendar API which is available here
You should take a look also in other sample for C#, let's say Tasks API sample
Start a new project and add a NuGet reference to Google.Apis.Calednar.v3. Remember that it's prerelease version.
Your code should look like the following:
It's based on the 2 samples above, I didn't compile or test it but it should work.
UserCredential credential;
using (var stream = new System.IO.FileStream("client_secrets.json",
System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { CalendarService.Scope.Calendar },
"user", CancellationToken.None);
}
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "YOUR APP NAME HERE",
});
var firstCalendar = (await service.CalendarList.List().ExecuteAsync()).Items().FirstOrDefault();
if (firstCalendar != null)
{
// Get all events from the first calendar.
var calEvents = await service.Events.List(firstCalendar.Id).ExecuteAsync();
// DO SOMETHING
var nextPage = calEvents.NextPage;
while (nextPage != null)
{
var listRequest = service.Events.List(firstCalendar.Id);
// Set the page token for getting the next events.
listRequest.PageToken = nextPage;
calEvents = await listRequest.EsecuteAsync();
// DO SOMETHING
nextPage = calEvents.NextPage;
}
}
I had the same error, and it was due to the app trying to launch the accept screen.
I first tried to get the vb.net example from google and ran that, which I did get to work, and change to my secret info, ran and got the accept screen. I then tried my app, and it still did not work.
I noticed that the dll was found here under my project installed from the nuget packages.
...packages\Microsoft.Bcl.Async.1.0.165\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll
but was not in the net45 dir. So I uninstalled the nuget packages (have to if changing the .net version) then changed my .net version for my project to 4.0 instead of 4.5, reinstalled the nuget packages, and then it worked!!

How to do HttpWebRequests from a Windows Phone 7?

I've been looking for an answer to my problem for several days, but sadly had no luck. I hope you guys here can help me out.
So, what I want to do is just a simple call against the delicious.com API that returns me the recent bookmarks ("posts" in delicious terminology). Therefore you simply use the following URI and enter your credentials for logging in to delicious.com.
When I do this from a standard .NET4 Console Application with the magic of HttpWebRequest and setting the Credentials, it works as expected. Trying to do the same basic HttpWebRequest on the WP7 emulator leads to an WebException stating that "The remote server returned an error: NotFound.".
When diving deeper into this exception I can see that my Response has a StatusDescription of "Unauthorized". It seems like no authentication happend at all.
Next I tried - as I read that setting the Credentials sometiemes leads to problems - was to directly set the "Authorization" RequestHeader. Guess what, the result was the same.
I ended up with the following code and without ideas. I hope, somebody here is able to
help me out and point a way, how I can reach my bookmarks from my WP7.
public void RetrieveRecentBookmarks(string userName, string password)
{
HttpWebRequest request = HttpWebRequest.CreateHttp(requestString);
byte[] bytes = System.Text.UTF8Encoding.UTF8.GetBytes(userName + ":" + password)
string authInfo = Convert.ToBase64String(bytes);
request.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " + authInfo;
RequestState state = new RequestState(request);
IAsyncResult result = request.BeginGetResponse(GetResponseCallback, state);
}
private static void GetResponseCallback(IAsyncResult asynchronousResult)
{
RequestState state = asynchronousResult.AsyncState as RequestState;
HttpWebRequest request = state.Request as HttpWebRequest;
// Here is where it breaks...
HttpWebResponse response = request.EndGetResponse(asynchronousResult) as HttpWebResponse;
}
Thanks in advance!
As I now know, my code is absolutely correct and working. What was not working was the Emulator communication, as already mentioned by Shawn Kendrot.
I set up a completely fresh Win7, installed the WP7.1 SDK (and 7.1.1 Update) and it works like expected. So it seems like something blocked my calls from the emulator. I've got no idea, what it was, but I must admit, I don't care too much as it is working now.
Thank you Shawn for pointing out that there are sometimes problems with the Emulator and the web, that kicked me into the right direction.

Implements a local restaurant search

I am currently trying to have a feature similar to the local scout (search button -> proximity) in my Windows Phone 7 app.
I'd like to get the 10 top restaurants or bars/clubs near my current location. Getting the geolocation of the user is the easy part.
Now I'd like to pass this location to an API and get the results.
I tried Bing Search API but I didn't figure out how to get special locations such as restaurants. And the SOAP service connection wasn't working from my phone.
Then I searched from others API... it seems that Google or Yahoo (PlaceFinder) or maybe Foursquare can be used too. But before investigating further, do you have any ideas of a simply solution to implement the local scout from Bing ?
Or is there another way to implement this feature ?
Oh, and I am french but not specially targeting a market so I'd like to use an international service.
Thanks a lot.
Damien
EDIT :
I guess I found a part of my solution :
Get business type/industry from Bing Phonebook API
As I want to use the Bing Search WSDL, I added a service reference to http://api.search.live.net/soap.asmx?AppId=42 (where 42 is my app key).
When I execute (from my phone) a simple test :
public void Test()
{
BingPortTypeClient bing = new BingPortTypeClient();
SearchRequest request = new SearchRequest();
request.AppId = AppId;
request.Sources = new SourceType[] { SourceType.Web };
request.Query = "restaurant";
bing.SearchCompleted += new EventHandler<SearchCompletedEventArgs>(bing_SearchCompleted);
bing.SearchAsync(request);
}
void bing_SearchCompleted(object sender, SearchCompletedEventArgs e)
{
}
I am getting the following exception :
There was no endpoint listening at http://api.search.live.net/soap.asmx that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
Inner : The remote server returned an error: NotFound.
I thought using this API would be quite more easy and well-documented...

Resources