diff --git a/README.md b/README.md index 4684f494..909c8fa1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ [![Build Status](https://travis-ci.org/yahoo/bullet-ui.svg?branch=master)](https://travis-ci.org/yahoo/bullet-ui) [![Code Climate](https://codeclimate.com/github/yahoo/bullet-ui/badges/gpa.svg)](https://codeclimate.com/github/yahoo/bullet-ui) [![Test Coverage](https://codeclimate.com/github/yahoo/bullet-ui/badges/coverage.svg)](https://codeclimate.com/github/yahoo/bullet-ui/coverage) -This is the UI for Bullet created with Ember 2. The UI stores all created queries, results and other metadata in the browser's **LocalStorage**. +This is the UI for Bullet created with Ember 2. The UI stores all created queries, results and other metadata in the browser's **WebStorage**. It uses [LocalForage](https://github.com/localForage/localForage) to wrap IndexedDB, WebSQL, LocalStorage depending +on what is available through the browser and on the device. ## Prerequisites @@ -74,7 +75,7 @@ You can add more configuration at the top level for each host you have the UI ru ```schemaNamespace``` is the fragment of the path to your schema web-service on the ```schemaHost```. There is no ```schemaPath``` because it **must** be "columns" in order for the UI to be able fetch the column resource (columns in your schema). -```modelVersion``` is a way for you to control your UI users' Ember models saved in LocalStorage. If there is a need for you to purge all your user's created queries, results and other data stored in their LocalStorage, then you should increment this number. The UI, on startup, will compare this number with what it has seen before (your old version) and purge the LocalStorage. +```modelVersion``` is a way for you to control your UI users' Ember models saved in WebStorage. If there is a need for you to purge all your user's created queries, results and other data stored in their WebStorage, then you should increment this number. The UI, on startup, will compare this number with what it has seen before (your old version is stored in LocalStorage) and purge the WebStorage. ```helpLinks``` is a list of objects, where each object is a help link. These links drive the dropdown list when you click the "Help" button on the UI's top navbar. You can use this to point to your particular help links. For example, you could use this to point your users toward a page that helps them understand your data (that this UI is operating on). diff --git a/app/adapters/application.js b/app/adapters/application.js index bb8793ae..4515dcc9 100644 --- a/app/adapters/application.js +++ b/app/adapters/application.js @@ -3,7 +3,9 @@ * Licensed under the terms of the Apache License, Version 2.0. * See the LICENSE file associated with the project for terms. */ +import LFAdapter from 'ember-localforage-adapter/adapters/localforage'; +import ENV from 'bullet-ui/config/environment'; -// The local storage ember-local-storage adapter -export { default } from 'ember-local-storage/adapters/adapter'; - +export default LFAdapter.extend({ + caching: ENV.APP.LOCALFORAGE_CACHING +}); diff --git a/app/initializers/startup.js b/app/initializers/startup.js index b057ff4c..09ef911d 100644 --- a/app/initializers/startup.js +++ b/app/initializers/startup.js @@ -27,22 +27,29 @@ export default { application.inject('controller', 'settings', 'settings:main'); let version = settings.modelVersion; - this.applyMigrations(version); - localStorage.modelVersion = version; + application.deferReadiness(); + this.applyMigrations(version).then(() => { + // Store versions in localStorage explicitly. + window.localStorage.modelVersion = version; + application.advanceReadiness(); + }); }, /** - * Applies any forced migrations for local storage. Currently, only wipes localStorage + * Applies any forced migrations for the client side storage. Currently, wipes localforage * if version is greater than the stored version or if stored version is not present. * @param {Number} version A numeric version to compare the current stored version against. - * @return {Boolean} Denoting whether local storage was modified. + * @return {Promise} That resolves to a boolean denoting whether the storage was wiped. */ applyMigrations(version) { - let currentVersion = localStorage.modelVersion; + let currentVersion = window.localStorage.modelVersion; if (!currentVersion || version > currentVersion) { - localStorage.clear(); - return true; + Ember.Logger.info('Wiping of all data requested...Performing wipe'); + return window.localforage.clear().then(() => { + Ember.Logger.info('Data was wiped.'); + return true; + }); } - return false; + return Ember.RSVP.resolve(false); } }; diff --git a/app/serializers/application.js b/app/serializers/application.js index 9c98f976..26ba5339 100644 --- a/app/serializers/application.js +++ b/app/serializers/application.js @@ -3,6 +3,6 @@ * Licensed under the terms of the Apache License, Version 2.0. * See the LICENSE file associated with the project for terms. */ +import LFSerializer from 'ember-localforage-adapter/serializers/localforage'; -// The local storage ember-local-storage serializer -export { default } from 'ember-local-storage/serializers/serializer'; +export default LFSerializer; diff --git a/app/serializers/column.js b/app/serializers/column.js new file mode 100644 index 00000000..cd66df85 --- /dev/null +++ b/app/serializers/column.js @@ -0,0 +1,8 @@ +/* + * Copyright 2016, Yahoo Inc. + * Licensed under the terms of the Apache License, Version 2.0. + * See the LICENSE file associated with the project for terms. + */ +import DS from 'ember-data'; + +export default DS.JSONAPISerializer; diff --git a/bower.json b/bower.json index 81e1cbf2..cca0c2e9 100644 --- a/bower.json +++ b/bower.json @@ -8,6 +8,7 @@ "interact": "^1.2.8", "jQuery-QueryBuilder": "^2.4.0", "jQuery-QueryBuilder-Subfield": "yahoo/jQuery-QueryBuilder-Subfield#v1.0.0", - "jQuery-QueryBuilder-Placeholders": "yahoo/jQuery-QueryBuilder-Placeholders#v1.0.0" + "jQuery-QueryBuilder-Placeholders": "yahoo/jQuery-QueryBuilder-Placeholders#v1.0.0", + "localforage": "~1.3.1" } } diff --git a/config/environment.js b/config/environment.js index 7c97d8a7..7bcdc538 100644 --- a/config/environment.js +++ b/config/environment.js @@ -26,6 +26,7 @@ module.exports = function(environment) { }, APP: { + LOCALFORAGE_CACHING: 'model', // Inject default static settings SETTINGS: configuration.default // Here you can pass flags/options to your application instance @@ -45,6 +46,9 @@ module.exports = function(environment) { // Testem prefers this... ENV.locationType = 'none'; + // Turn off caching for tests. Otherwise tests need to wipe it between each. + ENV.APP.LOCALFORAGE_CACHING = 'none'; + // keep test console output quieter ENV.APP.LOG_ACTIVE_GENERATION = false; ENV.APP.LOG_VIEW_LOOKUPS = false; diff --git a/package.json b/package.json index a2e3e1fa..e84c0155 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "ember-export-application-global": "^1.0.5", "ember-light-table": "^1.8.1", "ember-load-initializers": "^0.6.0", - "ember-local-storage": "1.3.4", + "ember-localforage-adapter": "2.2.1", "ember-moment": "7.0.0", "ember-power-select": "1.4.0", "ember-radio-button": "1.0.7", diff --git a/tests/acceptance/navigation-test.js b/tests/acceptance/navigation-test.js index 3d258120..c0fed062 100644 --- a/tests/acceptance/navigation-test.js +++ b/tests/acceptance/navigation-test.js @@ -14,12 +14,12 @@ let server; moduleForAcceptance('Acceptance | navigation', { beforeEach() { server = mockAPI(RESULTS.MULTIPLE, COLUMNS.BASIC); + return window.localforage.setDriver(window.localforage.LOCALSTORAGE); }, afterEach() { server.shutdown(); - // Wipe out localstorage because we are creating here - window.localStorage.clear(); + return window.localforage.clear(); } }); diff --git a/tests/acceptance/query-default-api-filter-test.js b/tests/acceptance/query-default-api-filter-test.js index 98603c48..b318f1ab 100644 --- a/tests/acceptance/query-default-api-filter-test.js +++ b/tests/acceptance/query-default-api-filter-test.js @@ -30,12 +30,12 @@ moduleForAcceptance('Acceptance | query default api filter', { return jsonWrap(200, FILTERS.AND_ENUMERATED); }); }); + return window.localforage.setDriver(window.localforage.LOCALSTORAGE); }, afterEach() { server.shutdown(); - // Wipe out localstorage because we are creating here - window.localStorage.clear(); + return window.localforage.clear(); } }); diff --git a/tests/acceptance/query-default-filter-test.js b/tests/acceptance/query-default-filter-test.js index 021ba967..b9fafe99 100644 --- a/tests/acceptance/query-default-filter-test.js +++ b/tests/acceptance/query-default-filter-test.js @@ -19,12 +19,12 @@ moduleForAcceptance('Acceptance | query default filter', { this.application.register('settings:mocked', Ember.Object.create({ defaultFilter: FILTERS.AND_LIST }), { instantiate: false }); this.application.inject('route', 'settings', 'settings:mocked'); server = mockAPI(RESULTS.MULTIPLE, COLUMNS.BASIC); + return window.localforage.setDriver(window.localforage.LOCALSTORAGE); }, afterEach() { server.shutdown(); - // Wipe out localstorage because we are creating here - window.localStorage.clear(); + return window.localforage.clear(); } }); diff --git a/tests/acceptance/query-firing-test.js b/tests/acceptance/query-firing-test.js index 2320905b..7f81c8c5 100644 --- a/tests/acceptance/query-firing-test.js +++ b/tests/acceptance/query-firing-test.js @@ -12,12 +12,15 @@ import { mockAPI, failAPI } from '../helpers/pretender'; let server; moduleForAcceptance('Acceptance | query firing', { + beforeEach() { + return window.localforage.setDriver(window.localforage.LOCALSTORAGE); + }, + afterEach() { - // Wipe out localstorage because we are creating here if (server) { server.shutdown(); } - window.localStorage.clear(); + return window.localforage.clear(); } }); diff --git a/tests/acceptance/query-lifecycle-test.js b/tests/acceptance/query-lifecycle-test.js index 273ed858..5774d1b2 100644 --- a/tests/acceptance/query-lifecycle-test.js +++ b/tests/acceptance/query-lifecycle-test.js @@ -13,15 +13,15 @@ let server; moduleForAcceptance('Acceptance | query lifecycle', { beforeEach() { - // Wipe out localstorage because we are creating queries here - window.localStorage.clear(); server = mockAPI(RESULTS.SINGLE, COLUMNS.BASIC); + return window.localforage.setDriver(window.localforage.LOCALSTORAGE); }, afterEach() { if (server) { server.shutdown(); } + return window.localforage.clear(); } }); diff --git a/tests/acceptance/query-results-lifecycle-test.js b/tests/acceptance/query-results-lifecycle-test.js index 6627a9bf..703f82aa 100644 --- a/tests/acceptance/query-results-lifecycle-test.js +++ b/tests/acceptance/query-results-lifecycle-test.js @@ -12,12 +12,16 @@ import { mockAPI } from '../helpers/pretender'; let server; moduleForAcceptance('Acceptance | query results lifecycle', { + beforeEach() { + return window.localforage.setDriver(window.localforage.LOCALSTORAGE); + }, + afterEach() { // Wipe out localstorage because we are creating here if (server) { server.shutdown(); } - window.localStorage.clear(); + return window.localforage.clear(); } }); diff --git a/tests/acceptance/query-validation-test.js b/tests/acceptance/query-validation-test.js index 093eab2b..995dd8e5 100644 --- a/tests/acceptance/query-validation-test.js +++ b/tests/acceptance/query-validation-test.js @@ -13,15 +13,15 @@ let server; moduleForAcceptance('Acceptance | query validation', { beforeEach() { - // Wipe out localstorage because we are creating here - window.localStorage.clear(); server = mockAPI(RESULTS.SINGLE, COLUMNS.BASIC); + return window.localforage.setDriver(window.localforage.LOCALSTORAGE); }, afterEach() { if (server) { server.shutdown(); } + return window.localforage.clear(); } }); diff --git a/tests/acceptance/result-lifecycle-test.js b/tests/acceptance/result-lifecycle-test.js index 2bee6d09..05f32f1d 100644 --- a/tests/acceptance/result-lifecycle-test.js +++ b/tests/acceptance/result-lifecycle-test.js @@ -13,14 +13,14 @@ let server; moduleForAcceptance('Acceptance | result lifecycle', { beforeEach() { - // Wipe out localstorage because we are creating queries here - window.localStorage.clear(); + return window.localforage.setDriver(window.localforage.LOCALSTORAGE); }, afterEach() { if (server) { server.shutdown(); } + return window.localforage.clear(); } });