ducky/web

Paddy 2015-05-31 Parent:b9d0efb44eaa Child:275a83e4c02e

7:e6da0d35a533 Go to Latest

ducky/web/src/models/me.js

Add error handling messages, clear server errors on validation. Add missing messages for error conditions that our server can return. Clear our server error messages when client-side validation occurs, so you aren't confusingly left with errors after you change the input that caused them. Ideally, this behaviour would be limited to just the errors that were caused by the updated field, but clearing all of them seems to be the more user-friendly behaviour than just leaving them. Baby steps. Fix a bug in our handling of error messages--ampersand-sync is kind of inconsistent, and parses our successful responses as JSON, but neglects to parse the error responses as JSON. So we need to manually parse our errors from JSON before we can work with them. We also added a handler for invalid_client error messages, which don't match our error objects (thanks OAuth2 spec!), so we translate it to one of our error objects and then add it to the server errors to be displayed.

History
1 import Model from 'ampersand-model'
2 import Sync from 'ampersand-sync'
3 import qs from 'qs'
4 import config from '../config'
5 import isObject from 'lodash.isobject'
6 import jwtDecode from 'jwt-decode'
8 export default Model.extend({
9 url: config.urlBase + '/token',
10 ajaxConfig: {
11 headers: {
12 'Content-Type': 'application/x-www-form-urlencoded',
13 'Authorization': 'Basic ' + btoa(config.clientID + ':' + config.clientSecret),
14 }
15 },
17 props: {
18 access_token: 'string',
19 refresh_token: 'string',
20 expires_in: 'int',
21 token_created: 'date',
22 name: 'string',
23 profileID: 'string',
24 },
26 derived: {
27 loggedIn () {
28 return !!this.access_token
29 },
30 needsRefresh () {
31 let d = this.token_created
32 return !!this.refresh_token && (new Date() >= d.setSeconds(d.getSeconds() + this.expires_in - 900))
33 },
34 profile: {
35 deps: ['profileID'],
36 fn () {
37 return app.profiles.get(this.profileID)
38 },
39 },
40 },
42 login (email, password) {
43 let options = {
44 data: qs.stringify({
45 'username': email,
46 'password': password,
47 'grant_type': 'password',
48 }),
49 }
50 let moc = this
51 options.success = function(resp) {
52 if (!resp.access_token) {
53 return false
54 }
55 let serverAttrs = moc.parse(resp, options)
56 serverAttrs.token_created = new Date()
57 console.log(serverAttrs)
58 if (options.wait) serverAttrs = assign({}, serverAttrs)
59 if (isObject(serverAttrs) && !moc.set(serverAttrs, options)) {
60 return false
61 }
62 const token = jwtDecode(moc.access_token)
63 moc.profileID = token.sub
64 moc.trigger('sync', moc, resp, options)
65 }
66 options.error = function(resp) {
67 moc.trigger('error', moc, resp, options)
68 }
69 let sync = Sync('create', moc, options)
70 },
72 writeToCache () {
73 // TODO: write this to chrome.storage.local
74 },
76 logout () {
77 // TODO: clear all cached data
78 },
80 })