C# Google drive sdk. How to get a list of google drive folders? - c#

I'm writing a program to allow a user to upload files to their Google Drive account. I have the upload part working and am using OAuth2. The issue I'm currently having is getting a list of folders from the users Drive account.
I found some code that is supposed to do this using the .setUserCredentials method, but it doesn't work:
DocumentsService service1 = new DocumentsService("project");
service1.setUserCredentials("user","pass");
FolderQuery query1 = new FolderQuery();
// Make a request to the API and get all documents.
DocumentsFeed feed = service1.Query(query1);
// Iterate through all of the documents returned
foreach (DocumentEntry entry in feed.Entries)
{
var blech = entry.Title.Text;
}
Nothing is returned. Ideally, I want to use OAuth2 to do this. I've been trying with the following code, trying to set the authentication token, but I always get denied access:
String CLIENT_ID = "clientid";
String CLIENT_SECRET = "secretid";
var docprovider = new NativeApplicationClient(GoogleAuthenticationServer.Description, CLIENT_ID, CLIENT_SECRET);
var docstate = GetDocAuthentication(docprovider);
DocumentsService service1 = new DocumentsService("project");
service1.SetAuthenticationToken(docstate.RefreshToken);
FolderQuery query1 = new FolderQuery();
DocumentsFeed feed = service1.Query(query1); //get error here
// Iterate through all of the documents returned
foreach (DocumentEntry entry in feed.Entries)
{
// Print the title of this document to the screen
var blech = entry.Title.Text;
}
..
private static IAuthorizationState GetDocAuthentication(NativeApplicationClient client)
{
const string STORAGE = "storagestring";
const string KEY = "keystring";
string scope = "https://docs.google.com/feeds/default/private/full/-/folder";
// Check if there is a cached refresh token available.
IAuthorizationState state = AuthorizationMgr.GetCachedRefreshToken(STORAGE, KEY);
if (state != null)
{
try
{
client.RefreshToken(state);
return state; // Yes - we are done.
}
catch (DotNetOpenAuth.Messaging.ProtocolException ex)
{
}
}
// Retrieve the authorization from the user.
state = AuthorizationMgr.RequestNativeAuthorization(client, scope);
AuthorizationMgr.SetCachedRefreshToken(STORAGE, KEY, state);
return state;
}
Specifically, I get "Execution of request failed: https://docs.google.com/feeds/default/private/full/-/folder - The remote server returned an error: (401) Unauthorized".
I've also tried:
var docauth = new OAuth2Authenticator<NativeApplicationClient>(docprovider, GetDocAuthentication);
DocumentsService service1 = new DocumentsService("project");
service1.SetAuthenticationToken(docauth.State.AccessToken);
but "State" is always null, so I get a null object error. What am I doing wrong and how is this done?

You should use the Drive SDK, not the Documents List API, which allows you to list folders. You can use "root" as a folderId if you want to list the root directory.

I actually implemented the v3 version of the GDrive SDK for .NET and needed to search for folders as well.
I prefer requesting uniquely all folders instead of getting all files and then performing a LinQ query to keep just the folders.
This is my implementation:
private async Task<bool> FolderExistsAsync(string folderName)
{
var response = await GetAllFoldersAsync();
return response.Files
.Where(x => x.Name.ToLower() == folderName.ToLower())
.Any();
}
private async Task<Google.Apis.Drive.v3.Data.FileList> GetAllFoldersAsync()
{
var request = _service.Files.List();
request.Q = "mimeType = 'application/vnd.google-apps.folder'";
var response = await request.ExecuteAsync();
return response;
}
You could request the name on the Q this way as well:
request.Q = $"mimeType = 'application/vnd.google-apps.folder' and name = '{folderName}'";
Which would lead and simplify things to (obviating null checking):
private async Task<bool> FolderExistsAsync(string folderName)
{
var response = await GetDesiredFolder(folderName);
return response.Files.Any();
}
private async Task<FileList> GetDesiredFolder(string folderName)
{
var request = _service.Files.List();
request.Q = $"mimeType = 'application/vnd.google-apps.folder' and name = '{folderName}'";
var response = await request.ExecuteAsync();
return response;
}

private IEnumerable<DocumentEntry> GetFolders(string id) {
if (IsLogged) {
var query = new FolderQuery(id)
{
ShowFolders = true
};
var feed = GoogleDocumentsService.Query(query);
return feed.Entries.Cast<DocumentEntry>().Where(x => x.IsFolder).OrderBy(x => x.Title.Text);
}
return null;
}
...
var rootFolders = GetFolders("root");
if (rootFolders != null){
foreach(var folder in rootFolders){
var subFolders = GetFolders(folder.ResourceId);
...
}
}
where GoogleDocumentsService is a instance of DocumentsService and IsLogged is a success logged flag.

I got this way to get list of folders from google drive
FilesResource.ListRequest filelist= service.Files.List();
filelist.Execute().Items.ToList().Where(x => x.MimeType == "application/vnd.google-apps.folder").ToList()

Related

Authenticate with OneDrive SDK in a c# console project

I'm trying to create a program that will download all my OneNote files from OneDrive. But when I try to authenticate using msaAuthenticationProvider a white window appears and then nothing happens. I think the window is supposed to be the Microsoft login, but nothing appears in it.
Here's my code:
string[] scopes = new string[] {
"onedrive.readonly",
"wl.signin"
};
var msaAuthenticationProvider = new MsaAuthenticationProvider(
clientId,
returnURL,
scopes);
await msaAuthenticationProvider.AuthenticateUserAsync();
var client = new OneDriveClient(URL, msaAuthenticationProvider);
It gets to the AuthenticateUserAsync method, then the window apperas, and after that nothing happens.
I'm also not sure what the returnURL is supposed to be because all examples where either for an app version or just said return URL without giving any examples.
sorry for the delay. Have you tried this method:
msaAuthenticationProvider.RestoreMostRecentFromCacheOrAuthenticateUserAsync();
Edit : If the last known connection token is usable, this method can be used to authenticate the user without prompt it. So, this restore the last authentication cache if it can or prompt the user to give his login and password. This can replace the already used AuthenticateUserAsync method. I had the same issue and this method solved it.
Edit 2 : The OneDrive SDK documentation is very poor, I found this myself fiercely as I found that you can get the connection token (to save it for example) and inject it when you need like that in an async task :
if (_OneDriveCacheBlob == null)
{
bool needtosaveblob = true;
_OneDriveCacheBlob = null;
CredentialCache cc = new CredentialCache();
_OneDriveCacheBlob = GetUser(CurrentUserName).OneDriveAuthProviderBlob;
if (_OneDriveCacheBlob != null)
{
cc.InitializeCacheFromBlob(_OneDriveCacheBlob);
needtosaveblob = false;
}
MsaAuthenticationProvider msaAuthProvider = new MsaAuthenticationProvider(OneDriveClass.clientId, OneDriveClass.returnUrl, scopes, cc);
int timeout = 15;
_ = Task.Run(() => WaitForODConnection(msaAuthProvider));
while (!WaitForODConnectionExecuted)
{
if (timeout <= 0)
break;
await Task.Delay(TimeSpan.FromSeconds(1));
timeout -= 1;
}
WaitForODConnectionExecuted = false;
if (timeout <= 0)
{
// Request for reconnection to OneDrive because of invalid Blob
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
//This method requests a new login by a simple msaAuthProvider.AuthenticateUserAsync() call from a new instance of MsaAuthenticationProvider and a new instance of CredentialCache.
//ChangeOneDriveAccount();
});
}
else
{
_OneDriveClient = new OneDriveClient(OneDriveClass.basUrl, msaAuthProvider);
}
string accessToken = msaAuthProvider.CurrentAccountSession.AccessToken;
JObject json = await GetUserInfos(msaAuthProvider.CurrentAccountSession.AccessToken);
if (json != null)
{
// If you need
oneDriveUserName = json["name"].ToString();
oneDriveEmail = json["emails"]["account"].ToString();
}
else
{
//Unable to get OneDrive user informations;
}
if (needtosaveblob)
{
_OneDriveCacheBlob = cc.GetCacheBlob();
//You can save _OneDriveCacheBlob to reuse it later;
}
}
To get the user infos :
/// <summary>
/// Return User informations as a JObject. To get username and email, if return isn't null :
/// username = json["name"].ToString();
/// email = json["emails"]["account"].ToString();
/// </summary>
/// <param name="accessToken">accesstoken of Onedrive account</param>
/// <returns>JObject value</returns>
public static async Task<JObject> GetUserInfos(string accessToken)
{
JObject json = null;
Uri uri = new Uri($"https://apis.live.net/v5.0/me?access_token={accessToken}");
System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(uri);
//user info returnd as JSON
string jsonUserInfo = await result.Content.ReadAsStringAsync();
if (jsonUserInfo != null)
{
json = JObject.Parse(jsonUserInfo);
//username = json["name"].ToString();
//email = json["emails"]["account"].ToString();
}
return json;
}
And because the OneDrive method never expires if the token is no longer usable :
bool WaitForODConnectionExecuted = false;
private async Task WaitForODConnection(MsaAuthenticationProvider msaAuthProvider)
{
await msaAuthProvider.RestoreMostRecentFromCacheOrAuthenticateUserAsync();
WaitForODConnectionExecuted = true;
}
It was not funny and I think my code is not clean so do not use it as it is without working a little on it.

List secrets in a KeyVault without logging in for every secret?

I've successfully managed to list all of the secrets in an Azure KeyVault - however I need to make a call to get a token each time I want to get the next secret.
How do I store the credentials so I only have to login once during the loop?
public async Task<List<string>> getsecretslist(string url)
{
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken));
List<string> secretlist = new List<string>();
var all = kv.GetSecretsAsync(url);
var myId = "";
foreach (Microsoft.Azure.KeyVault.Models.SecretItem someItem in all.Result)
{
myId = someItem.Id;
var mOtherThing = someItem.Identifier;
var yep = await kv.GetSecretAsync(mOtherThing.ToString());
secretlist.Add(yep.Value);
}
return secretlist;
}
In your GetToken callback method you need to cache the access token as long as it is valid and not expired. Then your callback will return the cached access token instead of doing the authentication again. The following code snippet will use the ADAL default token cache (e.g. TokenCache.DefaultShared).
public static async Task<string> GetToken(string authority, string resource, string scope)
{
var assertionCert = new ClientAssertionCertificate(clientId, certificate);
var context = new AuthenticationContext(authority, TokenCache.DefaultShared);
var result = await context.AcquireTokenAsync(resource, assertionCert).ConfigureAwait(false);
return result.AccessToken;
}
The best way that i found is to save the token you obtained in your GetToken function, for example:
var authenticationContext = new AuthenticationContext(authority, TokenCache.DefaultShared);
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource, KeyVaultUserClientId, new Uri(KeyVaultRedirectUri), new PlatformParameters(PromptBehavior.SelectAccount)).ConfigureAwait(false);
return authenticationResult.AccessToken;
Then i simply altered the getter for the client so it will check for the expiry, if its still valid (should have expiration of 60 minutes) it will return a simpler client which returns the lastAuthenticationResult
private static KeyVaultClient KeyVaultClient
{
get
{
if (lastAuthenticationResult != null && DateTime.UtcNow.AddSeconds(5) < lastAuthenticationResult.ExpiresOn)
{
if (m_cachedKeyVaultClient != null)
{
return m_cachedKeyVaultClient;
}
else
{
return new KeyVaultClient(getCachedToken);
}
}
if (m_keyVaultClient == null)
m_keyVaultClient = new KeyVaultClient(GetAccessTokenAsync);
return m_keyVaultClient;
}
}
private static async Task<string> getCachedToken(string authority, string resource, string scope)
{
return lastAuthenticationResult.AccessToken;
}
You don't need to call GetSecretAsync inside your loop. The secrets are already included in your Result set from calling GetSecretsAsync. This is why you are being authenticated repeatedly.
Here is a simple change to your loop to do what you are looking for.
var all = kv.GetSecretsAsync(url).GetAwaiter().GetResult();
foreach (var secret in all.Value)
{
secretlist.Add(secret.Id);
}

reading messages from fb

I successfully login to facebook but while trying to read the mailbox I got the following error: {"(OAuthException - #298) (#298) Requires extended permission: read_mailbox"}
If I add that scope in the URL;
var destinationUrl =
String.Format(
"https://www.facebook.com/dialog/oauth?client_id={0}&scope={1}&display=popup&redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token",
AppID, //client_id
"user_posts" //scope
);
and try to get mails:
private void FaceBookScrapper()
{
var client = new FacebookClient(_fbToken);
var input = new List<FbMesseage>();
if (client != null)
{
dynamic result = client.Get("me/inbox", null);
foreach (var item in result.inbox.data)
{
if (item.unread > 0 || item.unseen > 0)
{
foreach (var message in item.comments.data)
{
input.Add(new FbMesseage
{
Id = message.id,
FromName = message.from.name,
FromId = message.from.id,
Text = message.message,
CreatedDate = message.created_time
});
}
FbMesseageCollectionViewModelIns.LoadData(input);
}
}
}
}
}
}
That permission doesn't exist any more - it has been removed, together with the /user/inbox endpoint.
https://developers.facebook.com/docs/apps/changelog#v2_4_deprecations mentions this, and https://developers.facebook.com/docs/graph-api/reference/v2.5/user/inbox as well.
Under the latter URL, it says it right on the very top of the page:
This document refers to a feature that was removed after Graph API v2.4.
There is no way any more to access a user's inbox via API.

Load text from web during xml parsing in Windows 8 Async

I have a unique issue, I want to get the name of an application from it's AppID while I convert an XML file into objects. This is the code I'm using at present:
if (xdoc.Element("Application") != null)
{
var data = from query in xdoc.Descendants("AppID")
select new APP
{
AppID = query.Value,
AppName = GetName(query.Value).ToString(),
};
itemGridView.DataContext = data;
}
This is the code I'm using to convert the GUID into an app name using Microsoft's Store API. I can confirm that it does return the app name. I'm just unsure how I can get this to display.
private async Task<string> GetName(string guid)
{
try
{
string link = "https://services.apps.microsoft.com/browse/6.2.9200-1/615/en-NZ_en-NZ/c/NZ/cp/10005001/Apps/{0}";
string url = string.Format(link, guid);
var httpClient = new HttpClient();
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);
var response = await httpClient.SendAsync(httpRequestMessage);
var xmlString = await response.Content.ReadAsStringAsync();
XmlDocument NameXML = new XmlDocument();
NameXML = await XmlDocument.LoadFromUriAsync(new Uri(url));
string sAppName = NameXML.GetElementsByTagName("T")[0].ChildNodes[0].NodeValue.ToString();
return sAppName;
}
catch(Exception)
{
return guid;
}
}
I think my problem is with the async / await tasks. I've just been exposed to it now... how would I load up the App Name alongside the AppID when I parse the xml file?
The output that's being displayed when I run the app is "System.Threading.Tasks.Task[System.String]" (The objects load and the links and everything works fine, its just that the above is displayed instead of the app name).
I've been debugging using breakpoints, it appears that the GetName method only seems to be triggered later on, I'm not sure however.
Try to change this line :
AppName = GetName(query.Value).ToString(),
To this :
AppName = await GetName(query.Value),
GetName will return Task<string> instead of string when not awaited. And the method where above code resides required to be async because of using await inside that method :
private async void SomeMethod()
{
....
if (xdoc.Element("Application") != null)
{
var data = from query in xdoc.Descendants("AppID")
select new APP
{
AppID = query.Value,
AppName = await GetName(query.Value),
};
itemGridView.DataContext = data;
}
....
}
UPDATE :
As you already noticed, LINQ has very limited support for async/await currently. So to workaround this limitation, we can use normal for loop to avoid calling async function inside LINQ :
private async void SomeMethod()
{
....
if (xdoc.Element("Application") != null)
{
var query = from query in xdoc.Descendants("AppID")
select query.Value;
var data = new List<App>();
foreach (var q in query)
{
data.Add(new App{ AppId = q, AppName = await GetName(q) });
}
itemGridView.DataContext = data;
}
....
}

Accessing Google Docs with GData

Working Platform: ASP.NET 4.0 C# ( Framework Agnostic )
Google GData is my dependency
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Documents;
I have two pages Auth and List.
Auth redirects to Google Server like this
public ActionResult Auth()
{
var target = Request.Url.ToString().ToLowerInvariant().Replace("auth", "list");
var scope = "https://docs.google.com/feeds/";
bool secure = false, session = true;
var authSubUrl = AuthSubUtil.getRequestUrl(target, scope, secure, session);
return new RedirectResult(authSubUrl);
}
Now it reaches the List Page if Authentication is successful.
public ActionResult List()
{
if (Request.QueryString["token"] != null)
{
String singleUseToken = Request.QueryString["token"];
string consumerKey = "www.blahblah.net";
string consumerSecret = "my_key";
string sessionToken = AuthSubUtil.exchangeForSessionToken(singleUseToken, null).ToString();
var authFactory = new GOAuthRequestFactory("writely", "qwd-asd-01");
authFactory.Token = sessionToken;
authFactory.ConsumerKey = consumerKey;
authFactory.ConsumerSecret = consumerSecret;
//authFactory.TokenSecret = "";
try
{
var service = new DocumentsService(authFactory.ApplicationName) { RequestFactory = authFactory };
var query = new DocumentsListQuery();
query.Title = "project";
var feed = service.Query(query);
var result = feed.Entries.ToList().ConvertAll(a => a.Title.Text);
return View(result);
}
catch (GDataRequestException gdre)
{
throw;
}
}
}
This fails at the line var feed = service.Query(query); with the error
Execution of request failed: https://docs.google.com/feeds/default/private/full?title=project
The HttpStatusCode recieved on the catch block is HttpStatusCode.Unauthorized
What is wrong with this code? Do I need to get TokenSecret? If so how?
You need to request a token from Google and use it to intialize your DocumentsService instance.
Here's an example using Google's ContactsService. It should be the same for the DocumentsService.
Service service = new ContactsService("My Contacts Application");
service.setUserCredentials("your_email_address_here#gmail.com", "yourpassword");
var token = service.QueryClientLoginToken();
service.SetAuthenticationToken(token);
But as you mentioned, you are using AuthSub. I jumped the gun a bit too fast.
I see that you are requesting a session token. According to the documentation of the API you must use the session token to authenticate requests to the service by placing the token in the Authorization header. After you've set the session token, you can use the Google Data APIs client library.
Here's a complete example (by Google) on how to use AuthSub with the .NET client library:
http://code.google.com/intl/nl-NL/apis/gdata/articles/authsub_dotnet.html
Let me include a shortened example:
GAuthSubRequestFactory authFactory =
new GAuthSubRequestFactory("cl", "TesterApp");
authFactory.Token = (String) Session["token"];
CalendarService service = new CalendarService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
EventQuery query = new EventQuery();
query.Uri = new Uri("http://www.google.com/calendar/feeds/default/private/full");
EventFeed calFeed = service.Query(query);
foreach (Google.GData.Calendar.EventEntry entry in calFeed.Entries)
{
//...
}
And if I see correctly your example code pretty follows the same steps, except that you set the ConsumerKey and ConsumerSecret for the AuthFactory which is not done in the example by Google.
Used the 3-legged OAuth in the Google Data Protocol Client Libraries
Sample Code
string CONSUMER_KEY = "www.bherila.net";
string CONSUMER_SECRET = "RpKF7ykWt8C6At74TR4_wyIb";
string APPLICATION_NAME = "bwh-wssearch-01";
string SCOPE = "https://docs.google.com/feeds/";
public ActionResult Auth()
{
string callbackURL = String.Format("{0}{1}", Request.Url.ToString(), "List");
OAuthParameters parameters = new OAuthParameters()
{
ConsumerKey = CONSUMER_KEY,
ConsumerSecret = CONSUMER_SECRET,
Scope = SCOPE,
Callback = callbackURL,
SignatureMethod = "HMAC-SHA1"
};
OAuthUtil.GetUnauthorizedRequestToken(parameters);
string authorizationUrl = OAuthUtil.CreateUserAuthorizationUrl(parameters);
Session["parameters"] = parameters;
ViewBag.AuthUrl = authorizationUrl;
return View();
}
public ActionResult List()
{
if (Session["parameters"] != null)
{
OAuthParameters parameters = Session["parameters"] as OAuthParameters;
OAuthUtil.UpdateOAuthParametersFromCallback(Request.Url.Query, parameters);
try
{
OAuthUtil.GetAccessToken(parameters);
GOAuthRequestFactory authFactory = new GOAuthRequestFactory("writely", APPLICATION_NAME, parameters);
var service = new DocumentsService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
var query = new DocumentsListQuery();
//query.Title = "recipe";
var feed = service.Query(query);
var docs = new List<string>();
foreach (DocumentEntry entry in feed.Entries)
{
docs.Add(entry.Title.Text);
}
//var result = feed.Entries.ToList().ConvertAll(a => a.Title.Text);
return View(docs);
}
catch (GDataRequestException gdre)
{
HttpWebResponse response = (HttpWebResponse)gdre.Response;
//bad auth token, clear session and refresh the page
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
Session.Clear();
Response.Write(gdre.Message);
}
else
{
Response.Write("Error processing request: " + gdre.ToString());
}
throw;
}
}
else
{
return RedirectToAction("Index");
}
}
This 2-legged sample never worked for me for google docs.

Categories

Resources