Coach validation errors in one place

Imagine you want to see all IBM BPM coach validation errors in one place. By default after validation phase you need to find all invalidated widgets one by one, changing tab or opening accordions, and so on. Coach framework does not give any programmatic tool to get all validation messages from inside of coach view. While SPARK / BPMUI library gives some helper functions it does not cover all cases. Here I discuss simple way to plug-in to (or hack) coach engine and SPARK library to collect all validations at once.

Typical client-side validation

Typical client-side validation

The idea of problem is depicted below. There is a coach/screen that does validation, like on diagram aside, marking variables as invalid. On way back to coach the framework engine processes them and propagates invalid states to widgets. This way each coach view can be notified, via validation even handler, that its validity has changed – see (1) and (2) markers on screenshot below. This is the only extension place provided by coach framework and it is not enough to build single coach view that would display all errors at once, as I imagined (and implemented) like part (3) marked below. In some use cases there are errors not connected to any widget (like failing server calls) or validations that are coupled in a way that viewing them in one place would give user chance to solve them faster.

Collecting errors in one place as marked (3)

Collecting errors in one place as marked (3)

The SPARK UI toolkit (that became official UI library in BPM 8.6) put on top of coach framework comes with great help in validations. It introduces its own validation mechanics and distribution as SPARK widgets may work without bound variables yet being marked as valid or invalid via setValid() method on widgets. This way any invalidated SPARK widget, bound or boundless, can be reported by global method bpmext.ui.getInvalidViews(). This also works for custom widgets that are being initialized as SPARK compatible ones, via bpmext.ui.loadView() or bpmext.ui.loadContainer() methods during widget load phase. Minor issue is that SPARK approach does not cover overusing the tw.system.coachValidation with errors attached to non-existent variables one could use for widgetless errors. More limiting is lack of global validation notification on SPARK level, so that client is aware when validation state of screen changes and could re-read bpmext.ui.getInvalidViews() to get new results.

To solve first problem I took a look on how coach engine does the the validation. I placed breakpoint inside validate handler of my custom coachview to stop debugger there and inspect stack of calls. It immediatelly led me to handleValidationEvent inside the com.ibm.bpm.coach/engine module. While engine code is minified its public method names are still giving clue on processing steps and their sequence of calls. This way I realized that each view visited on validation gets called _deliverValidationEvents(event, viewMap, isClear) where event contains context information of processed validation, its event.domId is a key in viewMap? giving coach view object being validated, and optional flag isClear is set to true when validation is being cleared on widget instead of set new error. In addition I proxified handleValidationEvent to know when all validations erros are collected.

Hacking coach engine

Hacking coach engine

Engine validation handling routine

Engine validation handling routine

Engine validations dispatching routine

Engine validations dispatching routine

To cover all validations, including boundless SPARK widgets, I needed extra step. Review of bpmext package pointed me to updateViewValidationState method that is declared to be used by all widgets during validation phase. So I had candidate to be notified when SPARK is being manipulated.

    require(["com.ibm.bpm.coach/engine"], function (engine) {
        var dve = engine._deliverValidationEvents;
        engine._deliverValidationEvents = function (event, viewMap, isClear) {
            dve(event, viewMap, isClear); // original processing first
            console.log("_deliverValidationEvents", event, viewMap, isClear);
        }.bind(engine);
        var hve = engine.handleValidationEvent;
        engine.handleValidationEvent = function (event) {
            hve(event);
            console.log("handleValidationEvent", event);
        }.bind(engine);
    });
 
    var uvvs = bpmext && bpmext.ui && bpmext.ui.updateViewValidationState;
    if (uvvs) {
        bpmext.ui.updateViewValidationState = function (view, event) {
            uvvs(view, event); //call original handler
            console.log("updateViewValidationState", view, event);
        };
    }

Sequence of called proxies.

Sequence of called proxies.

Data provided to proxies.

Data provided to proxies.

To complete work there were two more problems to solve. First one was receiving duplicated notifications for bound widgets – from coach engine and from SPARK. Filtering redundant events on data collection phase was enough. Second issue was that SPARK does not notify when its validations end. I needed to generate synthethic event after burst of updateViewValidationState events. To do this part I crafted queueOnce(callback) method that when called multiple times waits until there is no more calls to itself in next event queue tick. Waiting in javascript even queue was important as SPARK uses setTimeout heavily to force specific order of events. So each call to updateViewValidationState called proxy collecting data and doing queueOnce passing finalization routine that run once after all updates finished.

Final result is that I have build two coaches. One to collect validations and another one to display them. When first one finishes its work using proxies described above, it fills in own binding being list of validation errors from all widgets. Second widget bound to that list listens on binding change notification and redisplays list of errors immediatelly after revalidation changes it. Such decoupling makes easier to adopt different display strategies per project without rebuilding or customizing widget collecting data.

This entry was posted in Software and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published.