Skip to content

Latest commit

 

History

History
502 lines (438 loc) · 19.3 KB

advanced_jsonstore.md

File metadata and controls

502 lines (438 loc) · 19.3 KB
copyright lastupdated
years
2018, 2019
2019-01-04

{:generic: .ph data-hd-programlang='generic'} {:java: .ph data-hd-programlang='java'} {:ruby: .ph data-hd-programlang='ruby'} {:c#: .ph data-hd-programlang='c#'} {:objectc: .ph data-hd-programlang='Objective C'} {:python: .ph data-hd-programlang='python'} {:javascript: .ph data-hd-programlang='javascript'} {:php: .ph data-hd-programlang='PHP'} {:swift: .ph data-hd-programlang='swift'} {:curl: .ph data-hd-programlang='curl'} {:generic: .ph data-hd-operatingsystem='generic'} {:ios: .ph data-hd-programlang='iOS'} {:android: .ph data-hd-programlang='Android'} {:cordova: .ph data-hd-programlang='Cordova'} {:shortdesc: .shortdesc} {:new_window: target="_blank"} {:tip: .tip} {:note: .note} {:pre: .pre} {:codeblock: .codeblock} {:screen: .screen}

Advanced offline storage configuration

{: #adv_configure_offline_storage}

Security in JSONStore

{: #security_jsonstore}

You can secure all the collections in a store by passing a password to the init function. If no password is passed, the documents of all the collections in the store are not encrypted. {: cordova}

Data encryption is only available on Android, iOS, Windows 8.1 Universal and Windows 10 UWP environments. Some security metadata is stored in the keychain (iOS), shared preferences (Android) or the credential locker (Windows 8.1). The store is encrypted with a 256-bit Advanced Encryption Standard (AES) key. All keys are strengthened with Password-Based Key Derivation Function 2 (PBKDF2). {: cordova}

Use closeAll to lock access to all the collections until you call init again. If you think of init as a login function you can think of closeAll as the corresponding logout function. Use changePassword to change the password. {: cordova}

Encryption is supported in iOS only. By default, the Mobile Foundation Cordova SDK for iOS relies on iOS provided APIs for encryption. If you prefer to replace this with OpenSSL: {: cordova}

  • Add the cordova-plugin-mfp-encrypt-utils plug-in:
    cordova plugin add cordova-plugin-mfp-encrypt-utils.
  • In the application logic, use: WL.SecurityUtils.enableNativeEncryption(false) to enable the OpenSSL option. {: cordova}

You can secure all the collections in a store by passing a JSONStoreOpenOptions object with a password to the openCollections function. If no password is passed, the documents of all the collections in the store aren’t encrypted. {: ios}

Some security metadata is stored in the keychain (iOS). The store is encrypted with a 256-bit Advanced Encryption Standard (AES) key. All keys are strengthened with Password-Based Key Derivation Function 2 (PBKDF2). {: ios}

Use closeAllCollections to lock access to all the collections until you call openCollections again. If you think of openCollections as a login function, you can think of closeAllCollections as the corresponding logout function. {: ios}

Use changeCurrentPassword to change the password. {: ios}

let collection:JSONStoreCollection = JSONStoreCollection(name: "people")
collection.setSearchField("name", withType: JSONStore_String)
collection.setSearchField("age", withType: JSONStore_Integer)

let options:JSONStoreOpenOptions = JSONStoreOpenOptions()
options.password = "123"

do {
  try JSONStore.sharedInstance().openCollections([collection], withOptions: options)
} catch let error as NSError {
  // handle error
}

{: codeblock} {: ios}

You can secure all the collections in a store by passing a JSONStoreInitOptions object with a password to the openCollections function. If no password is passed, the documents of all the collections in the store are not encrypted. {: android}

Some security metadata is stored in the shared preferences (Android). The store is encrypted with a 256-bit Advanced Encryption Standard (AES) key. All keys are strengthened with Password-Based Key Derivation Function 2 (PBKDF2). {: android}

Use closeAllCollections to lock access to all the collections until you call openCollections again. If you think of openCollections as a login function, you can think of closeAllCollections as the corresponding logout function. {: android}

Use changeCurrentPassword to change the password. {: android}

Context context = getContext();
try {
  JSONStoreCollection people = new JSONStoreCollection("people");
  people.setSearchField("name", SearchFieldType.STRING);
  people.setSearchField("age", SearchFieldType.INTEGER);
  List<JSONStoreCollection> collections = new LinkedList<JSONStoreCollection>();
  collections.add(people);
  JSONStoreInitOptions options = new JSONStoreInitOptions();
  options.setPassword("123");
  WLJSONStore.getInstance(context).openCollections(collections, options);
  // handle success
} catch(JSONStoreException e) {
  // handle failure
}

{: codeblock} {: android}

Multiple user support in JSONStore

{: #multiple_user_jsonstore}

You can create multiple stores that contain different collections in a single MobileFirst application. The init function can take an options object with a user name. If no user name is given, the default user name is jsonstore. {: cordova}

var collections = {
  people: {
    searchFields: {name: 'string'}
  }
};
var options = {username: 'yoel'};
WL.JSONStore.init(collections, options).then(function () {
    // handle success
}).fail(function (error) {
    // handle failure
});

{: codeblock} {: cordova}

You can create multiple stores that contain different collections in a single MobileFirst application. The init function can take an options object with a user name. If no user name is given, the default user name is jsonstore. {: ios}

let collection:JSONStoreCollection = JSONStoreCollection(name: "people")
collection.setSearchField("name", withType: JSONStore_String)
collection.setSearchField("age", withType: JSONStore_Integer)

let options:JSONStoreOpenOptions = JSONStoreOpenOptions()
options.username = "yoel"

do {
  try JSONStore.sharedInstance().openCollections([collection], withOptions: options)
} catch let error as NSError {
  // handle error
}

{: codeblock} {: ios}

You can create multiple stores that contain different collections in a single Mobile Foundation application.The openCollections function can take an options object with a user name. If no user name is given, the default user name is jsonstore. {: android}

Context context = getContext();
try {
  JSONStoreCollection people = new JSONStoreCollection("people");
  people.setSearchField("name", SearchFieldType.STRING);
  people.setSearchField("age", SearchFieldType.INTEGER);
  List<JSONStoreCollection> collections = new LinkedList<JSONStoreCollection>();
  collections.add(people);
  JSONStoreInitOptions options = new JSONStoreInitOptions();
  options.setUsername("yoel");
  WLJSONStore.getInstance(context).openCollections(collections, options);
  // handle success
} catch(JSONStoreException e) {
  // handle failure
}

{: codeblock} {: android}

Adapter integration

{: #adapter_integration}

Adapter integration is optional and provides ways to send data from a collection to an adapter and get data from an adapter into a collection. You can achieve these goals by using WLResourceRequest or jQuery.ajax if you need more flexibility. {: cordova}

  1. Create an adapter and name it JSONStoreAdapter.
  2. Define it's procedures addPerson, getPeople, pushPeople, removePerson, and replacePerson.
    function getPeople() {
     var data = { peopleList : [{name: 'chevy', age: 23}, {name: 'yoel', age: 23}] };
     WL.Logger.debug('Adapter: people, procedure: getPeople called.');
     WL.Logger.debug('Sending data: ' + JSON.stringify(data));
     return data;
     }
     function pushPeople(data) {
         WL.Logger.debug('Adapter: people, procedure: pushPeople called.');
         WL.Logger.debug('Got data from JSONStore to ADD: ' + data);
         return;
     }
     function addPerson(data) {
         WL.Logger.debug('Adapter: people, procedure: addPerson called.');
         WL.Logger.debug('Got data from JSONStore to ADD: ' + data);
         return;
     }
     function removePerson(data) {
         WL.Logger.debug('Adapter: people, procedure: removePerson called.');
         WL.Logger.debug('Got data from JSONStore to REMOVE: ' + data);
         return;
     }
     function replacePerson(data) {
         WL.Logger.debug('Adapter: people, procedure: replacePerson called.');
         WL.Logger.debug('Got data from JSONStore to REPLACE: ' + data);
         return;
     }
    {: cordova}
  3. To load data from an adapter use WLResourceRequest.
    try {
      var resource = new WLResourceRequest("adapters/JSONStoreAdapter/getPeople", WLResourceRequest.GET);
      resource.send()
      .then(function (responseFromAdapter) {
           var data = responseFromAdapter.responseJSON.peopleList;
      },function(err){
           //handle failure
      });
     } catch (e) {
         alert("Failed to load data from adapter " + e.Messages);
     }
    {: cordova}
  4. Calling getPushRequired returns an array of so called "dirty documents", which are documents that have local modifications that do not exist on the back-end system. These documents are sent to the adapter when push is called.
    var collectionName = 'people';
    WL.JSONStore.get(collectionName).getPushRequired().then(function (dirtyDocuments) {
         // handle success
    }).fail(function (error) {
       // handle failure
    });
    {: codeblock} {: cordova} To prevent JSONStore from marking the documents as "dirty", pass the option {markDirty:false} to add, replace, and remove. {: tip} {: cordova}
  5. You can also use the getAllDirty API to retrieve the dirty documents.
    WL.JSONStore.get(collectionName).getAllDirty()
     .then(function (dirtyDocuments) {
         // handle success
     }).fail(function (errorObject) {
         // handle failure
     });
    {: codeblock} {: cordova}
  6. To push changes to an adapter, call the getAllDirty to get a list of documents with modifications and then use WLResourceRequest. After the data is sent and a successful response is received make sure you call markClean.
    try {
      var collectionName = "people";
      var dirtyDocs;
    
      WL.JSONStore.get(collectionName)
      .getAllDirty()
      .then(function (arrayOfDirtyDocuments) {
         dirtyDocs = arrayOfDirtyDocuments;
    
         var resource = new WLResourceRequest("adapters/JSONStoreAdapter/pushPeople", WLResourceRequest.POST);
         resource.setQueryParameter('params', [dirtyDocs]);
         return resource.send();
         }).then(function (responseFromAdapter) {
             return WL.JSONStore.get(collectionName).markClean(dirtyDocs);
         }).then(function (res) {
           //handle success     
         }).fail(function (errorObject) {
           // Handle failure.
         });
    
     } catch (e) {
         alert("Failed To Push Documents to Adapter");
     }
    {: codeblock} {: cordova}
  7. Use enhance to extend the core API to fit your needs, by adding functions to a collection prototype. This example (the code snippet below) shows how to use enhance to add the function getValue that works on the keyvalue collection. It takes a key (string) as it's only parameter and returns a single result.
    var collectionName = 'keyvalue';
     WL.JSONStore.get(collectionName).enhance('getValue', function (key) {
         var deferred = $.Deferred();
         var collection = this;
         //Do an exact search for the key
         collection.find({key: key}, {exact:true, limit: 1}).then(deferred.resolve, deferred.reject);
         return deferred.promise();
     });
    
     //Usage:
     var key = 'myKey';
     WL.JSONStore.get(collectionName).getValue(key).then(function (result) {
         // handle success
         // result contains an array of documents with the results from the find
     }).fail(function () {
         // handle failure
     }); 
    {: codeblock} {: cordova}
  8. See the JSONStore sample for Cordova app from the Samples section. This project contains a Cordova application that uses the JSONStore API set. JavaScript adapter Maven project can be downloaded from here. {: cordova}

Adapter integration is optional and provides ways to send data from a collection to an adapter and get data from an adapter into a collection. You can achieve these goals by using WLResourceRequest. {: ios}

  1. Create an adapter and name it People.
  2. Define it's procedures addPerson, getPeople, pushPeople, removePerson, and replacePerson.
  3. To load data from an adapter use WLResourceRequest.
     // Start - LoadFromAdapter
     class LoadFromAdapter: NSObject, WLDelegate {
       func onSuccess(response: WLResponse!) {
         let responsePayload:NSDictionary = response.getResponseJson()
         let people:NSArray = responsePayload.objectForKey("peopleList") as! NSArray
         // handle success
       }
    
       func onFailure(response: WLFailResponse!) {
         // handle failure
       }
     }
     // End - LoadFromAdapter
    
     let pull = WLResourceRequest(URL: NSURL(string: "/adapters/People/getPeople"), method: "GET")
    
     let loadDelegate:LoadFromAdapter = LoadFromAdapter()
     pull.sendWithDelegate(loadDelegate)
    {: codeblock} {: ios}
  4. Calling allDirty returns an array of so called "dirty documents", which are documents that have local modifications that don’t exist on the back-end system.
     let collectionName:String = "people"
     let collection:JSONStoreCollection = JSONStore.sharedInstance().getCollectionWithName(collectionName)
    
     do {
       let dirtyDocs:NSArray = try collection.allDirty()
     } catch let error as NSError {
       // handle error
     }
    {: codeblock} {: ios} To prevent JSONStore from marking the documents as "dirty", pass the option {markDirty:false} to add, replace, and remove. {: tip}
  5. To push changes to an adapter, call the allDirty to get a list of documents with modifications and then use WLResourceRequest. After the data is sent and a successful response is received make sure you call markDocumentsClean.
     // Start - PushToAdapter
     class PushToAdapter: NSObject, WLDelegate {
       func onSuccess(response: WLResponse!) {
         // handle success
       }
    
       func onFailure(response: WLFailResponse!) {
         // handle failure
       }
     }
     // End - PushToAdapter
    
     let collectionName:String = "people"
     let collection:JSONStoreCollection = JSONStore.sharedInstance().getCollectionWithName(collectionName)
    
     do {
       let dirtyDocs:NSArray = try collection.allDirty()
       let pushData:NSData = NSKeyedArchiver.archivedDataWithRootObject(dirtyDocs)
    
       let push = WLResourceRequest(URL: NSURL(string: "/adapters/People/pushPeople"), method: "POST")
    
       let pushDelegate:PushToAdapter = PushToAdapter()
       push.sendWithData(pushData, delegate: pushDelegate)
    
     } catch let error as NSError {
       // handle error
     }
    {: codeblock} {: ios}
  6. Download the native iOS Swift application project from the Samples section. The project contains a native iOS Swift application that uses the JSONStore API set. JavaScript adapter Maven project can be downloaded from here. {: ios}

Adapter integration is optional and provides ways to send data from a collection to an adapter and get data from an adapter into a collection. You can achieve these goals by using functions such as WLResourceRequest or your own instance of an HttpClient if you need more flexibility. {: android}

  1. Create an adapter and name it JSONStoreAdapter.
  2. Define it's procedures addPerson, getPeople, pushPeople, removePerson, and replacePerson.
  3. To load data from an adapter use WLResourceRequest.
     WLResponseListener responseListener = new WLResponseListener() {
       @Override
       public void onFailure(final WLFailResponse response) {
         // handle failure
       }
       @Override
       public void onSuccess(WLResponse response) {
         try {
           JSONArray loadedDocuments = response.getResponseJSON().getJSONArray("peopleList");
         } catch(Exception e) {
           // error decoding JSON data
         }
       }
     };
    
     try {
       WLResourceRequest request = new WLResourceRequest(new URI("/adapters/JSONStoreAdapter/getPeople"), WLResourceRequest.GET);
       request.send(responseListener);
     } catch (URISyntaxException e) {
       // handle error
     }
    {: codeblock} {: android}
  4. Calling findAllDirtyDocuments returns an array of so called "dirty documents", which are documents that have local modifications that don’t exist on the back-end system.
     Context  context = getContext();
     try {
       String collectionName = "people";
       JSONStoreCollection collection = WLJSONStore.getInstance(context).getCollectionByName(collectionName);
       List<JSONObject> dirtyDocs = collection.findAllDirtyDocuments();
       // handle success
     } catch(JSONStoreException e) {
       // handle failure
     }
    {: codeblock} {: android} To prevent JSONStore from marking the documents as "dirty", pass the option options.setMarkDirty(false) to add, replace, and remove. {: tip} {: android}
  5. To push changes to an adapter, call the findAllDirtyDocuments to get a list of documents with modifications and then use WLResourceRequest. After the data is sent and a successful response is received make sure you call markDocumentsClean.
     WLResponseListener responseListener = new WLResponseListener() {
       @Override
       public void onFailure(final WLFailResponse response) {
         // handle failure
       }
       @Override
       public void onSuccess(WLResponse response) {
         // handle success
       }
     };
     Context context = getContext();
    
     try {
       String collectionName = "people";
       JSONStoreCollection collection = WLJSONStore.getInstance(context).getCollectionByName(collectionName);
       List<JSONObject> dirtyDocuments = people.findAllDirtyDocuments();
    
       JSONObject payload = new JSONObject();
       payload.put("people", dirtyDocuments);
    
       WLResourceRequest request = new WLResourceRequest(new URI("/adapters/JSONStoreAdapter/pushPeople"), WLResourceRequest.POST);
       request.send(payload, responseListener);
     } catch(JSONStoreException e) {
       // handle failure
     } catch (URISyntaxException e) {
       // handle error
     }
    {: codeblock} {: android}
  6. Download the native Android application project from the Samples section. The project contains a native Android application that uses the JSONStore API set. JavaScript adapter Maven project can be downloaded from here. {: android}