Bug 969055 - Validate items being saved with HomeProvider API (r=margaret)
authorLucas Rocha <lucasr@mozilla.com>
Wed, 12 Mar 2014 16:37:02 +0000
changeset 191502 69fb6e09e4c64c13e0fef0a0f079089a48f046cc
parent 191501 d0863e8202df54f16801c677ac9ab11eda13a536
child 191503 709e9207641d12065f566ef1aa480a23a0e4f859
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs969055
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 969055 - Validate items being saved with HomeProvider API (r=margaret)
mobile/android/base/tests/testHomeProvider.js
mobile/android/modules/HomeProvider.jsm
--- a/mobile/android/base/tests/testHomeProvider.js
+++ b/mobile/android/base/tests/testHomeProvider.js
@@ -8,16 +8,17 @@ const { utils: Cu } = Components;
 Cu.import("resource://gre/modules/HomeProvider.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Sqlite.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 const TEST_DATASET_ID = "test-dataset-id";
 const TEST_URL = "http://test.com";
+const TEST_TITLE = "Test";
 
 const PREF_SYNC_CHECK_INTERVAL_SECS = "home.sync.checkIntervalSecs";
 const TEST_INTERVAL_SECS = 1;
 
 const DB_PATH = OS.Path.join(OS.Constants.Path.profileDir, "home.sqlite");
 
 add_test(function test_request_sync() {
   // The current implementation of requestSync is synchronous.
@@ -42,17 +43,17 @@ add_test(function test_periodic_sync() {
     do_check_eq(datasetId, TEST_DATASET_ID);
     run_next_test();
   });
 });
 
 add_task(function test_save_and_delete() {
   // Use the HomeProvider API to save some data.
   let storage = HomeProvider.getStorage(TEST_DATASET_ID);
-  yield storage.save([{ url: TEST_URL }]);
+  yield storage.save([{ title: TEST_TITLE, url: TEST_URL }]);
 
   // Peek in the DB to make sure we have the right data.
   let db = yield Sqlite.openConnection({ path: DB_PATH });
 
   // Make sure the items table was created.
   do_check_true(yield db.tableExists("items"));
 
   // Make sure the correct values for the item ended up in there.
@@ -66,9 +67,39 @@ add_task(function test_save_and_delete()
 
   // Make sure the data was deleted.
   let result = yield db.execute("SELECT * FROM items");
   do_check_eq(result.length, 0);
 
   db.close();
 });
 
+add_task(function test_row_validation() {
+  // Use the HomeProvider API to save some data.
+  let storage = HomeProvider.getStorage(TEST_DATASET_ID);
+
+  let invalidRows = [
+    { url: "url" },
+    { title: "title" },
+    { description: "description" },
+    { image_url: "image_url" }
+  ];
+
+  // None of these save calls should save anything
+  for (let row of invalidRows) {
+    try {
+      yield storage.save([row]);
+    } catch (e if e instanceof HomeProvider.ValidationError) {
+      // Just catch and ignore validation errors
+    }
+  }
+
+  // Peek in the DB to make sure we have the right data.
+  let db = yield Sqlite.openConnection({ path: DB_PATH });
+
+  // Make sure no data has been saved.
+  let result = yield db.execute("SELECT * FROM items");
+  do_check_eq(result.length, 0);
+
+  db.close();
+});
+
 run_next_test();
--- a/mobile/android/modules/HomeProvider.jsm
+++ b/mobile/android/modules/HomeProvider.jsm
@@ -107,17 +107,30 @@ function syncTimerCallback(timer) {
       let success = HomeProvider.requestSync(datasetId, callback);
       if (success) {
         Services.prefs.setIntPref(getLastSyncPrefName(datasetId), now);
       }
     }
   }
 }
 
+this.HomeStorage = function(datasetId) {
+  this.datasetId = datasetId;
+};
+
+this.ValidationError = function(message) {
+  this.name = "ValidationError";
+  this.message = message;
+};
+ValidationError.prototype = new Error();
+ValidationError.prototype.constructor = ValidationError;
+
 this.HomeProvider = Object.freeze({
+  ValidationError: ValidationError,
+
   /**
    * Returns a storage associated with a given dataset identifer.
    *
    * @param datasetId
    *        (string) Unique identifier for the dataset.
    *
    * @return HomeStorage
    */
@@ -244,19 +257,33 @@ function getDatabaseConnection() {
       throw e;
     }
 
     gDatabaseEnsured = true;
     throw new Task.Result(db);
   });
 }
 
-this.HomeStorage = function(datasetId) {
-  this.datasetId = datasetId;
-};
+/**
+ * Validates an item to be saved to the DB.
+ *
+ * @param item
+ *        (object) item object to be validated.
+ */
+function validateItem(datasetId, item) {
+  if (!item.url) {
+    throw new ValidationError('HomeStorage: All rows must have an URL: datasetId = ' +
+                              datasetId);
+  }
+
+  if (!item.image_url && !item.title && !item.description) {
+    throw new ValidationError('HomeStorage: All rows must have at least an image URL, ' +
+                              'or a title or a description: datasetId = ' + datasetId);
+  }
+}
 
 HomeStorage.prototype = {
   /**
    * Saves data rows to the DB.
    *
    * @param data
    *        (array) JSON array of row items
    *
@@ -264,16 +291,18 @@ HomeStorage.prototype = {
    * @resolves When the operation has completed.
    */
   save: function(data) {
     return Task.spawn(function save_task() {
       let db = yield getDatabaseConnection();
       try {
         // Insert data into DB.
         for (let item of data) {
+          validateItem(this.datasetId, item);
+
           // XXX: Directly pass item as params? More validation for item? Batch insert?
           let params = {
             dataset_id: this.datasetId,
             url: item.url,
             title: item.title,
             description: item.description,
             image_url: item.image_url,
             filter: item.filter,