2.1.4.5 Example written processor

Using script in written processors

The script is defined as a widget parameter named script. It is entered in an XML CDATA section, to avoid problems with characters like < or > or &.

By default, the entire script is executed for each record processed. If a parameter named function is present, it identifies a function in the script which is called for each record. This is more efficient.

Within the script, each processor input is mapped to the JavaScript name inputN, where N is the input ID from the XML. If the input allows multiple attributes, the value will be an array; otherwise it is a plain value.

Each output from the processor is assigned to the JavaScript name outputN, where N is the output ID from the XML.

Properties and Resources

First, note that processor options are termed 'properties' internally, and where a property uses Reference Data, this is termed a 'resource' internally.

A packaged script processor can refer to properties and resources.

Each property is mapped to a JavaScript variable with the same name as the property. Non-resource properties are set to the equivalent JavaScript types (String, Number, Date, etc). Each resource property is created as an internal type with methods and properties which allow the resource to be queried.

The following properties and methods are available on resource values:

Name Type Meaning

name

String

The name of the resource

keys

Number

The number of keys (lookup columns) defined for the resource

results

Number

The number of result values (return columns) defined for the resource

types

Array of Strings

The data types (STRING, NUMBER, DATE) of the keys (lookup columns) and results (return columns) – keys first, then results.

loadall()

Array of resource records

Load all the elements of the resource

query(k1, k2 …)

Array of resource records

Query the resource – the number and type of the keys must match the resource definition.

Each resource record returned by loadAll or query is an array containing the key values and result values for the resource record. If there are no matching records, the result of the method call is an empty array.

If the entire contents of a resource are required, a preload parameter should be set on the property definition to indicate that the framework should load the resource once and share the values amongst the process threads, as in:

<resource name="resprop" required="false" structure="1:0" loadable="true">
<guidata><label>Resource property</label></guidata>
        <parameters>
          <parameter name="preload">true</parameter>
        </parameters>
</resource>

The loadAll call will then return the shared array. Given this property definition, the following JavaScript code fragment could be used to output some information on the resource:

var records = resprop.loadall();
output1 = 'Name: ' + resprop.name + "; keys: " + resprop.keys +
"; results: " + resprop.results;
output1 += ' (' + records.length + ' records)'

Complete Example

This is a complete example of a widgets.xml file describing a credit card number validation processor (widget).

The actual details of the credit card validation are omitted, as the objective of this example is to describe how a processor (widget) is constructed.

In this case, the widget has two outputs: a 'success' flag (Y or N) and the credit card type (Amex, Mastercard, etc). The example code uses a mod 10 of the sum of the digits as a lookup into an array of types.

Example 2-9 Complete widgets.xml file

<?xml version="1.0" encoding="UTF-8"?>
<widgets>
  <comment>Example packaged JavaScript widgets</comment>
   <groupid>1000:custom</groupid>
  <!-- ========== -->
  <!-- CC checker -->
  <!-- ========== -->
  <widget id="rde:scdemo" class="com.datanomic.director.widget.scripting.JavaScriptWidget">
    <guidata>
      <label>Simple CC check demo thing</label>
      <group>Custom scripts</group>
    </guidata>
    <!-- inputs -->
    <inputs>
      <input id="1" type="string">
        <guidata><label>Input CC</label></guidata>
      </input>
    </inputs>
    <!-- outputs -->
    <outputs cardinality="1:1">
      <output id="1" type="string" name="valid" metadata="true">
        <guidata><label>Validity</label></guidata>
      </output>
      <!-- CC type or message -->
      <output id="2" type="string" name="type">
        <guidata><label>Type</label></guidata>
      </output>
    </outputs>
    <!-- Spigots -->
    <spigots>
      <!-- All -->
      <spigot id="1">
        <guidata><label>All</label></guidata>
      </spigot>
      <!-- Valid -->
      <spigot id="2">
        <guidata><label>Valid</label></guidata>
        <filter outputid="1" value="Y" equals="true"/>
      </spigot>
      <!-- Invalid -->
      <spigot id="3">
        <guidata><label>Invalid</label></guidata>
        <filter outputid="1" value="N" equals="true"/>
      </spigot>
    </spigots>
    <!-- Results -->
    <results>
      <metrics>
        <!-- Summary metric: key is valid string -->
        <metric id="1" type="fixed">
  <fixedrecords fromspigots="true"/>
          <drilldowns>
            <metricdrill name="types" metricid="2" view="counter"/>
            <datadrill name="data" destid="1"/>
          </drilldowns>
        </metric>
        <!-- Frequency count of types -->
        <metric id="2" type="variable">
          <!-- Simple counter, ignoring no data values -->
          <variablerecords keyval="output:2">
            <filter outputid="1" value="Y"/>
          </variablerecords>
          <!-- Drilldown by type -->
          <drilldowns>
            <datadrill name="bytype" destid="2"/>
          </drilldowns>
        </metric>
      </metrics>
      <views default="summary">
        <!-- Summary view with:
                1. Number and % of records with valid values
                2. Number and % of records with invalid values
        -->
        <view name="summary" viewtype="fixed" metricid="1">
          <guidata>
            <label>Summary</label>
          </guidata>
          <columns>
            <!-- Number and % of records with valid values-->
            
            <columngroup drilldown="types">
              <guidata><label>Valid</label></guidata>
              <selector match="Y"/>
              <column datum="$count">
                <guidata><label>Valid</label></guidata>
              </column>
              <column datum="$percent" format="0.0">
                <guidata><label>%</label></guidata>
              </column>
            </columngroup>
            <!-- Number and % of records with invalid values-->
            
            <columngroup drilldown="data">
              <guidata><label>Invalid</label></guidata>
              <selector match="N"/>
              <column datum="$count">
                <guidata><label>Invalid</label></guidata>
              </column>
              <column datum="$percent" format="0.0">
                <guidata><label>%</label></guidata>
              </column>
            </columngroup>
          </columns>
        </view>
        <!-- Counter view -->
        <view name="counter" viewtype="variable" metricid="2" sortby="cp.countcol d" drilldown="bytype">
          <guidata>
            <label>Credit card types</label>
          </guidata>
          <!-- Type and count -->
          <columns>
            <column datum="$key">
              <guidata><label>Type</label></guidata>
            </column>
            <!-- Number and % of records matching the pattern -->
            
            <columnset name="cp" ref="countpercent"/>
          </columns>
        </view>
      </views>
    </results>
    <!-- Parameters -->
    <parameters>
      <parameter name="script">
<![CDATA[
///*************************************************************************
// string checkcc([String CardNumber])
//
// Returns CC type or null if invalid.  Simple demo mod 10 of sum of digits
// to determine CC type.
//
//*************************************************************************
var types = [ 'Amex', 'Mastercard', 'Visa' ];
function checkcc(ccnumber) {
  if (ccnumber == null || ccnumber.length == 0)
    return null;
  var sum = 0;
  for (var i = 0; i < ccnumber.length; i++)
    { var c = ccnumber.charCodeAt(i) - 48;
      if (c >= 0 && c <= 9)
        sum += c;
       else
        return null;
    }
    sum = sum % 10;
    return sum > types.length ? null : types[sum];
  }
function doit()
 { var type = checkcc(input1);
    
   output1 = type == null ? 'N' : 'Y';
   output2 = type == null ? '-' : type;
 }
]]>
      </parameter>
      <parameter name="function">doit</parameter>
    </parameters>
  </widget>  
  <!-- Shared columnset for count and percent columns -->
  <columnset name="countpercent">
    <column name="countcol" datum="$count">
      <guidata><label>Count</label></guidata>
    </column>
    <column name="percentcol" datum="$percent" format="0.0">
      <guidata><label>%</label></guidata>
    </column>
  </columnset>
</widgets>