BusinessRules

The BusinessRules desktop sample demonstrates how Business Rules Scripting can be used to validate changes made by client applications at the server level, before committing the changes to the back-end database. The benefit of putting change validation in the middle tier is that rules can be changed and updated on the fly without having to deploy new versions of the client applications and deal with the possibility that some users have not yet updated to the latest client version.

Getting Started

The sample is typically located in /Developer/RemObjects Software/Samples/Data Abstract/Desktop/BusinessRules, though you may have installed the Data Abstract for Cocoa and it's samples in another location.

To build it, you will of course need Xcode, and like all the samples provided with Data Abstract for Cocoa you will need to be running the Relativity Server with the DASamples Domain and the Simple Schema available.

Running the Sample

This sample allows you to see the results of attempting to apply modifications/edits to table rows that are being validated on the server side by a business rules script.

When this sample is run you will see an empty table and a toolbar which contains a pop-up and three buttons. The "Load" button retrieves all of the records in the "ClientsWithScripting" table, when they are received the table will update with those records. The "Remove" button will become active when a record is selected, pressing it will delete the row only from the local copy of the table. Finally the "Apply" button is only active when the table has any changes, pressing it will attempt to apply the local changes to the server; if there are any issues then an alert sheet will be displayed with the relevant information.

The "Servers" popup contains a list of available servers discovered by the Zeroconf discovery system. When the sample is run, it starts a Zeroconf discovery service that looks for instances of Relativity Server running on the local network. Any discovered servers will be added to the popup, if no servers are discovered then the dropbox on the toolbar will be empty. You can specify a server address yourself by clicking on the popup and then clicking on "Specify custom URL...".

There are 3 validation rules in effect, and are highlighted at the bottom of the user interface. Try and break each rule to see what happens:

  • Remove a row from the table and press "Apply"
  • Double Click on a rows "Discount" field, change the number to a value outside of 0 to 60% and press "Apply"
  • Double Click on a rows "Birthdate" field, change the year to one in the future and press "Apply"

Below you can see the results of trying to make all three changes:

Examining the Code

The code for creating the connection to the server and handling logging in is covered by other samples, as is how to make and apply changes to the server. Here we are solely interested in the business rules.

App Structure

The sample is built around four classes (AppDelegate, DataAccess, ServiceAccess, and RegisterServerWindowController) and the business rules which are located in the "Simple" schema.

The DataAccess class handles everything related to interacting with the Data Abstract SDK; including retrieving data from an instance of Relativity Server and applying changes back to the server.

The ServiceAccess class handles the discovery of any instances of Relativity Server that are available on the local network using ROZeroConf which is a feature available with the Remoting SDK that Data Abstract is built upon. ServiceAccess sets up a ROZeroConfBrowser Class object which searches for any servers that broadcast a value matching the value defined for RELATIVITY_SERVICE_NAME. When a service is found, or indeed disappears, the server list is updated and the popup with the list of available servers is also updated. It also handles the registration of custom server addresses. To explore further the ServiceAccess class and Zeroconf discovery see the article: The ServiceAccess Class and Zeroconf discovery.

The RegisterServerWindowController class is a subclass of NSWindowController which handles the UI aspects of a user manually adding a server url.

Lastly the AppDelegate class handles the primary setup of the application, registers that it will listen for notifications broadcast by the ServiceAccess and DataAccess classes, and acts as a delegate to the RegisterServiceWindowController, DataAccess and NSTableView classes. The main thing of note refreshUI: which is called when the NOTIFICATION_REPORT_READY notification is received and will reload the table and display the new data.

The Business Rules

Business Rules are located on the server in a schema and allow you to add business logic that can be used to validate data or enforce access restrictions. The rules are written as small blocks of EcmaScript, otherwise known as JavaScript, that are triggered on certain events for instance on processing an error, after applying a delta change, before getting data and so on. To learn more about business rules, how to add/edit them and the events available check out the articles Business Rules Scripting, Business Rules Scripting API and Business Rules Scripting Events.

Here we are interested in the rules that are assigned to the "ClientsWithScripting" table. If you open Server Explorer and connect to Relativity Server, navigate to the "ClientsWithScripting" table, expand the tree and click on "Scripts". This will display a page which contains the business rules used in this sample (duplicated below) which you can modify to test if you desire.

The beforeProcessDeltaChange event is trigged each time a change received from a client application, but before the change has been processed. It takes four arguments that are:

  • a Delta object that holds all of the changes.
  • a Delta Change object that holds information about a single delta change and contains the old and new values
  • wasRefreshed a bool this indicates if the record was refreshed
  • canRemove a bool to indicate if the record is currently allowed to be removed

There are 5 validation rules to cover all of the conditions used in this sample. If none of them generate a fail message, then the function returns and the changes are applied to the table. The fail message will not be returned until all of the delta changes have been processed.

// From the Schema
// Called before each change is applied to the database
function beforeProcessDeltaChange(delta, change, wasRefreshed, canRemove)
{
    var msg = "";
    var client = "ClientID=" + change.newValues['Id'];


    // Delete rule
    if (change.isDelete) {
        client = "ClientID=" + change.oldValues['Id'];
        msg = "Business Rule: #CLIENT: Deleting clients is prohibited!"
        .replace("#CLIENT", client);
        fail(msg);
    }

    // Discount rule. Min check
    var newDiscountValue = Math.round(100*change.newValues['Discount']);
    if (newDiscountValue < 0) {
        msg = "Business Rule: #CLIENT: Discount cannot be less than zero! Current value is: #VAL %"
        .replace("#CLIENT", client)
        .replace("#VAL", newDiscountValue);
        fail(msg);
    }

    // Discount rule. Max check
    if (newDiscountValue > 60) {
        msg = "Business Rule: #CLIENT: Discount cannot be greater than 60%! Current value is: #VAL %"
        .replace("#CLIENT", client)
        .replace("#VAL", newDiscountValue);
        fail(msg);
    }

    // Birthdate rule. Min check
    var minDate = new Date(1940, 1, 1, 0, 0, 0, 0);
    var newBirthdateValue = change.newValues['Birthdate'];
    if (minDate > newBirthdateValue) {
        msg = "Business Rule: #CLIENT: Birthdate cannot be less than 1940! Current value is: #VAL"
        .replace("#CLIENT", client)
        .replace("#VAL", newBirthdateValue);
        fail(msg);
    }

    // Birthdate rule. Max check
    var maxDate = new Date();
    if (maxDate < newBirthdateValue) {
        msg = "Business Rule: #CLIENT: Birthdate cannot be in future! Current value is: #VAL"
        .replace("#CLIENT", client)
        .replace("#VAL", newBirthdateValue);
        fail(msg);
    }