Skip to content
This repository has been archived by the owner on May 28, 2019. It is now read-only.

Commit

Permalink
Merge pull request #19 from muffinresearch/pinstate-hookup
Browse files Browse the repository at this point in the history
Hookup API for pin state and mock it for testing (bug 986100)
  • Loading branch information
muffinresearch committed Mar 21, 2014
2 parents d057335 + 265770a commit 578114d
Show file tree
Hide file tree
Showing 17 changed files with 290 additions and 43 deletions.
2 changes: 2 additions & 0 deletions config/default.js
Expand Up @@ -21,4 +21,6 @@ module.exports = {
test: {
port: 7778,
},

showClientConsole: false
};
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -23,7 +23,7 @@
"js-beautify": "^1.4.2",
"mocha-phantomjs": "~3.3.2",
"nunjucks": "~1.0.1",
"underscore": "~1.6.0"
"underscore": "^1.6.0"
},
"scripts": {
"test": "node -e \"require('grunt').cli()\" null test",
Expand Down
14 changes: 12 additions & 2 deletions public/js/main.js
Expand Up @@ -7,18 +7,26 @@ require(['config'], function(config) {
// Main Entry point of the app.
require([
'backbone',
'id',
'i18n',
'id',
'jquery',
'log',
'models/user',
'router',
'utils',
'views/throbber',
], function(Backbone, id, i18n, log, UserModel, router, utils, throbber){
], function(Backbone, i18n, id, $, log, UserModel, router, utils, throbber){

window.app = {};
var console = log('app');

// Common ajax settings.
$.ajaxSetup({
headers: {
"X-CSRFToken": $('meta[name=csrf]').attr('content')
}
});

function initialize() {
console.log('I AM SPARTACUS!');
// Always show throbber.
Expand All @@ -28,6 +36,8 @@ require(['config'], function(config) {
app.user = new UserModel();
app.router = new router.AppRouter();
Backbone.history.start({pushState: true, root: app.router.root});
// Start identity watch.
app.user.watchIdentity();
}

// Require locale then run init.
Expand Down
87 changes: 65 additions & 22 deletions public/js/models/user.js
Expand Up @@ -15,54 +15,94 @@ define([

var UserModel = BaseModel.extend({

// Initialization of the model. Event listeners and setup should happen here.
initialize: function(){
_.bindAll(this, 'checkAuth', 'handleLoginStatechange', 'loginHandler', 'logoutHandler');
this.on('change:logged_in', this.handleLoginStatechange);
_.bindAll(this, 'handleLoginStateChange', 'loginHandler', 'logoutHandler', 'watchIdentity');
this.on('change:logged_in', this.handleLoginStateChange);
},

defaults: {
logged_in: null,
// Whether the user has a pin.
pin: false,
// Date object for when the pin was locked.
pin_locked_out: null,
// If the user has a locked pin
pin_is_locked_out: null,
// If the user was previously locked out.
pin_was_locked_out: null
},

handleLoginStatechange: function() {
if (this.get('logged_in') === false) {
baseURL: utils.bodyData.baseApiURL || '',

url: '/mozpay/v1/api/pin/',

// Takes the data retrieved from the API and works out how to
// dispatch the user based on the reponse.
handleUserState: function(data) {
if (data.pin_is_locked_out === true) {
console.log('User is locked out. Navigating to /locked');
return app.router.navigate('/locked', {trigger: true});
} else if (data.pin_was_locked_out === true) {
console.log('User was locked out. Navigating to /was-locked');
return app.router.navigate('/was-locked', {trigger: true});
} else if (data.pin === true) {
console.log('User has a pin so navigate to /enter-pin');
return app.router.navigate('/enter-pin', {trigger: true});
} else {
console.log('User has no pin so navigate to /create-pin');
return app.router.navigate('/create-pin', {trigger: true});
}
},

// Runs fetch to get the current model state.
getUserState: function() {
// Check the user's state
console.log('Fetching model state');
this.fetch()
.done(this.handleUserState)
.fail(function(){
console.log('fail');
});
},

// When the `logged_in` attr changes this function decides
// if login is required or if the user is logged in it hands off to
// checking the User's state e.g. if the user has a PIN or if the user
// is currently locked out.
handleLoginStateChange: function() {
var logged_in = this.get('logged_in');
console.log('logged_in state changed to ' + logged_in);
if (logged_in === false) {
console.log('navigating to /login');
app.router.navigate('/login', {trigger: true});
} else {
// TODO: More advanced logic here to check state.
if (Backbone.history.fragment === 'enter-pin') {
// If we're already trying to get to 'enter-pin' navigating to it
// won't cause it to be re-rendered so force it to be rendered by
// calling the view direct.
console.log('Already on Enter Pin. Re-rendering');
app.router.showEnterPin();
} else {
console.log('navigating to /enter-pin');
app.router.navigate('/enter-pin', {trigger: true});
}
this.getUserState();
}
},

checkAuth: function() {
// Runs navigator.id.watch via Persona.
watchIdentity: function() {
id.watch({
onlogin: this.loginHandler,
onlogout: this.logoutHandler,
});
},

// Runs the logout for the user.
logoutHandler: function() {
var self = this;
self.set({'logged_in': false});
self.resetUser();
this.set({'logged_in': false});
this.resetUser();
},

// Carries out resetting the user.
// TODO: Needs timers.
resetUser: function _resetUser() {
var console = log('UserModel', 'resetUser');
console.log('Begin webpay user reset');
var request = {
'type': 'POST',
url: utils.bodyData.resetUserUrl,
headers: {'X-CSRFToken': $('meta[name=csrf]').attr('content')}
url: utils.bodyData.resetUserUrl
};
var result = $.ajax(request)
.done(function _resetSuccess() {
Expand All @@ -79,6 +119,8 @@ define([
return result;
},

// Handle login from id.watch. Here is where verification occurs.
// TODO: needs timers.
loginHandler: function(assertion) {

if (loginTimer) {
Expand All @@ -87,6 +129,7 @@ define([
}

throbber.show(this.gettext('Connecting to Persona'));
console.log('Verifying assertion');

$.ajax({
type: 'POST',
Expand All @@ -102,7 +145,7 @@ define([
// callback(data);
//});
}, this),
error: _.bind(function(xhr, textStatus ) {
error: _.bind(function(xhr, textStatus) {
if (textStatus === 'timeout') {
console.log('login timed out');
utils.trackEvent({'action': 'persona login',
Expand Down
17 changes: 12 additions & 5 deletions public/js/router.js
Expand Up @@ -53,11 +53,18 @@ define([
},

before: function() {
// Always run id.watch.
app.user.checkAuth();
// Check login state and prevent routing if unknown.
if (app.user.get('logged_in') !== true && Backbone.history.fragment !== 'login') {
console.log('Preventing navigation as logged_in state is unknown and not login view.');
// If logged_in state hasn't yet been set we need to prevent
// routing until it is.
if (app.user.get('logged_in') === null) {
console.log('Preventing navigation as logged_in state is unknown.');
this.navigate('', {replace: true});
return false;
}
// If logged_in state is false then we need to always show the login page.
// assuming that's not where we already are.
if (app.user.get('logged_in') === false && Backbone.history.fragment !== 'login') {
console.log('Not login page and logged_out so navigating to /login');
this.navigate('/login', {trigger: true});
return false;
}
},
Expand Down
6 changes: 6 additions & 0 deletions public/js/views/base.js
Expand Up @@ -35,6 +35,12 @@ define([
this.$el.html(this.template(template, data));
console.log('Replacing $el with rendered content');
return this;
},
clear: function clear() {
// Remote the content in the view.
this.$el.empty();
// Disconnect the view's event handlers.
this.unbind();
}
});
return BaseView;
Expand Down
8 changes: 7 additions & 1 deletion public/js/views/create-pin.js
@@ -1,11 +1,17 @@
define(['views/base', 'log', 'lib/pin'], function(BaseView, log, pin){
define([
'lib/pin',
'log',
'views/base',
'views/throbber'
], function(pin, log, BaseView, throbber){
var console = log('view', 'create-pin');
var CreatePinView = BaseView.extend({
render: function(){
console.log('rendering view');
this.setTitle(this.gettext('Create Pin'));
this.renderTemplate('create-pin.html');
pin.init();
throbber.hide();
return this;
}
});
Expand Down
7 changes: 6 additions & 1 deletion public/js/views/enter-pin.js
@@ -1,4 +1,9 @@
define(['views/base', 'log', 'lib/pin', 'views/throbber'], function(BaseView, log, pin, throbber){
define([
'lib/pin',
'log',
'views/base',
'views/throbber'
], function(pin, log, BaseView, throbber){
var console = log('view', 'enter-pin');
var EnterPinView = BaseView.extend({
render: function(){
Expand Down
9 changes: 5 additions & 4 deletions public/js/views/throbber.js
Expand Up @@ -2,13 +2,14 @@ define(['jquery', 'views/base', 'log'], function($, BaseView, log){

var console = log('view', 'throbber');
var ThrobberView = BaseView.extend({
el: $('#progress'),
el: '#progress',
$el: $('#progress'),
render: function(msg){
console.log('rendering view');
console.log('rendering throbber');
this.setTitle(msg || this.gettext('Loading...'));
this.renderTemplate('throbber.html', {msg: msg || this.gettext('Loading...')});
return this;
}
},
});

var throbberView = new ThrobberView();
Expand All @@ -20,7 +21,7 @@ define(['jquery', 'views/base', 'log'], function($, BaseView, log){
},
hide: function _hide() {
console.log('Hiding progress');
throbberView.remove();
throbberView.clear();
},
};
});
16 changes: 16 additions & 0 deletions public/stylus/spartacus.styl
Expand Up @@ -19,6 +19,22 @@ body, html {
width: 100%;
}

// Add stripes when running the dev server just to make it easier to know if you're looking
// at a webpay hosted version or the devlopment version.
.dev-server:before {
background-color: yellow;
background-image: repeating-linear-gradient(45deg, transparent, transparent 10px, black 10px, black 20px);
bottom: 0;
content: '';
display: block;
opacity: 0.5;
position: absolute;
right: 0;
top: 0;
width: 10px;
z-index: 20;
}

.app {
background: $bg-color;
grain();
Expand Down
11 changes: 11 additions & 0 deletions server/index.js
Expand Up @@ -39,6 +39,17 @@ app.get('/mozpay', function (req, res) {
app.get(/\/testlib\/?.*/, express.static(__dirname + '/../tests/static'));
app.get(/\/unit\/?.*/, express.static(__dirname + '/../tests/'));

// Fake API response.
app.get('/mozpay/v1/api/pin/', function(req, res) {
var result = {
pin: false,
pin_is_locked_out: false,
pin_was_locked_out: false,
pin_locked_out: null
};
res.send(result);
});

// Fake verification.
app.post('/fake-verify', function (req, res) {
var assertion = req.query.assertion ? req.query.assertion : '';
Expand Down
4 changes: 3 additions & 1 deletion server/templates/index.html
Expand Up @@ -8,11 +8,13 @@
<link rel="stylesheet" href="/css/spartacus.css">
</head>
<body
class="dev-server"
data-base-api-url="{{ settings.baseApiUrl }}"
data-privacy-policy="https://marketplace.firefox.com/privacy-policy"
data-reset-user-url="{{ settings.resetUserURL }}"
data-terms-of-service="https://marketplace.firefox.com/terms-of-use"
data-unverified-issuer="{{ settings.browseridUnverifiedIssuerl }}"
data-verify-url="{{ settings.verifyUserURL }}"
data-reset-user-url="{{ settings.resetUserURL }}"
>
<div id="app" class="app"></div>
<div id="progress" class="progress"></div>
Expand Down

0 comments on commit 578114d

Please sign in to comment.