Child pages
  • Developing the Mobile Service Solution

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
The mobile service solution manages the syncronisation of the offline data.

Requirements:
# Needs a form with name "offline_data"
# The form offline_data needs a method ws_read(version,name)
# If the data shown in mobile app is user specific the form offline_data needs a method ws_authenticate(useruid,password)
# The mobile_service and rest_ws plugin to be installed

h3. Constructing a offline data package for the mobile client

The ws_read(version,name) on the offline_data form has to return an OfflineDataDescription (=JSON) object filled with foundset data the developer wants the mobile client to retrieve.
An OfflineDataDescription instance is created with:
{code}
var retval = plugins.mobileservice.createOfflineDataDescription('data_');
{code}
Note: the argument is optional and is used to call rest endpoints for row data at forms starting with this prefix

Then we need to provide a list of relation names which should be used in traversal to find all data, like:
{code}
var traverse = new Array();
traverse.push('accountmanager_to_companies');
traverse.push('companies_to_contacts');
{code}

Lastly we have instruct the OfflineDataDescription to collect the data, starting with root foundset (containing records) and return.
{code}
retval.addFoundSet(fs_contact, traverse);
return retval;
{code}
Note:Basically addFoundSet in the service solution exposes an unrelated foundset to the mobile client, which can be used in an unrelated way like in a (first) form or databaseManager.getFoundset(...)
For each record in the provided (rootunrelated) foundset the specified relations are traversed and all data taken.

h3. User specific data

In order to provide a mobile client with user specific data the ws_authenticate(useruid,password) method should be added:
{code}
function ws_authenticate(useruid,password)
{
	//TODO find user and check password against pwhash column
	if (password == 'demo')//static demo check
	{
		var retval = new Object();
		retval.username = useruid;
		return retval;
	}
	return false;
}
{code}

In the ws_read method we can utilize the authenticate username variable via
{code}
var authenticate_info = questionParams.ws_authenticate[0];
globals.username = authenticate_info.username;
{code}
Here the authenticate username is put into a global variable which in turn can be used like:
{code}
//prepare personal data
var fs_contact = globals.contact_data$username;//global related foundset using username global var, containing the account manager contact
{code}

Full ws_read method for personalized data.
{code}
function ws_read(version,name)
{
	var questionParams = arguments[arguments.length-1];

	//create return value
	var retval = plugins.mobileservice.createOfflineDataDescription('data_');

	//setting the key for user_select relation
	var authenticate_info = questionParams.ws_authenticate[0];
	globals.username = authenticate_info.username;

	//prepare personal data
	var fs_contact = globals.contact_data$username;//global related foundset using username global var, containing the account manager contact

	/**
	 * @type {Array<String>}
	 */
	var traverse = new Array();
	traverse.push('accountmanager_to_companies');
	traverse.push('companies_to_contacts');

	retval.addFoundSet(fs_contact, traverse);
	return retval;
}
{code}

h3. Providing/retrieving entity(=table) row data

Row/record data is retrieved in separate calls for each entity, for example for "orders" row data results in a call to "orders" form is made on ws_read method.
Note: If a prefix is provided in the offlinedata the call will endup at prefix+entityname, example for prefix "data_" the call happens on form "data_orders"

ws_read is with a list of pks it wants as row data for, example code:
{code}
function ws_read(version,method)
{
	var questionParams = arguments[arguments.length-1];

	if (method == 'list')
	{
		/**
		 * @type {String}
		 */
		var ids = questionParams.ids[0];
		if (ids != null && ids != '')
		{
			/**
			 * @type {Array<Object>}
			 */
			var idsa = ids.split(',', -1);
			if (idsa.length > 0)
			{
				foundset.loadAllRecords();
				var json = plugins.mobileservice.getRowDescriptions(foundset, idsa)
				return json;
			}
		}
	}

	throw 404;
}
{code}
TIP: since ws_read for entities is likely the same, it might be beneficial to create a base form containing this logic and extend from this form

ws_update is called for changes made by mobile client, example code:
{code}
function ws_update(data,version,pk)
{
	if (foundset.find())
	{
		foundset.contact_id = pk;
		var count = foundset.search();
		if (count > 0)
		{
			var rec = foundset.getRecord(1);
			rec.name_first = data.name_first;
			rec.name_last = data.name_last;
			rec.email = data.email;
			rec.phone_cell = data.phone_cell;
			databaseManager.saveData(rec);
		}
		foundset.loadAllRecords();
	}
}
{code}

ws_create is called for new records on the mobile client, example code:
{code}
function ws_create(data,version,pk)
{
	var rec = foundset.getRecord(foundset.newRecord());

	rec.name_first = data.name_first;
	rec.name_last = data.name_last;
	rec.email = data.email;
	rec.phone_cell = data.phone_cell;
	rec.company_id = data.company_id;
	databaseManager.saveData(rec);

	foundset.loadAllRecords();
}
{code}
Note: the retrieved PK (and derived FK's) is always UUID's if the underlaying datamodel is not UUID based, keep and apply a mapping\!

h5. Other pages

{list-siblings}\\