Bug 1280855 - Read original metadata from attachment key; r?sebastian draft
authortarek <tarek@mozilla.com>
Wed, 22 Jun 2016 18:32:03 +0200
changeset 380675 418e53f848cf9bdeb003ee54f1eca6e2638ff26f
parent 379985 3c5025f98e561a20e24d97c91a9e4e0ec28015ea
child 523776 aa47ea5144f3b399b5c43615945648e41031fa7c
push id21279
push usertziade@mozilla.com
push dateWed, 22 Jun 2016 16:39:35 +0000
reviewerssebastian
bugs1280855
milestone50.0a1
Bug 1280855 - Read original metadata from attachment key; r?sebastian MozReview-Commit-ID: Kgdc6Nl6hho
mobile/android/base/java/org/mozilla/gecko/dlc/SyncAction.java
mobile/android/base/java/org/mozilla/gecko/dlc/catalog/DownloadContentBuilder.java
mobile/android/tests/background/junit4/resources/dlc_sync_old_format.json
mobile/android/tests/background/junit4/resources/dlc_sync_single_font.json
mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestSyncAction.java
--- a/mobile/android/base/java/org/mozilla/gecko/dlc/SyncAction.java
+++ b/mobile/android/base/java/org/mozilla/gecko/dlc/SyncAction.java
@@ -29,16 +29,18 @@ import java.net.HttpURLConnection;
  * Sync: Synchronize catalog from a Kinto instance.
  */
 public class SyncAction extends BaseAction {
     private static final String LOGTAG = "DLCSyncAction";
 
     private static final String KINTO_KEY_ID = "id";
     private static final String KINTO_KEY_DELETED = "deleted";
     private static final String KINTO_KEY_DATA = "data";
+    private static final String KINTO_KEY_ATTACHMENT = "attachment";
+    private static final String KINTO_KEY_ORIGINAL = "original";
 
     private static final String KINTO_PARAMETER_SINCE = "_since";
     private static final String KINTO_PARAMETER_FIELDS = "_fields";
     private static final String KINTO_PARAMETER_SORT = "_sort";
 
     /**
      * Kinto endpoint with online version of downloadable content catalog
      *
@@ -68,16 +70,22 @@ public class SyncAction extends BaseActi
             Log.d(LOGTAG, "Server returned " + rawCatalog.length() + " records (since " + lastModified + ")");
 
             for (int i = 0; i < rawCatalog.length(); i++) {
                 JSONObject object = rawCatalog.getJSONObject(i);
                 String id = object.getString(KINTO_KEY_ID);
 
                 final boolean isDeleted = object.optBoolean(KINTO_KEY_DELETED, false);
 
+                if (!isDeleted) {
+                    JSONObject attachment = object.getJSONObject(KINTO_KEY_ATTACHMENT);
+                    if (attachment.isNull(KINTO_KEY_ORIGINAL))
+                        throw new JSONException(String.format("Old Attachment Format"));
+                }
+
                 DownloadContent existingContent = catalog.getContentById(id);
 
                 if (isDeleted) {
                     cleanupRequired |= deleteContent(catalog, id);
                 } else if (existingContent != null) {
                     studyRequired |= updateContent(catalog, object, existingContent);
                 } else {
                     studyRequired |= createContent(catalog, object);
@@ -117,17 +125,17 @@ public class SyncAction extends BaseActi
         try {
             Uri.Builder builder = Uri.parse(CATALOG_ENDPOINT).buildUpon();
 
             if (lastModified > 0) {
                 builder.appendQueryParameter(KINTO_PARAMETER_SINCE, String.valueOf(lastModified));
             }
             // Only select the fields we are actually going to read.
             builder.appendQueryParameter(KINTO_PARAMETER_FIELDS,
-                    "attachment.location,original.filename,original.hash,attachment.hash,type,kind,original.size,match");
+                    "attachment.location,attachment.original.filename,attachment.original.hash,attachment.hash,type,kind,attachment.original.size,match");
 
             // We want to process items in the order they have been modified. This is to ensure that
             // our last_modified values are correct if we processing is interrupted and not all items
             // have been processed.
             builder.appendQueryParameter(KINTO_PARAMETER_SORT, "last_modified");
 
             connection = buildHttpURLConnection(builder.build().toString());
 
--- a/mobile/android/base/java/org/mozilla/gecko/dlc/catalog/DownloadContentBuilder.java
+++ b/mobile/android/base/java/org/mozilla/gecko/dlc/catalog/DownloadContentBuilder.java
@@ -211,24 +211,23 @@ public class DownloadContentBuilder {
         } else if (!id.equals(objectId)) {
             throw new JSONException(String.format("Record ids do not match: Expected=%s, Actual=%s", id, objectId));
         }
 
         setType(object.getString(KINTO_KEY_TYPE));
         setKind(object.getString(KINTO_KEY_KIND));
         setLastModified(object.getLong(KINTO_KEY_LAST_MODIFIED));
 
-        JSONObject original = object.getJSONObject(KINTO_KEY_ORIGINAL);
+        JSONObject attachment = object.getJSONObject(KINTO_KEY_ATTACHMENT);
+        JSONObject original = attachment.getJSONObject(KINTO_KEY_ORIGINAL);
 
         setFilename(original.getString(KINTO_KEY_FILENAME));
         setChecksum(original.getString(KINTO_KEY_HASH));
         setSize(original.getLong(KINTO_KEY_SIZE));
 
-        JSONObject attachment = object.getJSONObject(KINTO_KEY_ATTACHMENT);
-
         setLocation(attachment.getString(KINTO_KEY_LOCATION));
         setDownloadChecksum(attachment.getString(KINTO_KEY_HASH));
 
         JSONObject match = object.optJSONObject(KINTO_KEY_MATCH);
         if (match != null) {
             setAndroidApiPattern(match.optString(KINTO_KEY_ANDROID_API));
             setAppIdPattern(match.optString(KINTO_KEY_APP_ID));
             setAppVersionPattern(match.optString(KINTO_KEY_APP_VERSION));
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/resources/dlc_sync_old_format.json
@@ -0,0 +1,23 @@
+{
+  "data":[
+    {
+      "kind":"font",
+      "original": {
+        "mimetype":"application/x-font-ttf",
+        "filename":"CharisSILCompact-R.ttf",
+        "hash":"4ed509317f1bb441b185ea13bf1c9d19d1a0b396962efa3b5dc3190ad88f2067",
+        "size":1727656
+      },
+      "last_modified":1455710632607,
+      "attachment": {
+        "mimetype":"application/x-gzip",
+        "size":548720,
+        "hash":"960be4fc5a92c1dc488582b215d5d75429fd4ffbee463105d29992cd792a912e",
+        "location":"/attachments/0d28a72d-a51f-46f8-9e5a-f95c61de904e.gz",
+        "filename":"CharisSILCompact-R.ttf.gz"
+      },
+      "type":"asset-archive",
+      "id":"c906275c-3747-fe27-426f-6187526a6f06"
+    }
+  ]
+}
--- a/mobile/android/tests/background/junit4/resources/dlc_sync_single_font.json
+++ b/mobile/android/tests/background/junit4/resources/dlc_sync_single_font.json
@@ -1,23 +1,23 @@
 {
   "data":[
     {
       "kind":"font",
-      "original": {
-        "mimetype":"application/x-font-ttf",
-        "filename":"CharisSILCompact-R.ttf",
-        "hash":"4ed509317f1bb441b185ea13bf1c9d19d1a0b396962efa3b5dc3190ad88f2067",
-        "size":1727656
-      },
       "last_modified":1455710632607,
       "attachment": {
         "mimetype":"application/x-gzip",
         "size":548720,
         "hash":"960be4fc5a92c1dc488582b215d5d75429fd4ffbee463105d29992cd792a912e",
         "location":"/attachments/0d28a72d-a51f-46f8-9e5a-f95c61de904e.gz",
-        "filename":"CharisSILCompact-R.ttf.gz"
+        "filename":"CharisSILCompact-R.ttf.gz",
+        "original": {
+          "mimetype":"application/x-font-ttf",
+          "filename":"CharisSILCompact-R.ttf",
+          "hash":"4ed509317f1bb441b185ea13bf1c9d19d1a0b396962efa3b5dc3190ad88f2067",
+          "size":1727656
+        }
       },
       "type":"asset-archive",
       "id":"c906275c-3747-fe27-426f-6187526a6f06"
     }
   ]
 }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestSyncAction.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestSyncAction.java
@@ -99,16 +99,38 @@ public class TestSyncAction {
         Assert.assertEquals("/attachments/0d28a72d-a51f-46f8-9e5a-f95c61de904e.gz", content.getLocation());
         Assert.assertEquals(DownloadContent.TYPE_ASSET_ARCHIVE, content.getType());
         Assert.assertEquals(1455710632607L, content.getLastModified());
         Assert.assertEquals(1727656L, content.getSize());
         Assert.assertEquals(DownloadContent.STATE_NONE, content.getState());
     }
 
     /**
+     * Scenario: The catalog is using the old format, we want to make sure we abort cleanly.
+     */
+    @Test
+    public void testUpdatingWithOldCatalog() throws Exception{
+        SyncAction action = spy(new SyncAction());
+        doReturn(true).when(action).isSyncEnabledForClient(RuntimeEnvironment.application);
+        doReturn(fromFile("dlc_sync_old_format.json")).when(action).fetchRawCatalog(anyLong());
+
+        DownloadContent existingContent = createTestContent("c906275c-3747-fe27-426f-6187526a6f06");
+        DownloadContentCatalog catalog = spy(new MockedContentCatalog(existingContent));
+
+        action.perform(RuntimeEnvironment.application, catalog);
+
+        // make sure nothing was done
+        verify(action, never()).createContent(anyCatalog(), anyJSONObject());
+        verify(action, never()).updateContent(anyCatalog(), anyJSONObject(), anyContent());
+        verify(action, never()).deleteContent(anyCatalog(), anyString());
+        verify(action, never()).startStudyAction(anyContext());
+    }
+
+
+    /**
      * Scenario: The catalog contains one item and the server returns a new version.
      */
     @Test
     public void testUpdatingExistingContent() throws Exception{
         SyncAction action = spy(new SyncAction());
         doReturn(true).when(action).isSyncEnabledForClient(RuntimeEnvironment.application);
         doReturn(fromFile("dlc_sync_single_font.json")).when(action).fetchRawCatalog(anyLong());