Search code examples
angularjsbreezeionic-framework

Authentication: "cannot execute _executeQueryCore until metadataStore is populated"


I am trying to add authentication to my mobile app, built using Angular and Breeze.

In my app.js:

app.config(function ($httpProvider) {
    $httpProvider.interceptors.push(intercept)
});

//intercept runs on every http request and response
//When there is a 401 error it uses broadcast to send an event to 
//the application scope
function intercept($rootScope) {
    return {
        request: function (config) {
            $rootScope.$broadcast('loading:show');
            return config;
        },
        response: function (response) {
            $rootScope.$broadcast('loading:hide');
            return response;
        },
        responseError: function (error) {
            if (error.status === 401) {
                $rootScope.$broadcast('error:401');
            }
            return error;
        }
    }
}

app.run(function ($rootScope, $ionicLoading, $state) {
    $rootScope.$on('loading:show', function () {
        $ionicLoading.show({ template: 'Loading...' });
    });

    $rootScope.$on('loading:hide', function () {
        $ionicLoading.hide();
    });

    //401 errors mean the user is not authenticated
    //display a login dialog
    $rootScope.$on('error:401', function () {
        $ionicModal
            .fromTemplateUrl("app/user/login.html", { focusFirstInput: true })
            .then(function (modal) {
                modal.show();
            });
    });
});

And in my dataContext.js:

var properties = [];

function getProperties(refresh) {

    if (!refresh && properties.length > 0) {
        return common.$q.when(properties);
    }

    var query = EntityQuery
                .from('Properties')
                .select('ID, NameNumber, StreetName, ...etc')
                .orderBy('StreetName, NameNumber')
                .toType("Property")
                .using(manager)
                .execute()
                .then(querySucceeded, queryFailed);

    return query;

    function querySucceeded(data) {
        //set the properties held in memory
        properties = map(data.results);
        return properties;
    }

    function queryFailed(error) {
        console.log("Error while making http call: " + error.message);
        //return the properties held in memory
        return properties;
    }
}

The logic goes like this:

  1. getProperties() called

  2. EntityQuery triggers request to http://.../breeze/breeze/Metadata"

  3. Response is 401 (user not authenticated) - as expected

  4. 'error:401' event is broadcast and handled and login screen is displayed

This all works fairly well.

Now, if I cancel my login dialog, then refresh, that triggers getProperties again, but this time I get this error reported in my queryFailed function:

cannot execute _executeQueryCore until metadataStore is populated

So I guess that Breeze knows that a call was made previously and therefore expects it to be there, even though it failed with a 401. What can I do to get around this problem?


Solution

  • After trying many other things, I finally realised that I needed to resolve the responseError with a rejection. Otherwise (I guess) Breeze believes that the MetaData call was successful, and subsequently tries to access it, then generates the error when it is unsuccessful:

    responseError: function (error) {
        if (error.status === 401) {
            $rootScope.$broadcast('error:401');
        }
    
        //return error; wrong!
    
        return $q.reject(error);
    }