Spreadsheets API Can not update a read-only feed - google-sheets

I'm trying to add new spreadsheet if it not exists with GData Spreadsheet API for .NET but it gives me following exception:
Can not update a read-only feed
Here's my code:
var service = new SpreadsheetsService("<my-app>");
service.setUserCredentials("<login>", "<password>");
// Instantiate a SpreadsheetQuery object to retrieve spreadsheets.
SpreadsheetQuery query = new SpreadsheetQuery();
var title = "test";
query.Title = title;
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.Query(query);
if (!feed.Entries.Any())
{
var worksheet = new WorksheetEntry(20, 20, title);
service.Insert(feed, worksheet);
}
Through Fiddler I see that I'm doing request to:
GET /feeds/spreadsheets/private/full?title=test
and it goes fine, but I don't see any requests for updating data. I suppose that I should change somehow SpreadsheetQuery to make it capable to write data, but I can't find how.

It's me being inattentive because google documentation on Spreadsheet API says:
It is possible to create a new spreadsheet by uploading a spreadsheet
file via the Google Drive API. The Spreadsheets API does not currently
provide a way to delete a spreadsheet, but this is also provided in
the Google Drive API. For testing purposes, you may create a
spreadsheet manually or upload one.
So I basically installed GoogleDrive API with Nuget. And then added following method for adding file:
private static void AddFile(string title)
{
var clientID = "put here a clientID";
var clientSecret = "put here a clientSecret";
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = clientID,
ClientSecret = clientSecret,
},
new[] { DriveService.Scope.Drive },
"here goes your account",
CancellationToken.None).Result;
// Create the service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample",
});
var body = new Google.Apis.Drive.v2.Data.File();
body.Title = title;
//body.Description = "A test document";
body.MimeType = "application/vnd.google-apps.spreadsheet";
service.Files.Insert(body).Execute();
}
When I run code above at the first time - I received an exception that said
Could not load file or assembly
'Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.16.0
I run these in Package Manager Console:
Uninstall-Package Microsoft.Bcl.Async -Force
Install-Package Microsoft.Bcl.Async
and it worked. Hope it would help somebody who will stumble over the same issue.

Related

EWS using OAuth says "Das Format des Wertes Bearer ... ist ungültig"

i am trying to access my mailbox via ExchangeWebService using OAuth. I followed the instructions based on this page: "https://learn.microsoft.com/de-de/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth".
This is my code:
pcaOptions = new PublicClientApplicationOptions()
{
TenantId = "<my tenantID>",
ClientId = "<my ClientID>"
};
pca = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build();
// The permission scope required for EWS access
ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" };
service = new ExchangeService();
// Make the interactive token request
var authResult = await pca.AcquireTokenInteractive(ewsScopes).ExecuteAsync();
service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
service.Credentials = new OAuthCredentials(authResult.AccessToken);
Up to this Point, evrything seems allright. The method "AcquireTokenInteractive" opens a ne windows from Microsoft where i can choose my mailaccount from my organization. The variable "authResult" ist filled and contains a new "AccessToken".
The problem starts here:
var folders = await service.FindFolders(WellKnownFolderName.MsgFolderRoot, new FolderView(10));
This method causes an exception:
Das Format des Werts "Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6ImRIUXhxTlczdlpDeVM3ZGZrZVZyNlZUQ2xwRU1JWURoMmE5LVgxaEFLMGsiLCJhbGciOiJSUzI1NiIsIng1dCI6ImpTMVhvMU9XRGpfNTJ2YndHTmd2UU8yVnpNYyIsImtpZCI6ImpTMVhvMU9XRGpfNTJ2YndHTmd2UU8yVnpNYyJ9.eyJhdWQiOiJodHRwczovL291dGxvb2sub2ZmaWNlMzY1LmNvbSIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0L2I1MjgwMzYxLWJkYjMtNDlkNS05NjU1LWQ0MTg0ZWNlMmY2My8iLCJpYXQiOjE2NTA5NzI4NDIsIm5iZiI6MTY1MDk3Mjg0MiwiZXhwIjoxNjUwOTc4MTczLCJhY2N0IjowLCJhY3IiOiIxIiwiYWlvIjoiQVRRQXkvOFRBQUFBdXhETnhhc3FYZ1pzUjlHbHdRTDVuQnhzcXErdWdtRUNBRFpSTlEwbDhLWC9oTXd1a0VKWHltYUpvNHArRjVJdCIsImFtciI6WyJwd2QiLCJyc2EiXSwiYXBwX2Rpc3BsYXluYW1lIjoiYmFzZTRJVCBXZWJzZXJ2aWNlIiwiYXBwaWQiOiJjN2FjZDNiYi1hYzE2LTRjZDktYTk4Ni1jMGFkNThjNmQ5NTMiLCJhcHBpZGFjciI6IjAiLCJkZXZpY2VpZCI6IjhkMGJiN2ZhLTFhMzUtNDJjMy1iMTVlLTU3OTAwNjYyMGM0YiIsImVuZnBvbGlkcyI6W10sImZhbWlseV9uYW1lIjoiSGVpbWVyIiwiZ2l2ZW5fbmFtZSI6IkthcnN0ZW4iLCJpcGFkZHIiOiIyMTcuNy4yMjguMjE2IiwibmFtZSI6IkthcnN0ZW4gSGVpbWVyIiwib2lkIjoiNzFlMTA2MTAtMmRkMy00ZjViLTg1MzAtYWM3ODc2NjdhY2YwIiwib25wcmVtX3NpZCI6IlMtMS01LTIxLTE2NDczNzUyODQtMjEzNjMwNjk3Ni0xNjYxNjE4Njc2LTExMzQiLCJwdWlkIjoiMTAwMzIwMDExOUU3OUVDQiIsInJoIjoiMC5BVEVBWVFNb3RiTzkxVW1XVmRRWVRzNHZZd0lBQUFBQUFQRVB6Z0FBQUFBQUFBQXhBQlkuIiwic2NwIjoiRVdTLkFjY2Vzc0FzVXNlci5BbGwiLCJzaWQiOiI5Mzk2NDg1Mi0wMjY3LTRhNjAtOGViNC1lYTVjZWViZmI1MzYiLCJzaWduaW5fc3RhdGUiOlsia21zaSJdLCJzdWIiOiJwdEQwTXhhVFhfektpNUZfcGVTSVFubGZzNDRsMm9EdHBBczZQM2RoZWFBIiwidGlkIjoiYjUyODAzNjEtYmRiMy00OWQ1LTk2NTUtZDQxODRlY2UyZjYzIiwidW5pcXVlX25hbWUiOiJrLmhlaW1lckBiYXNlNGl0LmNvbSIsInVwbiI6ImsuaGVpbWVyQGJhc2U0aXQuY29tIiwidXRpIjoieTJ6bkg0ZTNKVTItdWZ4RGhhdFVBQSIsInZlciI6IjEuMCIsIndpZHMiOlsiYjc5ZmJmNGQtM2VmOS00Njg5LTgxNDMtNzZiMTk0ZTg1NTA5Il19.K6xGoZEMW19JsC-ZSrnUrgF8s8eQv_qwFBZzDELMHnikgZzqDwUEd7_41eAL5hDiKMLTfOfqMrjoVqHViqMic7wyY1QOlx9Kzz6vYTuqMFwYOdevwhEAOgH7uMzCE08c-Wu35UPV_vxWqZfGLLRgJHqTFaUWz0kkdtXk1vLs5MDvWL8CYwXq8gHzM2Mhh8wtB-ZIrjSAHtsrSt43KkBe-cP5PJvqvsyvm4lf-ajKxQtFj0Q4uc8YcCume0suiBgCltxuMcz4uP4OAFgGrbZ04BB33GLDwwFKcR9fs524k2D9kp-0Nf3THDPQYLpraGyPViwfBHdUcDC6BBb40-b0sQ" ist ungültig.
What am i doing wrong?
Greetings, Karsten.

copying google doc file in shared drive using c#

We have been using google drive for a while now to select an existing doc, owned by one of our employees, copy it and then merge data into placeholder fields to then download a pdf version of the doc. It's been working fine except now other employees want access to the created docs. So when we give them a link, the original employee has to give them access. We want to move the docs to a shared drive where all employees can see anything in the shared drive. From what I could find in google on this, it looks like we need to set the SupportsAllDrives property to true on the request. However, I can't find that property on any of the objects that we're creating when copying the base document. Because of this I keep getting a 404 from google when trying to copy the file. Can anyone suggest how to get this working?
var secrets = new ClientSecrets
{
ClientId = GoogleCredentials.accesskey,
ClientSecret = GoogleCredentials.secretkey
};
var refreshToken = _credService.GetRefreshToken();
if (string.IsNullOrEmpty(refreshToken))
{
throw new Exception("Missing google refresh token for google doc processor task.");
}
var token = new TokenResponse { RefreshToken = refreshToken };
var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets
}),
"user",
token);
var docService = new DocsService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials,
ApplicationName = "Contract Merge"
});
var driveService = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials,
ApplicationName = "Contract Merge"
});
var newTitle = "Agreement for " + contract.FirstName + " " + contract.LastName + " " + DateTime.Now.Month.ToString() + "-" + DateTime.Now.Day.ToString() + "-" + DateTime.Now.Year.ToString();
var newFile = new google.Apis.Drive.v2.Data.File { Title = newTitle };
var documentCopyFile = driveService.Files.Copy(newFile, GoogleConstants.TemplateDocId).Execute();
As you can see in the C# library reference, an optional query parameter like supportsAllDrives (see query parameters on the API docs) is handled by a property of the CopyRequest class.
Therefore, after building the CopyRequest, but before executing it, you have to set the property SupportsAllDrives to true, as shown here:
FilesResource.CopyRequest copyRequest = driveService.Files.Copy(newFile, GoogleConstants.TemplateDocId);
copyRequest.SupportsAllDrives = true;
copyRequest.Execute();
Reference:
Class FilesResource.CopyRequest

Store authentification data in MVC

I have created a custom Authorize attribute where I use the Office Graph to get AAD groups the current user is member of, and based on those I reject or authorize the user. I want to save the groups, because the call to Office Graph takes some performance. What would be the correct way to save that kind of data? I can see some people saves it to a SQL server, but then I would need to ensure cleanup etc.
Also I can see in some threads the session state is stated to be a bad choice due to concurrency. So the question is what options do you have to store this kind of information?
All suggestions are welcome.
If you were only using the group_id info, there is no need to use Office Graph and store it at all. We can enable Azure AD issue the groups claims by change the manifest of Azure AD like below:(refer this code sample)
"groupMembershipClaims": "All",
And if you are also using other info about groups, you can store these info into claims. Here is a code sample that add the name of groups into claims for your reference:
AuthorizationCodeReceived = async context =>
{
ClientCredential credential = new ClientCredential(ConfigHelper.ClientId, ConfigHelper.AppKey);
string userObjectId = context.AuthenticationTicket.Identity.FindFirst(Globals.ObjectIdClaimType).Value;
AuthenticationContext authContext = new AuthenticationContext(ConfigHelper.Authority, new TokenDbCache(userObjectId));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
context.Code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, ConfigHelper.GraphResourceId);
ActiveDirectoryClient graphClient = new ActiveDirectoryClient(new Uri(ConfigHelper.GraphServiceRoot),
async () => { return await Task.FromResult(result.AccessToken); }
);
try
{
foreach (var groupClaim in context.AuthenticationTicket.Identity.FindAll("groups"))
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri($"https://graph.windows.net/adfei.onmicrosoft.com/groups/{groupClaim.Value}?api-version=1.6"),
Method = HttpMethod.Get,
};
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage httpResponse = httpClient.SendAsync(request).Result;
var retJSON = httpResponse.Content.ReadAsStringAsync().Result;
var dict = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(retJSON);
((ClaimsIdentity)context.AuthenticationTicket.Identity).AddClaim(new Claim("groupName", dict["displayName"].ToString()));
}
}
}
catch (Exception ex)
{
}
},
Then we can these info from controller using the code below:
ClaimsPrincipal.Current.FindAll("groupName")

Upload file on Google drive

I am trying to upload an image on google drive using webapi. I copied the following chunk from Google drive doc but I am getting an error.
Here is the code:
var clientSecret = ConfigurationManager.AppSettings["GoogleDriveClientSecret"];
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret },
scopes, Environment.UserName, CancellationToken.None).Result;
var service = new DriveService(new BaseClientService.Initializer() { HttpClientInitializer = credential });
var folderId = "0B2bBiMQICgHCMlp6OUxuSHNaZFU";
var fileMetadata = new File()
{
Name = "photo.jpg",
Parents = new List<string>
{
folderId
}
};
FilesResource.CreateMediaUpload request;
using (var stream = new System.IO.FileStream("files/photo.jpg",
System.IO.FileMode.Open))
{
request = service.Files.Create(
fileMetadata, stream, "image/jpeg");
request.Fields = "id";
request.Upload();
}
var file = request.ResponseBody;
Now I am getting 2 errors in this code. First "Cannot resolve symbol Upload" at request.Upload() and second "Cannot resolve symbol ResponseBody" at request.ResponseBody
Any help?
You may refer with this related thread. The adding the attribute Inherits="Library.Account.RootVerifyUsers". Based from this reference, Visual Studio can often get confused about things like this. It is recommended to close Visual Studio and reopen it. Also, closing all open instances of VS may be required.

Google API Calender v3 Event Insert via Service Account using Asp.Net MVC

I have been trying to insert a Google calendar event via Google service account that was created for an app in my dev console, but I am continually getting a helpless 404 response back on the Execute method. In the overview of the dev console I can see that the app is getting requests because there are instances of errors on the calendar.events.insert method. There is no information on what is failing. I need this process to use the Service account process instead of OAuth2 so as to not require authentication each time a calendar event needs to be created.
I have set up the service account, given the app a name, have the p12 file referenced in the project. I've also, gone into a personal calendar and have shared with the service account email address. Also, beyond the scope of this ticket, I have created a secondary app, through an administration account and have granted domain wide access to the service account only to receive the same helpless 404 error that this is now giving.
Error Message: Google.Apis.Requests.RequestError
Not Found [404]
Errors [Message[Not Found] Location[ - ] Reason[notFound] Domain[global]
Any help identifying a disconnect or error would be greatly appreciated.
var URL = #"https://www.googleapis.com/calendar/v3/calendars/testcalendarID.com/events";
string serviceAccountEmail = "createdserviceaccountemailaq#developer.gserviceaccount.com";
var path = Path.Combine(HttpRuntime.AppDomainAppPath, "Files/myFile.p12");
var certificate = new X509Certificate2(path, "notasecret",
X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { Google.Apis.Calendar.v3.CalendarService.Scope.Calendar },
}.FromCertificate(certificate));
BaseClientService.Initializer initializer = new
BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Test App"
};
Google.Apis.Calendar.v3.CalendarService calservice = new Google.Apis.Calendar.v3.CalendarService(initializer);
string timezone = System.TimeZone.CurrentTimeZone.StandardName;
var calendarEvent = new Event()
{
Reminders = new Event.RemindersData()
{
UseDefault = true
},
Summary = title,
Description = description,
Location = location,
Start = new EventDateTime()
{
//DateTimeRaw = "2014-12-24T10:00:00.000-07:00",
DateTime = startDateTime,
TimeZone = "America/Phoenix"
},
End = new EventDateTime()
{
//DateTimeRaw = "2014-12-24T11:00:00.000-08:00",
DateTime = endDateTime,
TimeZone = "America/Phoenix"
},
Attendees = new List<EventAttendee>()
{
new EventAttendee()
{
DisplayName = "Joe Shmo",
Email = "joeshmoemail#email.com",
Organizer = false,
Resource = false
}
}
};
var insertevent = calservice.Events.Insert(calendarEvent, URL);
var requestedInsert = insertevent.Execute();
I had the same problem. The solution was to add an email client, whose calendar event you want to send.
Credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = Scopes,
User = "example_client_email#gmail.com"
}.FromCertificate(certificate));
So I found out that for this to work, You need to make sure that you access the google.Admin account for referencing the service account Client ID of the app you created.
Another thing that helps is making sure the timezone is in the following format "America/Phoenix"
I have now successfully created events through the service account WITHOUT authentication.

Resources