28

I'm using the Google .NET API to get analytics data from google analytics.

this is me code to start the authentication:

IAuthorizationCodeFlow flow =
    new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = new ClientSecrets
            {
                ClientId = googleApiClientId,
                ClientSecret = googleApiClientSecret
            },
            Scopes = new[] { 
                Google.Apis.Analytics.v3.AnalyticsService.Scope.AnalyticsReadonly
            },
            DataStore = new Google.Apis.Util.Store.FileDataStore("Test_GoogleApi")
        });

it users the FileDataStore which stores in local user profile as a file. I'm running this code inside an ASP.NET application so I can't really use that FileDataStore so what I will need is some other way to get the data.

Google.Apis.Util.Store contains only the FileDataStore and an interface of IDataStore. Before I go and implement my own DataStore - are there any other DataStore objects out there available for download?

Thanks

developer82
  • 13,237
  • 21
  • 88
  • 153

5 Answers5

34

The source for Google's FileDataStore is available here.

I've written a simple Entity Framework (version 6) implementation of IDataStore as shown below.

If you're looking to put this in a separate project, as well as EF you'll need the Google.Apis.Core nuget package installed.

public class Item
{
    [Key]
    [MaxLength(100)]
    public string Key { get; set; }

    [MaxLength(500)]
    public string Value { get; set; }
}

public class GoogleAuthContext : DbContext
{
    public DbSet<Item> Items { get; set; }
}

public class EFDataStore : IDataStore
{
    public async Task ClearAsync()
    {
        using (var context = new GoogleAuthContext())
        {
            var objectContext = ((IObjectContextAdapter)context).ObjectContext;
            await objectContext.ExecuteStoreCommandAsync("TRUNCATE TABLE [Items]");
        }
    }

    public async Task DeleteAsync<T>(string key)
    {
        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentException("Key MUST have a value");
        }

        using (var context = new GoogleAuthContext())
        {
            var generatedKey = GenerateStoredKey(key, typeof(T));
            var item = context.Items.FirstOrDefault(x => x.Key == generatedKey);
            if (item != null)
            {
                context.Items.Remove(item);
                await context.SaveChangesAsync();
            }
        }
    }

    public Task<T> GetAsync<T>(string key)
    {
        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentException("Key MUST have a value");
        }

        using (var context = new GoogleAuthContext())
        {
            var generatedKey = GenerateStoredKey(key, typeof(T));
            var item = context.Items.FirstOrDefault(x => x.Key == generatedKey);
            T value = item == null ? default(T) : JsonConvert.DeserializeObject<T>(item.Value);
            return Task.FromResult<T>(value);
        }
    }

    public async Task StoreAsync<T>(string key, T value)
    {
        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentException("Key MUST have a value");
        }

        using (var context = new GoogleAuthContext())
        {
            var generatedKey = GenerateStoredKey(key, typeof (T));
            string json = JsonConvert.SerializeObject(value);

            var item = await context.Items.SingleOrDefaultAsync(x => x.Key == generatedKey);

            if (item == null)
            {
                context.Items.Add(new Item { Key = generatedKey, Value = json});
            }
            else
            {
                item.Value = json;
            }

            await context.SaveChangesAsync();
        }
    }

    private static string GenerateStoredKey(string key, Type t)
    {
        return string.Format("{0}-{1}", t.FullName, key);
    }
}
Uroš Goljat
  • 1,806
  • 14
  • 15
Simon Ness
  • 2,272
  • 22
  • 28
  • how can i use this efdatastore? – Robel Haile Jul 06 '16 at 16:26
  • Works at local machine, but I got "Format of the initialization string does not conform to specification starting at index 0" on Azure – Kirill Jan 31 '18 at 13:40
  • @Kirill Looks like https://stackoverflow.com/questions/8243008/format-of-the-initialization-string-does-not-conform-to-specification-starting-a – Simon Ness Jan 31 '18 at 20:47
  • 2
    If you want to use this along with a non client based service (issue calendar events hours after the client leaves your site) you need to hang onto the refresh token. You only get a refresh token with your first token. Subsequent refreshes will wipe out the refresh token using the above StoreAsync. You never notice it on the web app because the token refreshing is handled through redirection Site > https://accounts.google.com/signin/oauth > back to site. – bryjohns Feb 23 '18 at 21:10
  • Worked for me but I had to increase the size of the field Value – default Oct 12 '20 at 18:14
7

I know this question was answered some time ago, however I thought this would be a good place to share my findings for those having similar difficulty finding examples. I have found it to be difficult to find documentation/samples on using the Google APIs .Net library for an Desktop or MVC Web application. I finally found a good example in the tasks example you can find in the samples repository on the Google Project site here <- That really really helped me out.

I ended up snagging the source for the FileDataStore and created an AppDataStore class and placed it in my App_Code folder. You can find the source here, though it was a simple change really - changing the folder to point to ~/App_Data instead.

The last piece of the puzzle I'm looking into figuring out is getting the offline_access token.


Edit: Here's the code for convenience:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.IO;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Util.Store;
using Google.Apis.Json;

namespace Google.Apis.Util.Store {
    public class AppDataFileStore : IDataStore {
        readonly string folderPath;
        /// <summary>Gets the full folder path.</summary>
        public string FolderPath { get { return folderPath; } }

        /// <summary>
        /// Constructs a new file data store with the specified folder. This folder is created (if it doesn't exist
        /// yet) under <see cref="Environment.SpecialFolder.ApplicationData"/>.
        /// </summary>
        /// <param name="folder">Folder name.</param>
        public AppDataFileStore(string folder) {
            folderPath = Path.Combine(HttpContext.Current.Server.MapPath("~/App_Data/"), folder);
            if (!Directory.Exists(folderPath)) {
                Directory.CreateDirectory(folderPath);
            }
        }

        /// <summary>
        /// Stores the given value for the given key. It creates a new file (named <see cref="GenerateStoredKey"/>) in
        /// <see cref="FolderPath"/>.
        /// </summary>
        /// <typeparam name="T">The type to store in the data store.</typeparam>
        /// <param name="key">The key.</param>
        /// <param name="value">The value to store in the data store.</param>
        public Task StoreAsync<T>(string key, T value) {
            if (string.IsNullOrEmpty(key)) {
                throw new ArgumentException("Key MUST have a value");
            }

            var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
            var filePath = Path.Combine(folderPath, GenerateStoredKey(key, typeof(T)));
            File.WriteAllText(filePath, serialized);
            return TaskEx.Delay(0);
        }

        /// <summary>
        /// Deletes the given key. It deletes the <see cref="GenerateStoredKey"/> named file in
        /// <see cref="FolderPath"/>.
        /// </summary>
        /// <param name="key">The key to delete from the data store.</param>
        public Task DeleteAsync<T>(string key) {
            if (string.IsNullOrEmpty(key)) {
                throw new ArgumentException("Key MUST have a value");
            }

            var filePath = Path.Combine(folderPath, GenerateStoredKey(key, typeof(T)));
            if (File.Exists(filePath)) {
                File.Delete(filePath);
            }
            return TaskEx.Delay(0);
        }

        /// <summary>
        /// Returns the stored value for the given key or <c>null</c> if the matching file (<see cref="GenerateStoredKey"/>
        /// in <see cref="FolderPath"/> doesn't exist.
        /// </summary>
        /// <typeparam name="T">The type to retrieve.</typeparam>
        /// <param name="key">The key to retrieve from the data store.</param>
        /// <returns>The stored object.</returns>
        public Task<T> GetAsync<T>(string key) {
            if (string.IsNullOrEmpty(key)) {
                throw new ArgumentException("Key MUST have a value");
            }

            TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
            var filePath = Path.Combine(folderPath, GenerateStoredKey(key, typeof(T)));
            if (File.Exists(filePath)) {
                try {
                    var obj = File.ReadAllText(filePath);
                    tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(obj));
                }
                catch (Exception ex) {
                    tcs.SetException(ex);
                }
            }
            else {
                tcs.SetResult(default(T));
            }
            return tcs.Task;
        }

        /// <summary>
        /// Clears all values in the data store. This method deletes all files in <see cref="FolderPath"/>.
        /// </summary>
        public Task ClearAsync() {
            if (Directory.Exists(folderPath)) {
                Directory.Delete(folderPath, true);
                Directory.CreateDirectory(folderPath);
            }

            return TaskEx.Delay(0);
        }

        /// <summary>Creates a unique stored key based on the key and the class type.</summary>
        /// <param name="key">The object key.</param>
        /// <param name="t">The type to store or retrieve.</param>
        public static string GenerateStoredKey(string key, Type t) {
            return string.Format("{0}-{1}", t.FullName, key);
        }
    }
}

I had to set the approval prompt to force in order to get the offline access token.

var req = HttpContext.Current.Request;
var oAuthUrl = Flow.CreateAuthorizationCodeRequest(new UriBuilder(req.Url.Scheme, req.Url.Host, req.Url.Port, GoogleCalendarUtil.CallbackUrl).Uri.ToString()) as GoogleAuthorizationCodeRequestUrl;
oAuthUrl.Scope = string.Join(" ", new[] { CalendarService.Scope.CalendarReadonly });
oAuthUrl.ApprovalPrompt = "force";
oAuthUrl.State = AuthState;
Sir CodesALot
  • 946
  • 1
  • 15
  • 16
  • Sorry for the delay Craig, yes as a matter of fact I did figure out the offline_access token. I had to add: oAuthUrl.ApprovalPrompt = "force"; – Sir CodesALot Jun 02 '14 at 18:30
  • 5
    You could have achieved the same with `FileDataStore` like this: `new FileDataStore(Server.MapPath("~/App_Data"), true)` – jansokoly Feb 26 '15 at 00:13
  • 2
    Note that this will not work for an Azure web site where you have no file system access. – ChiralMichael Sep 21 '16 at 20:05
3

You basicly need to create your own implimitation of Idatastore and then use that.

IDataStore StoredRefreshToken = new myDataStore();
// Oauth2 Autentication.
using (var stream = new System.IO.FileStream("client_secret.json", System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
 credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
 GoogleClientSecrets.Load(stream).Secrets,
 new[] { AnalyticsService.Scope.AnalyticsReadonly },
 "user", CancellationToken.None, StoredRefreshToken).Result;
}

Check here for a basic example of an implimitation of Idatastore. Google Oauth loading stored refresh token

Update:

Several versions of this can be found in my Authentication sample project on GitHub Google-Dotnet-Samples / Authentication / Diamto.Google.Authentication

Update 2

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
1

There are implementations for Windows 8 applications and Windows Phone available here:

Take a look in the following thread Deploying ASP.NET to Windows Azure cloud, application gives error when running on cloud before you are going to implement your own DataStore.

In the future we might also have a EF DataStore. Remember that it's an open source project so you may implement it and send it to review :) Take a look in our contribution page (https://code.google.com/p/google-api-dotnet-client/wiki/BecomingAContributor)

Community
  • 1
  • 1
peleyal
  • 3,472
  • 1
  • 14
  • 25
1

Our Web App is hosted on Azure so I needed to create an IDataStore for that.

I used Azure Table Storage as our data store.

Here's a gist of my attempt

Feedback and suggestions are welcome

Eilimint
  • 307
  • 1
  • 3
  • 11