locked
Using Google API Javascript Client in Javascript Metro App

    Question

  • EDIT: I solved this issue on my own. See two posts down for a jQuery extension that solves it.

    I am trying to use the Google API Javascript Client to grab a list of a user's playlists. However, when I try to load it with this script tag:

    <script src="https://apis.google.com/js/client.js?onload=google_api_loaded"></script>

    And I run my app, it breaks in the debugger, giving this error message:

    Unhandled exception at line 6, column 563 in https://apis.google.com/js/client.js?onload=google_api_loaded
    
    0x800a139e - JavaScript runtime error: Pa

    The full stack trace of my program is below:

    'WWAHost.exe' (Script): Loaded 'Script Code (MSAppHost/2.0)'. 
    Exception was thrown at line 38, column 86 in https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en_US.-un9ZznLxdw.O/m=client/rt=j/sv=1/d=1/ed=1/am=IQ/rs=AItRSTNyFD-KyIouALt7DY-cSNyOv6TM7w/cb=gapi.loaded_0
    0x800a03f6 - JavaScript runtime error: Invalid character
    Exception was thrown at line 99, column 367 in https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en_US.-un9ZznLxdw.O/m=client/rt=j/sv=1/d=1/ed=1/am=IQ/rs=AItRSTNyFD-KyIouALt7DY-cSNyOv6TM7w/cb=gapi.loaded_0
    0x800a139e - JavaScript runtime error: Pa
    Exception was thrown at line 69, column 132 in https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en_US.-un9ZznLxdw.O/m=debug_error/exm=client/rt=j/sv=1/d=1/ed=1/am=IQ/rs=AItRSTNyFD-KyIouALt7DY-cSNyOv6TM7w/cb=gapi.loaded_1
    0x800a138f - JavaScript runtime error: Object expected
    Unhandled exception at line 6, column 563 in https://apis.google.com/js/client.js?onload=google_api_loaded
    0x800a139e - JavaScript runtime error: Pa
    
    The program '[5756] WWAHost.exe' has exited with code -1 (0xffffffff).

    However, if I open the page that contains the exact snippet of HTML described above in a browser like Google Chrome or Internet Explorer, it works without errors. I made sure that the HTML shown above is run in a web context, so presumably it is not an issue of trying to load external scripts in a local context. Why is this particular script erroring out in the Windows Metro environment?



    Friday, September 06, 2013 1:01 AM

All replies

  • You will need to debug the google script yourself in this case.  One way to do this more efficiently is to down load the google code and embed it in your script, then set breakpoints in their code just before these functions and see what the variables in question are.  Then if you have any questions specific about the Google code you could engage them on their forums for assistance.

    It is possible someone else has already used these APIs in the Windows Store app so you could also wait to see if someone answers this post!


    Jeff Sanders (MSFT)

    @jsandersrocks - Windows Store Developer Solutions @WSDevSol
    Getting Started With Windows Azure Mobile Services development? Click here
    Getting Started With Windows Phone or Store app development? Click here
    My Team Blog: Windows Store & Phone Developer Solutions
    My Blog: Http Client Protocol Issues (and other fun stuff I support)

    Friday, September 06, 2013 6:58 PM
    Moderator
  • Jeff,

    Thanks for the advice. I actually went ahead and abstracted the OAuth part of the API out so that it would work with Windows Metro using the metro web authentication sample from MSDN.

    For anyone who comes across this problem in the future, I wrote a jQuery extension to support the google api. Note two things: I have not tested refreshing yet, and if you want the token to persist between sessions, all you have to do is write a handler to load it from the roamingSettings into the sessionState at startup.

    var GLOBAL_CLIENT_ID = YOUR_CLIENT_ID;
    var GLOBAL_CLIENT_SECRET = 'YOUR_CLIENT_SECRET';
    var GLOBAL_GAPI_AUTH_URL = "https://accounts.google.com/o/oauth2/auth"
                            + "?response_type=code"
                            + "&redirect_uri=http://localhost"
                            + "&client_id=" + GLOBAL_CLIENT_ID
                            + "&scope=https://www.googleapis.com/auth/youtube";
    
    (function () {
        $.gapi = function (url, options) {
            // Store an unmodified copy in case we have to refresh our token and re-call this function later
            var copy = { url: url, options: options };
    
            // Wrap $.ajax into a function that will auto-refresh a token if need be
            // check for token first
            options.error = options.error instanceof Function ? options.error : function (str) { };
            if (!WinJS.Application.sessionState.token) {
                // haven't authenticated yet!
                $.gapi_authenticate(GLOBAL_GAPI_AUTH_URL, function () {
                    $.gapi(copy.url, copy.options);
                }, function () {
                    options.error("Could not retrieve auth token");
                });
                return;
            }
    
            if (!options.headers) options.headers = {};
            options.headers.Authorization = WinJS.Application.sessionState.token.token_type + " " + WinJS.Application.sessionState.token.access_token;
    
            options.error = function () {
                $.gapi_refresh_token(function () {
                    $.gapi(copy.url, copy.options);
                });
            }
    
            $.ajax(url, options);
        };
    
        $.gapi_refresh_token = function (callback) {
            $.ajax("https://accounts.google.com/o/oauth2/token", {
                type: "post",
                data: {
                    client_id: GLOBAL_CLIENT_ID,
                    client_secret: GLOBAL_CLIENT_SECRET,
                    refresh_token: WinJS.Application.sessionState.token.refresh_token,
                    grant_type: "refresh_token"
                },
                dataType: 'json',
                success: function (response) {
                    response.refresh_token = WinJS.Application.sessionState.token.refresh_token;
                    WinJS.Application.sessionState.token = response;
                    Windows.Storage.ApplicationData.current.roamingSettings.token = response; // This is so we can retrieve the token if the app is suspended and restarted
                    callback(response);
                }
            });
        };
    
        $.gapi_token_from_auth_code = function (auth_code, callback) {
            $.ajax("https://accounts.google.com/o/oauth2/token", {
                type: "post",
                data: {
                    code: auth_code,
                    client_id: GLOBAL_CLIENT_ID,
                    client_secret: GLOBAL_CLIENT_SECRET,
                    redirect_uri: "http://localhost",
                    grant_type: "authorization_code"
                },
                dataType: 'json',
                success: function (response) {
                    // Store the token globally for use in $.gapi
                    WinJS.Application.sessionState.token = response;
                    callback(response);
                }
            });
        };
    
        $.gapi_authenticate = function (url, callback, error) {
            error = error instanceof Function ? error : function (str) { };
    
            var startURI = Windows.Foundation.Uri(url);
            var endURI = Windows.Foundation.Uri("http://localhost");
    
            Windows.Security.Authentication.Web.WebAuthenticationBroker.authenticateAsync(
                        Windows.Security.Authentication.Web.WebAuthenticationOptions.none, startURI, endURI)
                        .done(function (result) {
                            var url = result.responseData;
    
                            // split off the GET variables that were passed to the url
                            var get_vars = url.substring(url.indexOf('?') + 1, url.length).split('&');
                            var vars = {};
                            get_vars.forEach(function (pair) {
                                var kv = pair.split('=');
                                vars[kv[0]] = kv[1];
                            });
    
                            // awesome, we have an auth code, let's get a real access token now
                            $.gapi_token_from_auth_code(vars.code, callback);
    
                            if (result.responseStatus === Windows.Security.Authentication.Web.WebAuthenticationStatus.errorHttp) {
                                error(result.responseErrorDetail);
                            }
                        }, function (err) {
                            error(err.message);
                        });
        }
    
        // Call an initial gapi call which will force the app to re-authenticate if there is no token yet
        $.gapi("https://www.googleapis.com/youtube/v3/channels?part=id&mine=true", {
            success: function (response) {
                //alert(JSON.stringify(response), "Success");
            },
            error: function (response) {
                //alert(JSON.stringify(response), "Error");
            },
            dataType: "json"
        });
    })();


    Tuesday, September 10, 2013 1:16 AM