Search code examples
c#google-apigoogle-analytics-apigoogle-api-dotnet-clientservice-accounts

Google Analytics Realtime API .Net Client


Does anyone have any example c# code they can share using the Google.Apis.Analytics.v3 API and server authentication to get the current number of users on my website.

Background: I have an MVC internal website that has a support dashboard. It hooks into may different API's to collate some useful data. I have had a request to add the number of users currently on the our website as seen in the Google Analytics web portal.

I don't want to have users sign in using a google account individually, I was hoping I could use the "ServiceCredentials" method but I've got lost.

Appreciate any help.

Thanks


Solution

  • Make sure you grant your service account access in Google analytics at the account level.

    Warning: The quota on the Google Analytics api is 10000 requests per day per view. If you make more then 10000 requests in a day you are going to be locked out. Don't try to refresh the real-time data to often, ideally do it in a background script once a minute or something.

    Auth:

        /// <summary>
        /// Authenticating to Google using a Service account
        /// Documentation: https://developers.google.com/accounts/docs/OAuth2#serviceaccount
        /// </summary>
        /// <param name="serviceAccountEmail">From Google Developer console https://console.developers.google.com</param>
        /// <param name="serviceAccountCredentialFilePath">Location of the .p12 or Json Service account key file downloaded from Google Developer console https://console.developers.google.com</param>
        /// <returns>AnalyticsService used to make requests against the Analytics API</returns>
        public static AnalyticsService AuthenticateServiceAccount(string serviceAccountEmail, string serviceAccountCredentialFilePath)
        {
            try
            {
                if (string.IsNullOrEmpty(serviceAccountCredentialFilePath))
                    throw new Exception("Path to the service account credentials file is required.");
                if (!File.Exists(serviceAccountCredentialFilePath))
                    throw new Exception("The service account credentials file does not exist at: " + serviceAccountCredentialFilePath);
                if (string.IsNullOrEmpty(serviceAccountEmail))
                    throw new Exception("ServiceAccountEmail is required.");
    
                // These are the scopes of permissions you need. It is best to request only what you need and not all of them
                string[] scopes = new string[] { AnalyticsReportingService.Scope.Analytics };             // View your Google Analytics data
    
                // For Json file
                if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".json")
                {
                    GoogleCredential credential;
                    using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
                    {
                        credential = GoogleCredential.FromStream(stream)
                             .CreateScoped(scopes);
                    }
    
                    // Create the  Analytics service.
                    return new AnalyticsService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = "Analytics Service account Authentication Sample",
                    });
                }
                else if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".p12")
                {   // If its a P12 file
    
                    var certificate = new X509Certificate2(serviceAccountCredentialFilePath, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
                    var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
                    {
                        Scopes = scopes
                    }.FromCertificate(certificate));
    
                    // Create the  Analytics service.
                    return new AnalyticsService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = "Analytics Authentication Sample",
                    });
                }
                else
                {
                    throw new Exception("Unsupported Service accounts credentials.");
                }
    
            }
            catch (Exception ex)
            {
                Console.WriteLine("Create service account AnalyticsService failed" + ex.Message);
                throw new Exception("CreateServiceAccountAnalyticsFailed", ex);
            }
        }
    }
    

    Request:

    public static class RealtimeSample
    {
    
        public class RealtimeGetOptionalParms
        {
            /// A comma-separated list of real time dimensions. E.g., 'rt:medium,rt:city'.
            public string Dimensions { get; set; }  
            /// A comma-separated list of dimension or metric filters to be applied to real time data.
            public string Filters { get; set; }  
            /// The maximum number of entries to include in this feed.
            public int Max-results { get; set; }  
            /// A comma-separated list of dimensions or metrics that determine the sort order for real time data.
            public string Sort { get; set; }  
    
        }
    
        /// <summary>
        /// Returns real time data for a view (profile). 
        /// Documentation https://developers.google.com/analytics/v3/reference/realtime/get
        /// Generation Note: This does not always build corectly.  Google needs to standardise things I need to figuer out which ones are wrong.
        /// </summary>
        /// <param name="service">Authenticated analytics service.</param>  
        /// <param name="ids">Unique table ID for retrieving real time data. Table ID is of the form ga:XXXX, where XXXX is the Analytics view (profile) ID.</param>
        /// <param name="metrics">A comma-separated list of real time metrics. E.g., 'rt:activeUsers'. At least one metric must be specified.</param>
        /// <param name="optional">Optional paramaters.</param>        /// <returns>RealtimeDataResponse</returns>
        public static RealtimeData Get(analyticsService service, string ids, string metrics, RealtimeGetOptionalParms optional = null)
        {
            try
            {
                // Initial validation.
                if (service == null)
                    throw new ArgumentNullException("service");
                if (ids == null)
                    throw new ArgumentNullException(ids);
                if (metrics == null)
                    throw new ArgumentNullException(metrics);
    
                // Building the initial request.
                var request = service.Realtime.Get(ids, metrics);
    
                // Applying optional parameters to the request.                
                request = (RealtimeResource.GetRequest)SampleHelpers.ApplyOptionalParms(request, optional);
    
                // Requesting data.
                return request.Execute();
            }
            catch (Exception ex)
            {
                throw new Exception("Request Realtime.Get failed.", ex);
            }
        }
    
    
    }
        public static class SampleHelpers
    {
    
        /// <summary>
        /// Using reflection to apply optional parameters to the request.  
        /// 
        /// If the optonal parameters are null then we will just return the request as is.
        /// </summary>
        /// <param name="request">The request. </param>
        /// <param name="optional">The optional parameters. </param>
        /// <returns></returns>
        public static object ApplyOptionalParms(object request, object optional)
        {
            if (optional == null)
                return request;
    
            System.Reflection.PropertyInfo[] optionalProperties = (optional.GetType()).GetProperties();
    
            foreach (System.Reflection.PropertyInfo property in optionalProperties)
            {
                // Copy value from optional parms to the request.  They should have the same names and datatypes.
                System.Reflection.PropertyInfo piShared = (request.GetType()).GetProperty(property.Name);
                if (property.GetValue(optional, null) != null) // TODO Test that we do not add values for items that are null
                    piShared.SetValue(request, property.GetValue(optional, null), null);
            }
    
            return request;
        }
    }
    

    Code ripped from my sample project for Google Analytics API v3 I also have a tutorial which can be found here Google Analytics Real-Time API with C# – What's happening now!