Wizard: User Guide

Introduction to ZOAPIIO Wizard

Application Wizard is a super-fast and convenient way to build full-featured custom business applications on your ZOAPIIO platform. The input to the wizard is a JSON file, which can be created using any plain text editor. There is also a convenient graphical editor to create it in case you do not want to use a text editor or are not fully up to speed on the JSON file contents and its organization.

The Wizard creates the underlying database and the server APIs to manage the data. The Visualization application, bundled with forms the Web front-end of your application. The UI is modern, responsive (Mobile compatible), configurable, intuitive, and consistent.

Process is simple-

  1. Describe your application, database entities/relationships, presentation, access controls, workflows, and dashboard widgets etc. in the input (JSON).
  2. Let the wizard build and configure your entire application.
  3. Deploy the generated application to your instance.
  4. Launch the visualization app URL, point it to the generated application and start using it right away.
  5. Optionally and incrementally, add customization to your project's backend and front-end as necessary. Detailed and comprehensive customization is supported by the Wizard.
The application can be safely regenerated after changes to database entities - existing data in database is preserved. You can construct your application incrementally - simply edit-deploy-run - right from your favorite browser!

Anatomy of the Wizard Config

Let us begin the tour with a minimal example - kind of the customary "Hello World". The example below is minimal to the extent that is necessary to demonstrate the core features.

In the listings below, hover over the Highlighted Text to see explanatory tips.

The Configuration

This is the master application configuration that by itself is sufficient for the entire server for visualization to be generated. It contains database, presentation, and behavioral attributes of the application. If your application has customization, then references to the customization code (front-end and backend) are also put here. The visualization application runs off the APIs that are generated by the Wizard from this master configuration.

The business specific customization - both server and client-side - is provided from outside through separate independent files which are incorporated into the main application.

Let us start by looking at the configuration file first. You should make a Project folder on your desktop and put all project files together, starting with this.

Basicdemo.js

{
   "_comment": [
      "1. Basic Demo App to explain core features."
   ],
   "application": {
      "name": "Basicdemo",
      "title": "Basic Demo App",
      "themes": [
         {
            "name": "default"
            ,"type": "dark"
            ,"title": "Default (Dark)"
         },{
            "name": "theme-a"
            ,"type": "light"
            ,"title": "Alternate (Light)"
         }
      ],
      "userroles": ["super", "management", "operator"],
      "defaultrole": "super",
      "entities": [
         {
            "name": "setting", "label": "Settings"
	    ,"menu": "Setting"
	    ,"properties": [
            ]
	    ,"data": "setting"
	    ,"menuicon": "cogs"
	    ,"icon": "cog"
            ,"tip": "Organization Settings"
	    ,"access": { "full": ["management", "super"] }
	    ,"classification": { "open": "WWR-R-" }
         },{
            "name": "assettype", "label": "Asset Type"
            ,"menu": "Assets"
            ,"properties": [
               {
                  "name": "Category"
                  ,"label": "Asset Category"
                  ,"type": "easyselect:Asset,Resource,Inventory,Consumable,Spare"
                  ,"validation": "required", "cached": true
               },{
                  "name": "Description"
                  ,"label": "Comments"
                  ,"type": "string"
                  ,"validation": ""}
            ]
            ,"menuicon": "toolbox"
            ,"icon": "chevron-circle-right"
            ,"tip": "Asset type definition"
            ,"access": { "full": ["management", "super"] }
            ,"classification": { "open": "WWRR--" }
	    ,"dependents": [
               {
		  "name": "asset"
		  ,"property": "AssetTypeId"
               }
            ]
         },{
            "name": "assetsubtype", "label": "Asset Sub-type"
            ,"menu": "Assets"
            ,"properties": [
               {
                  "name": "Category"
                  ,"label": "Asset Category"
                  ,"type": "select:Asset,Resource,Inventory,Consumable,Spare"
                  ,"validation": "required", "cached": true
               },{"name": "AssetTypeId,AssetTypeName"
                  ,"label": "Asset Type"
                  ,"type": "entity:assettype"
                  ,"validation": "required"
                  ,"cached": true
		  ,"filter": "Category"
               },{
                  "name": "Description"
                  ,"label": "Comments"
                  ,"type": "string"
                  ,"validation": ""}
            ]
            ,"icon": "chevron-circle-down"
            ,"tip": "Asset sub-type definition"
            ,"access": { "full": ["management", "super"] }
            ,"classification": { "open": "WWRR--" }
            ,"dependents": [
               {
                  "name": "asset"
                  ,"property": "AssetSubtypeId"}
            ]
         },{
            "name": "asset", "label": "Asset"
            ,"menu": "Assets"
            ,"properties": [
               {"name": "Category"
                  ,"label": "Asset Category"
                  ,"type": "select:Asset,Resource,Inventory,Consumable,Spare"
                  ,"validation": "required", "cached": true
	       },{"name": "AssetTypeId,AssetTypeName"
                  ,"label": "Asset Type"
		  ,"type": "entity:assettype"
                  ,"validation": "required", "cached": true
		  ,"filter": "Category"
               },{"name": "AssetSubtypeId,AssetSubtypeName"
                  ,"label": "Asset Sub-type"
                  ,"type": "entity:assetsubtype"
                  ,"validation": "required"
		  ,"filter": "assettype"
               },{"name": "Ownership"
                  ,"label": "Ownership"
                  ,"type": "easyselect:Owned,Rented"
                  ,"validation": "required"
               },{"name": "SerialNo,AssetCode"
                  ,"label": "Serial Number"
                  ,"labels": ",Code"
		  ,"type": "multiple"
		  ,"types": "string,string"
                  ,"validation": ""
               },{"name": "POName,POFile"
                  ,"label": "Purchase Order"
		  ,"type": "file"
                  ,"validation": ""
               },{"name": "ServiceDue"
                  ,"label": "Service Due"
		  ,"type": "custom:BDServiceDue"
                  ,"validation": ""
               },{"name": "Description"
                  ,"label": "Description"
                  ,"type": "text"
                  ,"validation": ""
               }
            ]
            ,"icon": "tools"
            ,"tip": "An Asset"
            ,"access": { "full": ["management", "super"] }
	    ,"RESTaccess": true
	    ,"openaccess": false
            ,"classification": { "open": "WWRR--" }
	    ,"handler": { "aftersave": "ROUTINE.AfterAssetSave" }
	    ,"children": [
               {
                  "name": "servicelog"
                  ,"label": "Service Log"
		  ,"properties": [
                     {"name": "DateServiced"
                        ,"label": "Date of Service"
                        ,"type": "date"
                        ,"default": "today"
                        ,"validation": "required"
                     },{"name": "Description"
                        ,"label": "Service Comments"
                        ,"type": "string"
                        ,"validation": ""
                     }
                  ]
               }
            ]
         },{
            "name": "report", "label": "Asset Report"
	    ,"data": "report"
            ,"menu": "Assets"
	    ,"properties": [
               {"name": "AssetTypeId,AssetTypeName"
                  ,"label": "Asset Type"
                  ,"type": "entity:assettype"
                  ,"validation": "required"
               }
            ]
            ,"icon": "boxes"
            ,"tip": "The asset report"
	    ,"editor": "BDAssetReport"
            ,"access": { "full": ["management", "super"] }
            ,"classification": { "open": "------" }
         },{
            "name": "user", "label": "Users"
            ,"menu": "User"
            ,"properties": [
            ]
	    ,"data": "user"
            ,"menuicon": "user-ninja"
            ,"icon": "user"
	    ,"bootuser": "Username=admin,Password=nopassword,Name=Administrator"
            ,"access": { "full": ["management", "super"] }
            ,"classification": { "open": "WWWWRR" }
         },{
            "name": "usergroup", "label": "Teams"
            ,"menu": "User"
            ,"properties": [
            ]
	    ,"data": "usergroup", "icon": "users"
            ,"access": { "full": ["management", "super"] }
            ,"classification": { "open": "WWWWRR" }
         },{
            "name": "userprofile", "label": "My Profile"
            ,"menu": "User"
            ,"properties": [
            ]
	    ,"data": "userprofile", "icon": "users"
            ,"access": { "full": ["management", "super"] }
            ,"classification": { "open": "WWWWRR" }
         },{
            "name": "b2cuser", "label": "Customer"
            ,"menu": "User"
            ,"properties": [
            ]
	    ,"data": "b2cuser"
            ,"icon": "user-circle"
            ,"access": { "full": ["management", "super"] }
            ,"classification": { "open": "WWWWRR" }
            ,"handler": {
               "beforeregister": "ROUTINE.BeforeRegister"
               ,"afterregister": "ROUTINE.AfterRegister"
            }
         }
      ],
      "customization": {
         "scripts": [
            {
               "name": "misc"
               ,"file": "Basicdemo_misc.js"
            }
         ],
         "helpfiles": [
            {
               "name": "basic"
               ,"title": "Essential User Guide"
	       ,"file": "Basicdemo_help.html"
            }
         ],
         "handlers": [
            {
               "name": "custom"
	       ,"file": "Basicdemo_custom.webc"
            }
         ]
      }
   }
}
	

Server-Side Customization

The server-side programming in ZOAPIIO is done using the WebIDE designer (Refer to the ZOAPIIO Web-IDE: User Guide for full details). You will notice that the program below is not very readable, but do not worry because you will be using the graphical WebIDE editor to create this.
  1. In the config file, we mentioned the presence of the server customization in the application definition. So the Wizard will be expecting this file.
  2. When generating the application, leave blank when asked for the file.
  3. After the application is generated, modify it and add your custom additions - Services, Routines, Methods, even changes to the schema.
  4. Then from the WebIDE menu select "File -> Save Custom Changes". This will download the file containing only the changes you have made.
  5. When you must generate the application again, simply provide the custom changes file and these will be automatically added to the project after the generation is complete.
  6. You can, of course, repeat the same cycle multiple times. Edit the project any number of times and then save the custom changes. Remember to include the latest saved changes every time you generate.

Basicdemo_custom.webc

<PRMLCONFIG CONFIG="RRML">
<ROUTINE NAME="AfterAssetSave">
  <BIZ-RULE OPCODE="Comment" P1="Generate the AssetCode if blank."/>
  <BIZ-RULE OPCODE="Return" CONDITION="/_IN/AssetCode > """/>
  <BIZ-RULE OPCODE="Compute" P1="/_IN/AssetCode = /_IN@__GenerateAssetCode"/>
</ROUTINE>

<METHOD NAME="GenerateAssetCode">
private String GenerateAssetCode(Node in, Node out) {
	String type=get(in, "AssetTypeName");
	String serial=get(in, "SerialNo");
	while (type.length()<2) type+="X";
	return type.substring(0,2)+serial;
}
</METHOD>
</PRMLCONFIG>
	

Client-Side Customization

Basicdemo_misc.js

// Asset Report
function BDAssetReport (divid, params, form) {
	this.divid=divid;
	this.form=form;
	this.params=params;
	this.data=null;
	//
	this.voptsrq=this.form.getProperty('voptsrq', 'env');
	this.vdatarq=this.form.getProperty('vdatarq', 'env');
	this.assettypeid=params.AssetTypeId;
	// Fetch the data from the server.
	this.getData();
}
// Class methods to implement the report.
BDAssetReport.prototype.getData = function() {
	var qry='vapp/getdata/asset+servicelog/_';
	var reqobj={}, req={}; reqobj['asset']={};
	reqobj['asset']['AssetTypeId'] = this.assettypeid;
	req[this.vdatarq]=reqobj;
	req[this.voptsrq]=[
		{'name': 'LIMIT', 'value': 100000}
	];
	this.form.getProperty([qry, req], 'query', [this.renderReport, this]);
}
BDAssetReport.prototype.renderReport = function(data) {
	this.data=data;
	var assets=data.asset||[];
	var servicelog=data.servicelog||[];
	var tabledata=[];
	var columns=[
		{'title': 'Asset Name'},
		{'title': 'Asset Type'},
		{'title': 'Ownership'},
		{'title': 'Last Service'}
	];
	for (var i in assets) {
		var d=assets[i];
		var assetid=d.Id;
		var lastservice='';
		for (var j in servicelog) {
			if (servicelog[j].ParentId == d.Id && servicelog[j].DateServiced > lastservice)
				lastservice=servicelog[j].DateServiced;
		}
		var item=[d.Name, d.AssetTypeName, d.Ownership, lastservice];
		tabledata.push(item);
	}
	var html='';
	html+='<div id="'+this.divid+'_g" style="width: 100%"></div>';
	html+='<table id="'+this.divid+'_dt" class="display compact"></table>';
	$('#'+this.divid).html(html);

	var buttons=[ 'copy', 'excel', 'pdf', 'csv', 'print' ];
	$('#'+this.divid+'_dt').DataTable({
		dom: 'Bfrtip'
		,buttons: buttons
        	,data: tabledata
        	,columns: columns
		,order: []
	});
}

// JS Class for custom Control Property - ServiceDue.
function BDServiceDue (div, names, form) {
	this.div=div;
	this.form=form;
	this.vdatarq=this.form.getProperty('vdatarq', 'env');
	this.assetid=form.getProperty('Id');
	this.render();
	this.updateValue();
}
BDServiceDue.prototype.updateValue = function() {
	// If this is an attached asset, get its availability from the host asset.
	var qry='vapp/getdata/servicelog/'+this.assetid;
	var reqobj={}, req={};
	req[this.vdatarq]=reqobj;
	var that=this;
	this.form.getProperty([qry, req], 'query', function(data) {
		var slogs=data.servicelog||[];
		var lastservice='';
		for (var i in slogs) {
			if (lastservice < slogs[i].DateServiced) lastservice=slogs[i].DateServiced;
		}
		var html='';
		html+='<span>'+lastservice+'</span>';
		$(this.div).html(html);
	});
}
BDServiceDue.prototype.render = function() {
	var html='';
	html+='<table width=100%></table>';
	$(this.div).html(html);
}
BDServiceDue.prototype.get = function(valonly) {
	if (valonly) return '';
	return [''];		// Not saved, only displayed.
}
BDServiceDue.prototype.handleFormChange = function(name) {
}
	

User Help File

Basicdemo_help.html

<!-- Single page help file. -->
<style>
body { font-family: Helvetica; color: #555; font-size: 16px; }
.content { padding: 2px 315px 0 7px; }
.title { font-size: 25px; font-family: Helvetica; color: #18335e; }
h1 { font-size: 22px; color: #222; font-family: Arial; }
h2 { font-size: 18px; color: #333; font-family: Helvetica; }
h3 { font-size: 17px; color: #555; font-family: Arial; text-decoration: underline; }
</style>

<div class="content">
<p class="title">Basic Demo App: User Guide.</p>

<h1>Introduction to Basic Demo</h1>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean hendrerit eros convallis,
consectetur dui vel, vestibulum velit. Vivamus eget odio ex. Proin ut nulla nunc. Sed felis mi, porttitor
et dolor id, aliquam blandit felis. Cras vulputate non massa sed sollicitudin. Aliquam rutrum augue in
nulla dictum, in rhoncus est tristique. Donec at purus et risus lobortis mollis nec eget arcu. Sed tempus
velit at porttitor convallis. Quisque aliquam, turpis in auctor tincidunt, leo massa ornare nibh, nec
tempus dolor ex id sapien. Aenean mollis sapien vel magna ultrices pellentesque. Aenean eu nisl dapibus,
sodales enim sed, efficitur nisl.</p>

</div>
	

Running the Wizard

In the last section, we saw how to create the configuration file for the Wizard. It is advisable to build most of your basic application first and then incrementally add the customization as necessary.

A medium sized application with limited customization can be created in under a week.

Create a folder on your desktop, where you will save the configuration files. When you save something from the WebIDE designer, it will always be saved in your Downloads folder. You will need to manually move it to your project folder. Login to the Designer and follow the Menu File > New Project > App Wizard. The Wizard page will appear.

  1. If you have a Wizard configuration file saved from an earlier run, or you manually created the configuration, choose the file and click on "Load from File".
  2. The JSON code will be displayed in the box below for reference only - you are not required to edit it directly.
  3. To edit the configuration, click on "Visual Editor" to open the visual editor. However, you may find that using a text editor to edit the configuration is more convenient.
  4. Navigate the intuitive Visual Editor and create your application definition.
  5. Always locally save the configuration JSON, because it cannot be retrieved from the server. To save the JSON file locally, use "Save to File" button.
  6. How much of visual editing and manual editing you want to use if up to your preferences.
  7. Once ready, click on the "Continue" button. The wizard input will be validated and if all is well, you will go to the next page, where you can select the "customization" files that are a part of your application. Click "Continue".
  8. The application will be generated.
  9. Deploy the generated application to your instance.

Running your Application

The visualization app is available as a part of your instance. You can launch it on your generated application using the URL-

{Your Instance URL}/vapp/index.html?{Your App name}

You can create multiple Wizard applications and use them as independent applications.

  1. It is advised that you use the bootuser feature to automatically create the first user in the database.
  2. If you have created the boot user, login using the credentials and start using the application.
  3. If you have not used the boot user, then at the time of application launch, it will give you an opportunity to create the first user from the app itself.
  4. Create user accounts for other users who will be using the app.

You are all set. This completes our "Getting Started" part of the guide.

Using your learning so far, you should be able to create Wizard configuration files, simple customizations, generate, deploy and start using fully functional enterprise apps. At the end of this guide, there are some screenshots to give you an idea of what your finished app will look like.

In the following sections we will go through more details and nuances that almost all real-world applications require - such as workflow control, access control, documents & attachments, dashboards etc.

Regenerating the Application

You can safely regenerate the Wizard application, anytime you change the configuration.

  1. If you have made any changes to the entity declarations, the tables would be regenerated without loss of data".
  2. If you have not made any change to the entity declarations or server handlers, and only making change to a supporting customization file, then you should not regenerate the application.
  3. Before clicking on "Continue", change "Update Artifacts Only" to Yes. The designer will only update the artifacts, without regenerating the entire application.

The Generated Application

This section is for advanced users only. If you do not plan to significantly customize the application, you can skip this section - or read it for academic interest.

Let us look at the generated application. You do not need to know most of these things, but you might require familiarity with the APIs that drive the visualization app. For instance,

  1. You may use these APIs while building customization in your app. You will notice that in our example app above, we used one of these APIs (getdata) to fetch data from the server.
  2. You may want other applications to integrate with your app using these APIs.

While calling the API from a custom front-end component (report, or control), the access rights of the logged-in user are used. For inbound integration with another application, explicit authentication must be performed before calling the services. See Integrating other Applications.

The Request/Response Object

Reproduced below are some snippets from the data schema - The hierarchical data structures that the APIs use to communicate. The REQUEST node is the abstraction of the HTTP request - GET as well as POST - for the service APIs. What you see below is not the full structure - you will notice the elements with DATATYPE names like "TYP:..." - this refers to a detailed reusable structure declared elsewhere.


  <REQUEST>
    <HTTP-HEADER NAME="Authorization"/>
    <HTTP-HEADER NAME="User-Agent"/>
    <TAG NAME="VAPP_voptsRQ" DUPLICATES="true">
      <TAG NAME="name" DATATYPE="STRING" ISKEY="true"/>
      <TAG NAME="value" DATATYPE="STRING"/>
    </TAG>
    <TAG NAME="VAPP_vechoRQ">
      <TAG NAME="ContentType" DATATYPE="STRING"/>
      <TAG NAME="FileName" DATATYPE="STRING"/>
      <TAG NAME="B64Data" DATATYPE="STRING"/>
    </TAG>
    <TAG NAME="VAPP_vanyRS">
      <TAG NAME="*ANY" DATATYPE="" DESCRIPTION="No Structure"/>
    </TAG>
    <TAG NAME="VAPP_vautoRQ">
      <TAG NAME="ColumnName" DATATYPE="STRING"/>
      <TAG NAME="EntityName" DATATYPE="STRING"/>
      <TAG NAME="ValuePrefix" DATATYPE="STRING"/>
    </TAG>
    <TAG NAME="VAPP_vloginRQ" DATATYPE="TYP:VAPP_vloginType"/>
    <TAG NAME="VAPP_vdataRQ" DATATYPE="TYP:VAPP_vdataType"/>
    <TAG NAME="VAPP_vwidgetRQ" DATATYPE="TYP:VAPP_vwidgetType"/>
    <TAG NAME="VAPP_vpushRQ" DATATYPE="TYP:VAPP_vpushType"/>
    <TAG NAME="VAPP_vprocletRQ" DATATYPE="TYP:VAPP_vprocletType"/>
  </REQUEST>
	

This is the Response message structures used by the APIs. Again, this is not the complete structure.


  <TAG NAME="Response">
    <TAG NAME="Status" DATATYPE="STRING" DUPLICATES="true"/>
    <TAG NAME="TimeStamp" DATATYPE="DATETIME"/>
    <TAG NAME="VAPP_vdataRS" DATATYPE="TYP:VAPP_vdataType"/>
    <TAG NAME="VAPP_vprocletRS" DATATYPE="TYP:VAPP_vprocletType"/>
    <TAG NAME="VAPP_vwidgetRS" DATATYPE="TYP:VAPP_vwidgetType"/>
    <TAG NAME="VAPP_vfileRS" DATATYPE="STRING"/>
    <TAG NAME="VAPP_vechoRS" DATATYPE="STRING"/>
    <TAG NAME="VAPP_vautoRS">
      <TAG NAME="result" DATATYPE="STRING" DESCRIPTION="Result Values" DUPLICATES="true">
        <TAG NAME="Value" DATATYPE="STRING"/>
        <TAG NAME="Count" DATATYPE="NUMBER"/>
      </TAG>
    </TAG>
    <TAG NAME="VAPP_vloginRS" DATATYPE="EXT:Basicdemo_userType">
      <TAG NAME="B2C" DATATYPE="TYP:Basicdemo_b2cuserType"/>
    </TAG>
    <TAG NAME="VAPP_vconfigRS">
      <TAG NAME="config" DUPLICATES="true">
        <TAG NAME="Name" DATATYPE="STRING"/>
        <TAG NAME="RecType" DATATYPE="STRING"/>
        <TAG NAME="Type" DATATYPE="STRING"/>
        <TAG NAME="Data" DATATYPE="STRING"/>
      </TAG>
    </TAG>
  </TAG>
	

Reproduced below is a section of the TYPES node. The declarations under TYPES represent the definition only - not the actual data. These structures are referenced in the schema using the TYP:... notation.


  <TYPES>
    <TAG NAME="VAPP_vdataType">
      <TAG NAME="Dashboards" DUPLICATES="true">
        <TAG NAME="Id" DATATYPE="NUMBER"/>
        <TAG NAME="Name" DATATYPE="STRING"/>
        <TAG NAME="Type" DATATYPE="STRING"/>
        <TAG NAME="Owner" DATATYPE="STRING"/>
        <TAG NAME="OwnerGroup" DATATYPE="STRING"/>
        <TAG NAME="Classification" DATATYPE="STRING"/>
        <TAG NAME="Access" DATATYPE="STRING"/>
        <TAG NAME="Data" DATATYPE="STRING"/>
        <TAG NAME="Description" DATATYPE="STRING"/>
      </TAG>
      <TAG NAME="Polygons" DUPLICATES="true">
        <TAG NAME="Id" DATATYPE="NUMBER"/>
        <TAG NAME="Name" DATATYPE="STRING"/>
        <TAG NAME="Owner" DATATYPE="STRING"/>
        <TAG NAME="OwnerGroup" DATATYPE="STRING"/>
        <TAG NAME="Classification" DATATYPE="STRING"/>
        <TAG NAME="Access" DATATYPE="STRING"/>
        <TAG NAME="Data" DATATYPE="STRING"/>
        <TAG NAME="Description" DATATYPE="STRING"/>
      </TAG>
      <TAG NAME="MessageSummary" DUPLICATES="true">
        <TAG NAME="Username" DATATYPE="STRING"/>
        <TAG NAME="UnreadCount" DATATYPE="NUMBER"/>
      </TAG>
      <TAG NAME="setting" DATATYPE="TYP:Basicdemo_settingType" DUPLICATES="true"/>
      <TAG NAME="assettype" DATATYPE="TYP:Basicdemo_assettypeType" DUPLICATES="true"/>
      <TAG NAME="asset" DATATYPE="TYP:Basicdemo_assetType" DUPLICATES="true"/>
      <TAG NAME="servicelog" DATATYPE="TYP:Basicdemo_servicelogType" DUPLICATES="true"/>
      <TAG NAME="report" DATATYPE="TYP:Basicdemo_reportType" DUPLICATES="true"/>
      <TAG NAME="user" DATATYPE="TYP:Basicdemo_userType" DUPLICATES="true"/>
      <TAG NAME="usergroup" DATATYPE="TYP:Basicdemo_usergroupType" DUPLICATES="true"/>
      <TAG NAME="b2cuser" DATATYPE="TYP:Basicdemo_b2cuserType" DUPLICATES="true"/>
      <TAG NAME="userdevice" DATATYPE="TYP:Basicdemo_userdeviceType" DUPLICATES="true"/>
      <TAG NAME="pushmessage" DATATYPE="TYP:Basicdemo_pushmessageType" DUPLICATES="true"/>
      <TAG NAME="message" DATATYPE="TYP:Basicdemo_messageType" DUPLICATES="true"/>
      <TAG NAME="workflow" DATATYPE="TYP:Basicdemo_workflowType" DUPLICATES="true"/>
      <TAG NAME="notification" DATATYPE="TYP:VAPP_vnotificationType"/>
    </TAG>
    <TAG NAME="Basicdemo_assetType">
      <TAG NAME="Id" DATATYPE="NUMBER" ISKEY="true"/>
      <TAG NAME="ParentId" DATATYPE="NUMBER"/>
      <TAG NAME="Name" DATATYPE="STRING"/>
      <TAG NAME="Owner" DATATYPE="STRING"/>
      <TAG NAME="OwnerGroup" DATATYPE="STRING"/>
      <TAG NAME="Classification" DATATYPE="STRING"/>
      <TAG NAME="Access" DATATYPE="STRING"/>
      <TAG NAME="Deleted" DATATYPE="NUMBER"/>
      <TAG NAME="UpdatedBy" DATATYPE="STRING"/>
      <TAG NAME="UpdatedAt" DATATYPE="DATETIME"/>
      <TAG NAME="AssetTypeId" DATATYPE="NUMBER"/>
      <TAG NAME="AssetTypeName" DATATYPE="STRING"/>
      <TAG NAME="Ownership" DATATYPE="STRING"/>
      <TAG NAME="SerialNo" DATATYPE="STRING"/>
      <TAG NAME="AssetCode" DATATYPE="STRING"/>
      <TAG NAME="POName" DATATYPE="STRING"/>
      <TAG NAME="POFile" DATATYPE="STRING"/>
      <TAG NAME="ServiceDue" DATATYPE="STRING"/>
      <TAG NAME="Description" DATATYPE="STRING"/>
      <TAG NAME="workflow" DATATYPE="TYP:Basicdemo_workflowType" DUPLICATES="true"/>
    </TAG>
  </TYPES>
	

Service APIs

The two most important services used in customization and integration are the services that allow you to query and update the entity data - getdata and setdata. gethistory, and dumpfile are also useful and deal with fetching data.

getdata

This service API allows you to retrieve data using a non-trivial query structure.

  1. You can query data for a single entity (parent or child) or an entity along with one or more of its child entities.
  2. You can provide detailed filters for each of the entities in the query.
  3. There is a separate options object that can be passed to control what data and how much data should be retrieved.
  4. You can optionally fetch workflow information (history/status) with each entity.

The API is accessed at the following url

{Instance URL}/wc/{Wizard Project Name}/vapp/getdata/{P1}/{P2}/[P3]

URL Component Description
{Instance URL} This is the base URL of your ZOAPIIO instance. If the instance is hosted on ZOAPIIO cloud, you can get this from the control panel. If you are using on-premise hosting, your system administrator would know the base URL.

wc This prefix is mandatory for all ZOAPIIO HTTP services, to distinguish it from other types of services (SOAP, GRAPHQL, etc.) supported by ZOAPIIO.

{Wizard Project Name} This is your project name - in the case of our example this is Basicdemo. You can create multiple projects in the same instance, and this makes sure there is no conflict between the services of different applications.

vapp This path component is added by the Wizard to suggest that this is a service used by the visualization app.

getdata The name of the service itself.

{P1} The first path parameter to the service is the name(s) of the entity to be fetched.
  1. To fetch records from a single entity, just put the name.
  2. To additionally fetch the details of the entity's child entities append the child's name separated by a '+'.
E.g. asset, assettype, asset+servicelog.

{P2}

The second path parameter is the ParentId filter. For root level entities, this is always 0 and for child entities, this is the Id value of the parent.

A wildcard value of '_' (underscore) can be used if no filtering on ParentId is required.


[P3] The third, last and optional path parameter is the Id value if the entity record. This is a convenient way to fetch all data for an entity record, when its Id value is known. Filters are ignored when Id is present.
The getdata API.

Filter criteria and options are sent to the query as POST data in JSON format. An example of getdata query is-

POST http:.../wc/Basicdemo/vapp/getdata/asset+servicelog/_
{
	"vdataRQ": {
		"asset": {
			"SerialNo": "ABC%"
		},
		"servicelog": {
			"DateServiced": "2022-12-01,2022-12-31"
		},
	},
	"voptsRQ": [
		{ "name": "FETCH", "value": "SUMMARY" },
		{ "name": "LIMIT", "value": 500 }
	]
}
	
The above query will search and return up to 500 asset records - summary properties only - with SerialNo starting with ABC. Additionally, for each of these assets, if there is a servicelog record for service in Dec 2022, those records will also be returned.

Following are the rules for specifying the filter selection-

  1. If the Id value is present in the URL (Path parameter 3), then filter selection is not applied - only the record with the given Id will be returned.
  2. If one or more child entities are a part of the query, filter can be specified for each entity separately - as in example above.
  3. When fetching parent-child entity records together, the parent selection is run first, and the child selection is only applied to the child entities of the matching parents.
Property Type Filter Value
string, text The value can use '%' as the wildcard.

integer, decimal, date, datetime Two integer/decimal/date/datetime values representing the range must be provided separated by a comma. If you want to search for a fixed value, repeat the same value as both ends of the range. If a single value is provided, it implies a range with the given value as the lower end and no upper end. If the first value is blank, that implies range with no lower end. Examples-
  • 2,10: range 2 to 10 both inclusive.
  • 2,: Range 2 to infinity
  • 2: Range 2 to infinity
  • ,100: Range negative infinity to 100 inclusive.

decimalrange, daterange When searching for a property declared as a range - two names will be there - the search should use the first name and specify a single value. The search will look for entity records where the value given is within the range.

hashtag hashtag is a datatype supported by the Wizard. The input for the hashtag consists of multiple space separated words, each acting as a tag, which can be independently used in the search. The search value is a list of words separated by spaces and will search all entity records, which has all the tags given in the list.

entity The entity search works like the integer search (see above). Two integer values representing the Id of the target entity should be provided (separated by comma).
getdata API filters.

List of options-

Query Option Description
FETCH You can control the level of detail fetched using this option. Values - SUMMARY, FULL (default). Summary only fetches the basic properties - Name, Id and other properties flagged as "cached". (Also see LIGHT below)

LIMIT This is used to set a limit to the number of records that will be fetched. The default is 100. If multiple entity records are being fetched, this applies only to the primary entity only (first in the list).

LIGHT A true/false value option. If set to true, any 'file' type properties in the entity are not fetched. Usually, the file objects are large in size, and it is advised to always use option and if required, fetch the file object separately.

SINCE The value is a date value (yyyy-mm-dd) and only entity records modified on or after that date are fetched.

WORKFLOW Possible values - ASSIGNMENT. If specified, along with each entity record, its workflow assignment status is also returned.

INCLUDE This option provides an even finer control of which properties will be fetched. You can provide a comma separated list of property names, which you want returned with the result.

EXCLUDE This option also controls which properties to return. The property names mentioned in the value list are excluded from the response.
getdata API options.

setdata

This service API allows you to add or update entity data.

  1. The record level permissions are checked before allowing the update. See Access Control.
  2. The Meta properties - Id, ParentId, UpdatedBy, and UpdatedAt cannot be updated.
  3. The Meta properties - Owner, OwnerGroup, Classification, and Access can be updated if your current access credentials allow you to update Privileged properties of the entity record. Caution is advised when updating these properties, because they will affect the access rights to the updated record and your own account may lose access to the record.
  4. When updating a record, only the values present in the data are updated. This allows you to update only specified properties.
  5. Multiple entity records of different entity types can be updated in the same API call.
  6. If Id is present and is non-zero, it implies an update. If the entity record with that Id is not present, the update fails. After update, the updated record is returned.
  7. If Id is not present or is zero, it implies an add. The data is added, and the newly created record is returned.

The API is accessed at the following URL

{Instance URL}/wc/{Wizard Project Name}/vapp/setdata

The path components are same as that for getdata. This API does not have any path parameters.

The data to be added/updated is sent to the query as POST data in JSON format. An example of setdata query is-

POST http:.../wc/Basicdemo/vapp/setdata
{
	"vdataRQ": {
		"asset": {
			"Id": 19,
			"SerialNo": "ABC0123"
		}
	}
}
	
The above query will update the SerialNo property of the asset with Id 19.

When creating a new entity record of a child entity, you must specify the ParentId property, unless you are also creating the parent entity in the same API call.

gethistory

This service API allows you to retrieve history of changes to an entity record.

  1. The generated app automatically maintains the history of changes to all records.
  2. To retrieve the history, you must know the Id of the entity record. You can get it by querying the database using getdata.

The API is accessed at the following url

{Instance URL}/wc/{Wizard Project Name}/vapp/gethistory/{Entity Name}/{Record Id}

The query can be called using GET or POST without any post data.

dumpfile

This service API allows you to retrieve just the contents of a 'file' property from an entity record. This is used to retrieve the contents of a file property as viewable raw binary output. The value of 'file' properties returned by getdata are in a Base64 encoded form. This API gets the raw decoded file.

The API is accessed at the following url

{Instance URL}/wc/{Wizard Project Name}/vapp/gethistory/{Entity Name}/{Record Id}/{Property Name}/[thumbnail]
  1. The {Property Name} path parameter must refer to a file property. File properties have multiple names - use the first name from the list.
  2. If the file (attachment) property refers to an image, additional path parameter 4 can be set as "thumbnail", to retrieve a thumbnail version of the image.

The query can be called using GET or POST without any post data.

Login Policy

The generated Business application tries to consider various login scenario that are typically used by organizations. Additionally, the Wizard allows you to customize the login process using server-side traps.

Your basic application includes a property-set linked to a database table by the name Z_Properties. You would use this to store application properties that need to be configurable from outside the application. The Wizard uses this property set to let you control the login behavior.

The following is the list of property names that you can add to your application. The properties can be added to your application from the Wizard config file itself (using the properties section under customization) or you can update the settings directly in the database table.

Each of the properties below must be prefixed with the application name separated by a period. E.g. if your application is called Sample, then the setting name would be Sample.login.policy... This will allow each application in your container to use a different login policy.

Also, when adding the property though the Wizard configuration file, the application name is automatically added - you should use only the base name.


Property Name Description
login.policy.ldap.UseLdap If 'true', the application will use an LDAP server for user authentication. See How LDAP authentication works below. Other LDAP related settings must be provided.

login.policy.ldap.Provider The URL ldap://... of the LDAP server to use for authentication.

login.policy.ldap.Domain The domain name of the LDAP. '@' and the domain name are appended to the Username before passing to the LDAP server..

login.policy.ldap.SecurityGroup The name of the security group on LDAP server corresponding to your ZOAPIIO application. This can be blank if security group check is not required.

login.policy.ldap.SearchDN This is used by the LDAP integration to search for the 'memberOf' attribute of the user. You should ask your LDAP administrator for the DN string.

login.policy.ldap.BootUser Even when you login using LDAP and local user record is expected to exist, with the exception of the boot user. When logging in with the boot user a local record may exist but is not required. The login completed and the user is assigned a local role (see next setting).

login.policy.ldap.BootUserRole When logging in as the boot user and if no local user record is found, this role is assigned to the user to complete the login.

The settings below do not apply when LDAP is in use.

login.policy.MinPasswordStrength This controls the minimum password strength that the users must have for their passwords. If a user is created with a lesser strength, the user password is deemed expired and the user must change it at next login. A value of 10 is very liberal and the value of 50 is recommended if you need to enforce strong passwords.

login.policy.PasswordExpiryDays The application will automatically expire the account password after given number of days and the user will need to change it at next login.

login.policy.MaxFailedAttempts The user account is locked after given number of consecutive failed attempts by the user.

login.policy.PasswordDisallowReuseLast When changing the password, the user cannot pick a password that has been used last given number of times.

login.policy.MFA See How MFA works below. To enable it, this must be set to 'optional'.

login.policy.MFAType When using MFA, this must be set to 'TOTP' - which is the only method supported at present.

login.policy.MFAIssuerName A Short string to describe your application. This name will be stored with the user's authenticator mobile app that will allow them to distinguish if from other keys in the app.
Login Policy and Control.

How LDAP works

The default LDAP integration used by the Wizard generated application delegates the password verification to the LDAP server and locally verifies that the user belongs to a designated Security Group. You can create multiple Security Groups in the LDAP server and designate different groups to different applications.

A local user record for the user may still need to be maintained for other settings like the role and group memberships. The locally stored password is ignored. A user login is accepted if Username/Password are accepted by the LDAP server and the user belongs to the named security group.

Operations like Change Password and Recover Password are not valid in LDAP mode. Remaining User settings and operations proceed normally as for local authentication.

If you need to use a different mechanism of authentication - LDAP or otherwise, you can use the server-side login traps and write custom login implementation.


How MFA works

The Wizard generated application supports 2FA using TOTP (Time based One Time Password). The user must have a mobile device and install "Google Authenticator" mobile app on it. A one-time pairing must be performed that installs a key on the Google Authenticator app. The pairing requires the user to scan a QR code from the application's pairing page.

Once paired, the Google Authenticator will generate a unique 6-digit that changes every 30 or 60 seconds. The generated code is valid only during the time window when it was generated. It is assumed that the person who is in possession of the device is the only one who can get this code. Validating against the TOTP gives an additional level of confidence that the user is who they say they are.

Within your Wizard config file, you can link certain entities to elevated security. The application will ask for 2FA before allowing operations on that entity. Once a 2FA is performed, you can control for how long that elevated security is valid. For instance, if you set it to 10 minutes, the 2FA previlege stays for 10 minutes, after which it must be renewed.

Multi Tenancy and Extranet Users

Multi-tenancy refers to a situation where the same application/database is used to service multiple user organizations. You may want to build an application and sell subscriptions to it to multiple customers - each using it like it is dedicated to them.

A significant implication of this is that the data must be stored in a silo where a tenant has visibility of only their own data.

Wizard applications have multi-tenancy feature in them. This allows an application developed for a single tenant (default) to be easily rehashed for multi-tenancy. The generated APIs have the silo separation baked into them, and the application does not have to worry about the data isolation.

Extranet refers to a situation where a user organization exposes certain features/pages of the application to a partner organization, usually their suppliers or customers. For instance, a supplier may be allowed to raise invoices and view the status of each invoice. Customers may be exposed to a Ticket sub-system, where they can raise complaints and see the progress.

Wizard applications have extranet capability as well.

Multi Tenancy

When generating the application-

  1. In every entity that needs to be isolated at the tenant level, add a property (field) of type tenantscope with validation set to hidden. This field will be automatically linked to the Tenant Id of the user at the time of creation and will thereafter remain fixed. The data management APIs will automatically apply Tenant Id filter, resulting in the silo.
  2. Note that the tenenantscope property needs to be there for workflow enabled entities - even if you do not have multi-tenancy in your application. This is because workflow can span across the host and tenants and workflow management needs to keep Tenant Id information.
  3. Provide a beforelogin server trap handler to resolve the Tenant Id of the user at the time of login. System will not assume the Tenant Id present in the system at the time of login, because that creates a risk of cross-login. Via this handler, you must determine the Tenant Id of the Username so login process can continue. See details under customization.

When using the application-

  1. When creating users, you need to specify a non-zero Tenant Id for a user when initiating the tenant onboarding.
  2. Typically, you will have a Tenants table (entity) for managing the Tenants. Simply use the Id value of the Tenant row from this entity as the Tenant Id of the user.
  3. When the user logs in to the application, any records they create in Tenant bound entities, will be automatically tagged to the Tenant Id of the logged in user.
  4. The host user and users associated with the host organization should be created with Tenant Id set to zero.
  5. The host users (Tenant Id - 0) can work with all database records of all Tenants.

Extranet Users

The application uses two User properties to manage Extranet users. The two together uniquely idenfity the extranet user. For local users the Scope is blank, and the Id is 0.

  1. The User Scope Type. This refers to the type of extranet user. E.g. it can be 'customer', 'supplier' etc.
  2. The User Scope Id. This refers to the unique Id of the user for that User scope type.

When generating the application-

  1. Declare the external user types under externalroles in the configuration file.
  2. When setting the access attribute of the entity, use ext: followed by the external role name to refer to the extranet user class of that type.

When using the application-

  1. When creating users, select the extranet user type.
  2. The system will automatically assign a unique User Scope Id to the user.
  3. You must follow a naming convention for the Username to make sure that all Usernames - local and external - are unique.

Integrating other Applications

The Wizard generated application is Web-API driven. This makes it very easy for an external application to get comprehensive access to its database. The APIs detailed above can be called by any external application.

Authentication

The external application must, of course, first authenticate itself before calling the APIs. Popular OAuth2 protocol is used for authenticating external applications. If you are unfamiliar with this protocol, please refer to public resources about OAuth.

  1. The authentication is performed against the Users created in the visualization app. The same userid and password are used that are used for app login.
  2. Every API call must carry an Authorization header so that the system can establish identity of the originator of the call.
  3. There are two types of authorization headers - Basic and Bearer.
  4. In Basic authorization type, the Username and Password information is coded into a single string and sent. ZOAPIIO authentication API (below) must be called using the Basic authorization.
  5. If authentication is successful, the call will return a bearer token. The bearer token must be passed as the identity proof with every other ZOAPIIO API call.

The Authentication API is-

{Instance URL}/wc/{Wizard Project Name}/vapp/authenticate

The API is called using GET or POST. Basic OAuth authorization header (with username and password) must be sent with this call. No other information needs to be passed. If successfully authenticated, the following response will be returned.


{
	"SESSIONID": "",
	"SESSIONOWNER": "",
	"INSTANCEID": "39d539e8-000001",
	"RESPONSE": {
		"Status": [
			"OK",
			"{The OAuth Token String...}"
		]
	}
}
	

The application must parse this response and extract the OAuth Token. This token should be passed as Authorization header with subsequent calls.

REST interface to your data

REST is a standard protocol for adding, updating, fetching, and deleting entity records in the target application. The actual operation is controlled by the HTTP request type POST, PUT, GET, and DELETE. At times, certain applications use REST to interact with other applications. ZOAPIIO supports REST based access to its contents.

REST must be explicitly enabled in the master configuration file for entities that require REST access. This is done by setting the "RESTaccess" property of the entity to true. Then the entity data can be accessed using REST over the following API-

{Instance URL}/wc/{Wizard Project Name}/vapp/rest/{Entity Name}

For details about REST, please refer to public resources.

Access Control

The wizard application supports access control at two levels-

  1. Access to entity pages.
  2. Access to entity data.
The two access levels are independent. The page access is implemented in the UI and the data access is implemented in the server APIs. For example, if an entity page is restricted to "read-only", the UI will disallow any updates. This does not restrict access to the entity data using API, which is separately controlled and managed.

Page Access

The page access is controlled through the user role mechanism. This allows you to configure the app such that each user type (role) sees a different UI. The access property of the entity in the wizard configuration controls this - a sample is given below. There are three access types-

  1. full: The user has full access to the page.
  2. readonly: The update operations are disabled, and the user cannot make changes to or delete the entity records.
  3. demo: This is a special access type, which works like 'full' access except that the Save operation does not actually update the database.

"access": {
	"full": ["operator", "manager"],
	"readonly": ["audit"],
	"demo": ["demo"]
}
	
  1. If 'access' specification is omitted, all users are granted full access.
  2. If 'access' specification is present, at least one role should be given full access. Otherwise, all users are granted full access.
  3. readonly, and demo access specification is only consulted if at least one user role has full access.

Data Access

Access to entity data is controlled using an access tag associated with each entity record. Each entity record has a Owner (user) and a Group Owner (usergroup). For the purpose of record level access, each user attempting to access it is considered to be of one of the following categories:

  1. Owner
  2. Owner Group Member
  3. Group Manager
  4. Other

Data inside an entity record is itself divided into two access categories.

  1. Ordinary data items
  2. Privileged data items

The record level access is controlled as follows:

  1. A unit of access is a single character, one of (-, R, W, or D), representing none, read, write and delete access. Delete access implies Write access and Write access implies Read access.
  2. For each category of user above, there is a two-character code - one for regular properties and another for privileged properties. So WR indicates write access to general properties but only read access to privileged properties.
  3. The access to a record is controlled by an eight-character code - two characters for each of the user categories above in that order. So, the access control code would look something like 'DD WR WW R-'

Classification is a simpler way of defining the access code. In the wizard configuration, you define short names for access controls that you would like to use. E.g., you could say "open" represents "WWWWWWWW" (unrestricted), and "private" represents "WW------" (fully restricted). You can have other such access classes representing other intermediate settings, depending upon your requirements to control access. At the time of creating a record, the user can pick a classification, thus setting its access level.

The "Access" column in the entity table stores the access code and the "Classification" column stores the codified access tag. Usually, only one of them is present, but if Access is present, it overrides the Classification value.

The visualization app allows you to create users, assign roles, create user groups and add users/managers to them. The app also allows you to edit the access setting for each entity record.

Managing Attachments

Attachments and file data (documents, images etc.) are an important part of any application. has elaborate support for storing and managing file data. ZOAPIIO also supports full text indexing of the document contents. This is a very powerful feature that allows you to search for entities based on the contents of the attached documents.

ZOAPIIO also has a built-in OCR and PDF to text conversion tools. These can be accessed through custom code. Please refer to WebIDE programming guide for details.

File data is defined in the Wizard configuration as a regular entity property to be saved in a multitude of configurations.

  1. In database as a column (optionally compressed)
  2. In a local file on disk
  3. In an AWS S3 bucket

How the attachments are handled and saved is controlled through the name, type, and validation attributes of the entity property.

  1. The type attribute must be set to 'file'.
  2. The file type is a complex property and requires multiple names (comma separated).
  3. Minimum two (maximum three) names should be there - one for the file name and the other for the file data.
  4. If the name attribute contains two names, the file data is stored in the table itself.
  5. If there is a third name, then that is assumed to be a pointer (see format below) to an external storage where the data will be stored. Presently, local file and AWS S3 are supported - in future, support for other destinations may be added.
  6. When using external storage, Wizard app uses a default mechanism for storing the data as files on disk. The default mechanism can be overridden to change the folder structure or to store it on AWS S3. When overriding the default mechanism, you must use the 'beforesave' handler mechanism of customization and provide a custom target pointer.
  7. In validation (comma separated list), using 'image' implies that the attachment is an image.
  8. In validation, flagging 'document' implies that the attachment is a document. (PDF, DOCX, HTML or TXT). See 'Special Handling' for documents below.

'file' Location Pointer

The structure of the file location pointer is-

{Storage ID}:{File Path}

{Storage ID} can be 'local' or the name of a storage target configured for your ZOAPIIO instance. Using a storage location other than local, requires pre-configuration on the server. If you plan to use custom storage destinations in your app, please contact your administrator to create an AWS S3 (or any other supported type) on the server. Access to external targets requires credential configuration, which is a system administrator task.

'local' implies local file storage and does not require any pre-configuration on the server.

{File Path} looks like a regular file path - it must start with a '/', although it will be created under a folder on your Instance. It can contain names with characters that are acceptable as filename characters on Linux OS. It can contain '/' (slash) as path separators and finally you can include a placeholder string '${Id}' which is always substituted with the Id of the entity record.

When using external storage, no action is needed from your end if you want the files to be stored on disk as default folder structure. The default mechanism is sufficient for most cases. However, if you want to override it, you can do so through the 'beforesave' event handler for the entity.

'document' Special Handling

If a file type entity property is flagged as document (via validation attribute), the wizard automatically extracts the raw text from the document (PDF, HTML, TXT, or DOCX) and adds it as an additional column in the table. This new column name is created by appending 'Text' to the first name in the names list.

The raw text is also fully indexed, allowing you to search for entities using words appearing inside the documents. The text in this 'Text' column can be retrieved and used using the APIs like a normal entity property.

Filtering & Cascading

The visualization app will allow you to create, query and edit data. The form is automatically generated from the configuration you have provided. The form content and behavior can be tailored to some extent. For example, you can have conditional properties - let us say there is a selection field with 'Others' as an option. If this is selected, you may want to capture additional details. This can be implemented using the 'condition' attribute (See Reference Guide in the end).

Another situation that occurs sometimes is when a group of fields are related to each other and the selection cascades through them. For instance, in our example - for an Asset Category, there are Asset types, and for each Asset type, there are Asset Sub-types.

When entering/searching Asset details, Category, then Asset Type, and then Asset Sub-type will cascade. Meaning, when the Category is picked, the list of Asset Types will be shortened, and when the Asset Type is picked, the sub-type list is shortened.

This cascading can be achieved using the 'filter' attribute in entity property definition.

  1. The value of the filter can be a property name (like Category in our example) or an entity name (like assettype in our example).
  2. When using filtering on a selection field, use 'select:..' data type instead of 'easyselect:..' type.
  3. The name of the property should be the same in both entities participating in cascading.
  4. See the example at the top of this guide.

Workflow

entity records are workflow enabled. You need to declare the workflow definition with the entity. See Reference Guide at the end for details.

Widgets and Dashboards

Data visualization (or dashboards) is a primary use case of the applications. It allows you to combine inputs from multiple sources (Web-APIs) and visualize them in a variety of widgets. The dashboards are created by the users of the application, but you must define the type and nature of the widgets that your application will have.

Pre-defined Widgets

Some pre-defined widgets are available, which you can use to create a customized homepage for your users (See Reference).

  1. #widget-workflow: Displays the workflow assignments of the user.
  2. #widget-personalize: Displays controls to personalize the app.
  3. #widget-profile: Displays the user's profile picture and allows editing of the profile.
  4. #widget-shortcuts: Displays all accessible entity types in a grid with a link to the entity's listing page.
  5. #widget-messages: Displays user's recent messages.

See Reference Guide for details.

Customization

Invariably, you would want the wizard generated application to have application specific business processing incorporated. You can provide custom code to modify the server as well as front-end application functionality.

Customization is meant for advanced users and requires knowledge of programming - primarily JavaScript. If you are not an experienced programmer yourself, you can perhaps approach a colleague to help you or get help from one of the several freelancing public services.

Each of the customization modules below require you to provide the program in a separate file from your local desktop. When you initiate the application generation, you will be prompted for all the files referenced here.

Backend

ZOAPIIO Wizard allows you to specify service-side hooks that can be called at the time of database operations on the entities. The Server-side code is developed using the ZOAPIIO WebIDE itself, and cannot be written using a standard editor.

When you add custom code to your project after it is generated, the changes will be erased at the time of the next generate. The Wizard provides a mechanism to deal with this situation - it lets you save the custom changes separately from the rest of the project and lets you incorporate it back with every generate.

  1. In your Wizard source file, declare a handler under "handlers" in the customization section. Give it a name and assign a file which has .webc extension. This file will be used to supply customization from outside.
  2. Identify the server-side hooks that you require and declare them against the entities as necessary.
  3. Proceed to create your project using the Wizard normally.
  4. After you click Continue below, on the next screen, Wizard will ask you for this handler file. Leave it blank at the time of first generation.
  5. After generating the project, proceed to add Web-APIs, Routines, methods, Data declarations, Database Queries, even Web applications to your project.
  6. Remember to include the Routines and Methods to handle the hooks declared in the wizard source.
  7. Then invoke the "File -> Save Customization" command from the menu - it will save only the customization that you have added, to a local file.
  8. Copy that file to the customization file that we named in the first step.
  9. Proceed with "Deploy" to deploy your project to the server.
  10. When you need to run the Wizard again, simply provide this file so that changes are automatically inserted into your project.
  11. The process described above can be repeated to build your application incrementally. That is-
    • Generate the application and add existing customization snapshot (Possibly nil, the first time).
    • Continue customizing and making more changes.
    • Save the current state of customization in a local file and input it back in the next iteration.

Front-end.

The front-end customization in the Wizard generated project is used to add-

  1. Custom controls. Wizard supports a rich set of entity property types and you can add your own controls using this feature. Sometimes you need complex controls for editing multiple properties in a single control. That can also be achieved using this.
  2. Report renderer. The Wizard has report type entities, but the processing and rendering of the report must be provided by you. If you are using reports, you must provide this custom module.
  3. Child data editor. The Wizard creates a table-based editor for the child entities. However, sometimes the data in the child rows need to follow application specific constraints and require a custom editor which can edit the entire set in a custom UI. This feature can be used to create your own editor.
  4. Custom list renderer. The Wizard creates a table-based editor for the entity data. This implementation is very powerful and meets most needs. Wizard allows you to create a custom renderer for the data listing for the root (not-child) entities as well.
  5. Entity Actions. Custom actions can be added to each entity to implement business specific operations on the data records.
  6. Workflow Renderer. The workflow definition in your Wizard source file is rendered by the application using a generic UI, which is self-contained and sufficient for most cases. In case you require a customized control to render the Workflow, you can provide your own.
  7. Dynamic properties. ZOAPIIO Wizard expects that for each entity you will provide the properties and their details in the source itself. However, certain situations require that some properties and their details be determined only at run-time. The ZOAPIIO Wizard allows you to dynamically inject properties at run-time to an entity through customization.
  8. Dynamic dashboard widget. ZOAPIIO Wizard supports a wide range of Widgets for creating dashboards. You can create custom widgets using this feature and use them in your dashboards.
  9. Pages to the UI. You can add entirely custom pages to the UI as well.

You can club all your front-end customization in a single file, or keep it separated in multiple files. Declare each file under the customization section of the source JSON file. At the time of generation, you will be prompted for each file.

At the end of this guide, you will find the skeletal templates for each type of customizer. You can use these as starting points for your front-end code.

Incremental FE changes.

When you are building your application, typically you will be changing all parts (front-end and backend) in no specific order - based entirely on convenience. Whenever you make a change to the backend customization, you must regenerate the entire project because your changes are baked into the generated project.

The same is not true when making only the front-end changes. You do not need to regenerate the project and you can simply push the front-end JS files as an incremental change. To achieve this, select "Update Artifacts Only" to Yes above and click continue. You will be taken to the script load page, where you can choose to update all or some of the front-end scripts.

After updating the artifacts, you can stay on that page and click "Run Again" to push more changes without launching the Wizard from the beginning. When you are working on the front-end code, you need to make repeated deployments in a short time. This helps you achieve the deployment process efficiently and effectively.

Templates.

Custom Control Template    👓

Report Renderer Template    👓

Child data Editor Template    👓

List Renderer Template    👓

Action Handler Template    👓

Workflow Renderer Template    👓

Dynamic Properties Template    👓

Dynamic Workflow Template    👓

Custom Dashboard Widget Template    👓

Custom Page Template    👓

Help File Template    👓

Interacting with UI/Server

Your JavaScript program can interact with the page (and server) by calling the following methods on the 'form' object, which is received in the constructor.

Function 'form' method Description
Get the value of a field on the page getProperty(fieldname) Should also be called to get the initial value of the fields being rendered.

Execute an ajax query getProperty([url, reqobj], 'query', [this.handler, this, parameter]) The query URL can be one of the Wizard generated APIs, or a custom query provided by you.

  • url - the URL,
  • reqobj - the object which is passed as POST data,
  • 'query' - fixed string to be passed literally,
  • this.handler - a method you should implement in the class to receive the result,
  • this - Current object reference, should always be this,
  • parameter - a parameter to pass to the handler. The handler will receive two parameters - the retrieved data and the parameter provided here.

Get the list of entities from UI cache getProperty(entityname, 'entity')
  • entityname - the name of the entity,
  • 'entity' - fixed string to be passed literally.

Get current user info getProperty(null, 'login')
  • null - ignored,
  • 'login' - fixed string to be passed literally.

Get page environment getProperty(envname, 'env')
  • envname - the name of the environment setting (vappprefix, voptsrq, vdatarq, instanceid, projectid)
  • 'env' - fixed string to be passed literally.

Render a report as an interactive table jQuery plug-in DataTable When rendering a report as a table, you can use DataTable jQuery plug-in. It is loaded in the UI page. See documentation at datatables.net

Interact with UI page.

App Properties

server provides you abstraction for Application properties. These properties are stored on the server in a database table and your programs can define and access them. You can update the Application property table directly using the DB Assistant tool. However, the Wizard allows you to populate these properties conveniently at generation time. The properties you define here are accessible to your custom server APIs as Properties abstraction. See the Web-API documentation for details.

Property Description
name A Unique name for the property.

value The value of the property.
Application properties.

Screen Shots

Screen Shots

Reference Guide

Summary

Legend: Array value
Object Description
application The complete description of your application.
themes The visualization app has two pre-defined themes (dark/light). You should define here the themes you want to be available in your app. At least one theme must be defined. If you define both themes, then the one defined first is the default.
homepage You can control what page is displayed for a user, upon a successful login. The homepage can be a dashboard or an entity listing page.
widgets If your homepage is a dashboard, then this contains its dashboard layout.
uidefaults Default settings for the UI. The settings can be overridden by each user to personalize the experience.
entities An entity refers to a database table and necessary UI pages to manage it. Define all the entities here.
access This defines entity page level access control in your app.
classification Classification is used in entity data level access control. It defines codified access tags for entity records.
handler There are server-side events associated with entity record lifecycle. This allows you to configure custom handlers for these events.
properties The properties (or fields or columns) of the entity table. Note that the Wizard adds some properties of its own.
children If the entity has any child entities, define them here.
properties The properties of the child entity.
dependents Define entities which are related to this entity through a foreign key relationship.
list The 'list' is used to specify an alternate listing style for the entity listing page. The default listing is a table, but you may override it here to render it as a grid. Presently only grid is supported as an alternative.
commands You can configure business operations on the entity records, for which you provide a custom processing logic. This is used to transform the entity record in a business specific way. The commands will be shown on the edit page from where the operations can be invoked.
workflow If the entity has a workflow associated with it, define it here.
steps The steps in the workflow.
customization Grouping of all customization elements (front-end and back-end) for your app.
scripts The JavaScript files that contain external custom code.
helpfiles The single-page HTML help files for the app.
handlers The ZOAPIIO program files that contain external server-side custom code. Multiple handlers can be put in the same file.
custompages You can add fully custom pages to the app. Define them here.
properties ZOAPIIO apps have an application property-set concept. It is stored on the server and can be accessed by server-side code. If you want to create some application properties at generation stage, you can put them here.
widgets Widgets are building blocks of a dashboard. ZOAPIIO allows users to create and save custom dashboard layouts by combining these widgets. The widgets, their definition and behaviour should be provided here.
selection The selection parameters in the widget.
output Definition of the widget output.
query A database query that will fetch the data for the widget.
result Definition of the widget result.
options Options for the widget result.
dashboards Normally, the users will create their own dashboards from the visualization app. However, you can add some dashboards to the generated app which are accessible to all users in the basic app itself.
widgets Widgets that are a part of the dashboard.
The Wizard config structure.

application

Property Description
name Unique name for the application. The name is important, it will be prefixed to the table names and embedded inside API names to ensure that the different applications you are building do not interfere with each other. Pick a short name, about 5-6 characters, starting with a letter and without spaces or special characters.

title A short phrase describing the application.

logintip A tip to display on login page - useful if you are using customized login login authentication like SSO or custom LDAP.

favicon A 32x32 image (PNG) in data:URL format to serve as the favicon for the application. The data url allows image specification as string. An example is "data:data:image/svg+xml;base64,{Base64 encoded image data}".

logo A small image in data:URL format to serve as the logo for the application. Format and encoding is same as in favicon.

userroles An array of strings enumerating the roles used in the application.

externalroles An array of string enumerating the external roles used in the application. Each external role refers to an extranet user type.

defaultrole Your application will use User Roles to control access to pages and entities. When you launch your application for the first time, it allows you to add the first user from the home page. This is the role that will be assigned to the application boot user. Provide the name of the most powerful role in your application here because you will use this to create other users.
'application' properties.

themes

Property Description
name The name is fixed and you should choose one from the list - default (dark), or theme-a (alternate light theme).

type The UI engine needs to know if this theme is dark or light. Choose the one applicable to the selected name. Choosing a different value may result in sub-optimal rendering of the UI.

title A short title of your choice - this is displayed in the UI.
'themes' properties.

homepage

Property Description
type The options are - dashboard, or entity, depending on what you want to be shown as the homepage. If entity is selected, the entity name should be provided in entity property. Otherwise the dashboard layout should be provided under widgets.

condition You can have multiple homepage declarations (it is an array). This allows you to have different homepages for different user types. The condition for each homepage specification is evaluated and the first one to pass is selected. The value here should be a JavaScript expression evaluating to a boolean. Properties from the user entity can be referenced with '@' prefixing. E.g. @Userrole=='admin', would pass if the logged in user has 'admin' role.

entity The name of the entity which should be listed on the homepage. This is applicable only if the type is set to 'entity'.
'homepage' properties.

widgets (homepage)

Property Description
name The name of the widget - a custom or a predefined widget name can be used.

x The horizontal placement (zero based) of widget on a 9x9 grid.

y The vertical placement (zero based) of widget on a 9x9 grid.

h The height (as number of cells) for widget on a 9x9 grid.

w The width (as number of cells) for widget on a 9x9 grid.

refresh The refresh interval for the widget in seconds.
'widgets' properties.

uidefaults

Property Description
theme The name of the these (See themes).

menuposition 'top' or 'left' - option for default menu position.
'uidefaults' properties.

entities

Property Description
name The name of the entity. This will be used as the database table name (with application name as the prefix). Have a short, yet descriptive name, without any special characters or spaces. It is advised to use all lowercase name for consistency.

label A short phrase describing the entity. This name will appear in the UI, so make it meaningful and keep it short.

menu The application UI will group entities under menus. Provide the name of the menu under which the entity should appear. Keep the menu name short and meaningful. Same menu name can be assigned to more than one entity - they will all appear under that menu.

icon An icon can be specified with the entity. Font Awesome icon set version 5.15 (free) is used for the icons. Enter the name of the icon from the set (E.g. anchor).

menuicon This icon is displayed against the Menu entry where the entity is grouped. The menuicon needs to be specified with only one (any) entity under a menu group. The specification is same as that for the icon attribute (FA 5.15 icon set).

data This property is important, and it tells the wizard what type of data will be stored in the entity. When creating the database table, some columns are automatically added. This property will have an effect on the columns that will be added. This property can be blank.
  1. person: The entity represents a person.
  2. thing: The entity represents a thing or a place.
  3. user: The entity represents the application(b2b) user - users within the organization who are added to the system manually. Only one entity can have this data setting.
  4. usergroup: The entity represents the application user groups (teams). Only one entity can have this data setting.
  5. b2cuser: The entity represents a subscriber(b2c) user - subscribers can register/subscribe through the web/mobile app and add themselves to the system (See ZOAPIIO app development guide). Only one entity can have this data setting.
  6. setting: The entity will be used to store the application settings. A setting entity is special because only one entity record can be created. Only one entity can have this data setting.
  7. noname: The entity represents something which has no name. I.e. the name column(s) will not be added. Because the Name column is automatically added in most cases, this is useful to explicitly inhibit that.
  8. transaction: The entity represents data which is created and maintained manually (custom APIs). It is not shown on the UI and no extra columns are added.
  9. report: The entity represents an on-line report. No database table is generated, and you should provide a customization module to process and display the report.

bootuser This property is only applicable for the entity with data=user. You can provide here the details of a user that would be automatically created at the time of first generate of the application. You can use this to login to the application and create other users. The boot user gets the same role that is defined as the default role for the application. The format is-
"Username=XXX,Password=XXX,Name=XXX"

tip This can be a larger description of the entity extending up to a sentence. It is shown to the user as mouseover tip.

editor This is required if the 'data' value is set to 'report'. It is the name of a JavaScript class, that will be invoked to process and render the report. The JavaScript code should be separately provided - see Customization.

indexes An array of strings - this allows you to create indexes on the table that will be created during generation. Each string in the array represents a different index and each index is a comma separated list of property (column) names in the index. E.g. "Name, Age".

RESTaccess A true/false value to tell the wizard if REST access API should be generated for this entity.

openaccess A true/false value to tell the wizard if the data in the table is for public consumption. If set to 'true', the getdata for the entity can be invoked without performing a login.

fetchsummary This is a true/false value. Usually, when the visualization app loads, it fetches a summary (Id, Name, etc.) of all entity records. This is done for optimization but can become a burden if the number of records is large. If you expect the number of records in the entity to be larger than, say 2000, set this to false.

fetchlimit When the visualization app lists the records of an entity, it fetches a maximum of 100 records. If you want this to be set to a higher value, you can do so here.

fetchorder Useful when using fetchlimit, it specified the ordering of fetching records. This must be a property name optionally followed by "Desc" to indicate reverse ordering.
'entities' properties.

access

'access' attribute controls page level access to the entity. It has no effect on data access. Access Control for details.

Property Description
name The name is fixed and you should choose one from the list.

full A list of strings - userroles with full access page.

readonly A list of strings - userroles with read only access page.

demo A list of strings - userroles with demo access page.
'access' properties.

classification

Classification allows you to define readable names that map to an access code, which itself control entity data access. The user it not restricted to using these names, they can specify their own access control code. This is for convenience only. Depending upon the sensitive attached to the data in the entity, you may define classifications like "open", "restricted", "private" etc. and the user can simply pick one value instead of entering detailed access rights for each placeholder in the code. Access Control for details.

For example { "open": "WWWWWWRR", "private": "WWR-R-R-" }.

handler

The wizard would generate APIs to access the entity data. Handlers allow you to specify custom processing for major access control points. The handler program stubs are in programming language. The program stubs should be provided as an external file - see Customization.

The values for the handlers are provided as "METHOD.<name>" or "ROUTINE.<name>", naming the method or routine to execute at that control point.

Property Description
beforesave Custom program stub (server) to execute before saving the record. This stub can reject the save operation or update the data in the record. The database table Node is passed as the first parameter to the routine/method. To reject the save operation set node /VARIABLES/ACCESS/CANWRITE to "NOK".

aftersave Custom program stub (server) to execute just before saving the record. In this stub, you have the opportunity to modify the record after all system processing has been performed. You cannot reject the save operation from this handler.

afterdbsave Custom program stub (server) to execute after saving the record. In this stub, you cannot reject the save operation and changes to the record have no effect. Typically used to make other dependent changes, sending user notifications, or making API calls to other applications notifying the change.

beforesearch Custom program stub (server) to execute before search on the entity. You can modify the SQL command right before the search is fired. The Database table node is passed as the first parameter. The program can access _WHERE field from the Table node and alter it, if required.

beforelist Custom program stub (server) to execute before listing a record. You can reject access to the record and modify record properties. The database Node is passed as the first parameter. To reject list access to the record, set /VARIABLES/ACCESS/CANREAD to "NOK".

beforeregister Custom program stub (server) to execute when a new Subscriber (b2cuser) is trying to register. You can carry out additional validations and reject the effort. If the registration is to be rejected, assign the error message to the Data Path "/Response/Status[2]".

afterregister Custom program stub (server) to execute after a new Subscriber (b2cuser) has registered register. In the handler, you can update any dependent values, trigger workflow or issue alerts.

beforeworkflowsave Custom program stub (server) to execute when a workflow record is being saved. The workflow record (See workflow definition in the generated app), is passed as the parameter to the METHOD/ROUTINE set here.

prelogin

Custom program stub (server) that must be provided if your application uses Multi Tenancy. To prevent conflict in user names between tenants, there needs to be a naming convention for Usernames. Typically, you can have a Tenant code as prefix to the Username, thus making names unique across the system.

The handler receives the Login record as input parameter. You must examine the Username field and using whatever codification you have decided to use, resolve the Tenant Id of the user. You must update the Username (replacing the original value) and TenantId fields in the input.

If the Tenant Id resolution fails, you must set it to -1. This flags that the Username is invalid.


beforelogin

Custom program stub (server) that you must provide to implement alternate login mechanisms such as Single Sign-On (SSO) in your custom environment.

If you implement this handler, you must update the LoginType field in the output parameter to the handler. Setting it to fail causes the login to fail. Leaving it blank will cause the default login procedure to continue.

And setting it to user will confirm that the login has succeeded. You must also update the Userrole property in the output parameter, if you claim successful login. You can also set other User record values for the user.

'handler' properties.

properties

A property declared for an entity can be a simple property or a complex property, determined by its type. This means that the property may result in the generation of one or more columns in the table. Each property on the UI is rendered on a single row. For instance, type 'country' is a complex property and results in two table columns - Country code, and Country name.

Where a complex property is involved, you must provide more than one name for property - the number of names must match the count that the property type expects. The names are input as comma separated values - E.g. CountryCode,CountryName. Furthermore, you can create a complex property (multiple columns but rendered on the same UI row), by using the type 'multiple'. This is used to group related properties to simplify the look of the UI page. It is advised to have not more than three properties in a group for clean rendering.

Property Description
name Unique name(s) for the property. Multiple names, if applicable, are input as comma separated names without any spaces. Each name must not contain spaces or special characters - the only characters permitted are letters (both upper and lowercase), digits and underscore (_). The name cannot start with an underscore.

privileged A boolean value to indicate if this is a privileged property. Privileged properties have additional access controls. This allows you to take properties that contain sensitive information to be applied additional protection. See Classification above.

label A short user readable name for the property. There will be only one label, even if there are multiple names. Also see labels below.

type The type of the property.
  1. string : Simple string field.
  2. text : Simple text field.
  3. ftext : Simple formatted text (HTML) field.
  4. password : Simple password field.
  5. integer : Simple integer field.
  6. decimal : Simple decimal field.
  7. amount : Simple amount field (decimal with currency).
  8. time : Simple time field.
  9. date : Simple date field.
  10. datetime : Simple date with time field.
  11. dow : Simple date of week field.
  12. timezone : Simple timezone field.
  13. hashtag : Simple string field with special processing attached to it. Hashtag fields are enabled for searching as tags. The input valuee is space separated list of tags.
  14. country : Complex (Code,Name) field representing Country.
  15. timerange : Complex (From,To) field for time range.
  16. decimalrange : Complex (From,To) field for decimal range.
  17. amountrange : Complex (From,To) field for amount range.
  18. geolocation : Complex (Latitude,Longitude) for geo location.
  19. daterange : Complex (From,To) field for date range.
  20. temperature : Complex (Value,Unit) field for temperature.
  21. file : Complex (FileName,FileData) for documents.
  22. thing : Complex (Name,Picture) for an object.
  23. person : Complex (Title,FirstName,LastName,Picture) for a person.
  24. multiple : Complex property with arbitrary number of values.
  25. select: : String field with fixed list of options.
  26. easyselect: : Same as select: but rendered as checkboxes.
  27. combo: : String field with combo (select but ability to enter any value) input.
  28. entity: : Complex (Id,Name) reference to another entity record.
  29. collection: : Simple (JSON) field to capture a collection of other entity records.
  30. custom: : A user defined field. It can have any number of names, the custom control should handle all fields collectively. See Customization
Property types that end with a colon ':', require a subtype to be entered - see below.

subtype A subtype must be entered when the type name ends with a colon ':', as follows
  1. select: : Comma separate list of select options.
  2. easyselect: : Same as select: above.
  3. combo: : Same as select: above.
  4. entity: : Name of the entity, to be used in reference.
  5. collection: : Name of the entity, to be used for collection.
  6. custom: : The name of the JavaScript class to render the property on UI. The custom JavaScript should be provided as a separate file. See Customization

validation The input validations applicable for the property. Validations implicit from the property type are automatically applied. Additional multiple validations can be specified as comma separated list.
  1. required : A value must be provided for the property.
  2. hidden : A value is not displayed and is not editable.
  3. simple : A input must be a simple string (Only letters, digits, and @._- are permitted).
  4. email : A input must be a valid email address.
  5. phone : A input must be a valid phone number.
  6. url : A input must be a valid URL.
  7. match : For type 'password' property, the input values must match (when setting a password).
  8. single : For type 'dow' fields, only a single day of week can be selected.
  9. readonly : The value cannot changed.
  10. addonly : The value can only be added (I.e. changed when it is blank).
  11. document : For type 'file' property, the attachment should be a document (pdf, html, docx, or text). There are additional implications when this validator is used - see the section on Managing Attachments
  12. compressed : For type 'file' property, the data should be stored with compression.
  13. encrypted : For type 'string' property, the data should be stored with encryption.
  14. image : For type 'file' property, the attachment should be an image (png, gif, or jpg).
  15. username : The input value should be unique (should not already exist as a username).
  16. regex[...] : The input value should match the given pattern.

default The default value for the property. For date fields, shortcut 'today' and for time fields, shortcut 'now' can be used.

searchdefault The default value for the property when invoking the search. The default listing produced on the 'List' command will use this default. You can, of course, change it on the search page and search again.

condition This is used to conditionally display a property on the page. If specified, it should be a JavaScript expression evaluating to a boolean value. It can reference other properties on the page by prefixing it with an @. E.g., "@Status=='incomplete' would show this property only if the Status property on the same form is equal to 'incomplete'.

filter The filter attribute is used to implement cascaded fields. See Filtering and Cascading

types The comma separated names of simple types of each field in the property. Applicable where the type is 'multiple' or 'custom:'

labels A comma separate list of field labels, if the type is 'multiple'. For 'multiple' type fields, the label should be for the property as a whole and labels should be short values for each field in the property

cached This is a boolean true/false value that defaults to false. The Visualization app upon loading, fetches selected properties of all entities (unless explicitly disabled using - 'fetchsummary' property). This allows the UI to have quick access to key details - the local cache is updated when an entity record is saved.

The contents of the cache can be accessed programmatically in the client side custom code. Setting the 'cached' attribute to true would cause this entity property to be fetched with initial load.
Entity 'properties' properties.

children

Property Description
name The name of the child entity. Same as for 'entities'

label Same as for 'entities'.

editor The visualization app provides a simple Add/Edit/Delete interface to manage data in the child entity. However, your application may require that all records in the child entity be edited together through a custom UI. If this is the case, provide the name of a JavaScript class, which will be invoked to edit the child entity records. The JavaScript code should be separately provided - see Child data editor.

The value of this attribute can be optionally followed by a window size specification for the editor window, in the format - {ClassName}:{width in px}x{height in px}

fetchlimit Same as for 'entities'.
'children' properties.

properties

The specification for properties for child entities is same as that of root entities, with some constraints.

  1. Data type 'multiple' cannot be used.
  2. Property 'data' is not applicable.
  3. Property 'condition' has no effect. I.e. conditional rendering is not provided.

dependents

Another relationship between entities that can be represented in the Wizard is one-to-many relationship. This is different from parent-child because a child entity has only one parent. To represent a dependent entity, define that entity normally and then add a record here.

Property Description
name The name of the related entity.

property This is the property (field) in the related entity that links to the Id field of this entity and establishes the relationship.


label A meaningful label for the relationship, or leave blank if you do not believe in putting labels on relationships.


workflow Value true/false - if true, it tells the app to display the workflow information, when the listing the dependent entity records.

'dependent' properties.

list

Property Description
layout If you are using alternate listing style for an entity, use this option. Presently only 'grid' is supported.

columns The number of columns in the grid. Provide an integer value here.

editor Here you should provide the name of a JavaScript class, which you will inject into the application through front-end customization. The JS class will render the record as the cards for the grid. This is optional - if omitted, the system provided a default minimal rendering of the card.

workflow Value true/false - if true, it tells the app to display the workflow information, when the listing the dependent entity records.
'list' properties.

commands

Commands are used to add business specific operations on entity records.

Property Description
name The name of the command.

label This is will be displayed to the user as label for the command.

icon You can pick an icon for the command. (Fontawesome font pack v5).

editor A JavaScript class to execute the command. This class should be injected into the application through customization.

properties The commands can have user selectable options as parameters to the command. Specify them as properties here. Use the same structure as properties for the entity itself.
'commands' properties.

workflow

Workflow is a list of steps that should be performed on the records of an entity.

steps

Property Description
name The name of the workflow step.

attributes A list of attributes that define the nature of the step. The following attributes can be used in the list. For teamspec and userspec, the JavaScript expression can refer to user and team properties by prefixing it with '@'. E.g. "@UserRole=='somerole'".
  • approval: This is an approval task.
  • ticket: This is a ticket (help desk) task.
  • task: This is a regular task.
  • all: This task can be performed by anyone.
  • supervisor: This task can be performed by a supervisor (assigned as manager).
  • team[teamspec]: This task can be performed by a member of a team meeting the spec - the spec is a JavaScript expression evaluating to a boolean value.
  • user[userspec]: This task can be performed by a user meeting the user spec - the spec is a JavaScript expression evaluating to a boolean value.

prereq A list of workflow steps which are prerequisites for this step.
'steps' properties.

scripts

Property Description
name The name of the script - give a short meaningful name.

title Give a short title for the script.

file When running the wizard, you must select a file using the Explorer - the wizard, by itself, cannot read your files. Provide here the name of the file (.js extension), which you will supply. The file name acts as a validation so that you do not accidently load a different file.
'scripts' properties.

helpfiles

Property Description
name The name of the helpfile - give a short meaningful name.

title Give a short title for the helpfile.

file Same as in 'scripts' above, except the file extension should be .html.
'helpfiles' properties.

handlers

Property Description
name The name of the handler - give a short meaningful name.

title Give a short title for the handler.

file Same as in 'scripts' above, except the file extension should be .webc.
'handlers' properties.

custompages

Property Description
name The name of the custompage - give a short meaningful name.

title Give a short title for the custompage.

file Same as in 'scripts' above, except the file extension should be .js.
'custompages' properties.

properties

Property Description
name The name of the application property.

value The value of the application property.
Application 'properties' properties.

widgets

Property Description
name The name of the widget.

label A short label for the widget

type See widget types below.
Widget basic properties.

visualization app supports a rich collection of widgets compiled from an analysis of several real-life dashboards. The widgets can be textual or graphical, or sometimes even a combination. The following widget types are supported-

Widget Types

Type Seq Description
PieChart 1 Pie charts are quite common and require no description. pie charts support sub-groups of items, effectively letting you represent what would normally take multiple Pie Charts. Suppose you want to represent the break-up of employees by designation and gender in the same pie-chart, you can create two groups "designation" and "gender" and then plot it in the same chart.
2 The pie chart expects a single row of data with the following fields in the input - group, name, value (See Result below).

LineChart 1 Line Charts are also very common and it will plot each item in the data row on the line chart. The result specification should have exactly one value of type 'string' and one or more 'number' values.

Status 1 Status charts are used to represent the status of an object or activity. The status chart can optionally have a progress indicator or a history.
2 The status chart expects a single row of data with the following fields in the output (See result) - reading (the value), units, statuscolor (E.g. red, green, etc..), label1, label2 (up to two labels describing the context of the status), progress (a number between 0 and 100), history (a comma separated values indicating last few values for this indicator for easy visualization by the user).

TimeSince 1 Time since charts are used to represent the time elapsed since an event - normally used for depicting events that you do not want to occur, E.g. accidents.
2 This chart expects a single row of data with the following fields in the output (See result) - days, hours, minutes, seconds.

Gauge 1 Gauge charts are used to represent one or more values on a odometer type gauge - such as CPU utilization on a server or production efficiency etc.
2 The Gauge chart expects one or more rows (but given the rendering limitation, usually very few) of data with the following fields in the output (See result) - name, value.
3 The Gauge chart can also be passed the range option in the widget data consisting of 5 numbers in ascending order indicating the values range to be shown on the meter. E.g., a setting of "0,40,60,80,100" would mean: 0-40 is white color, 40-60 is green (OK), 60-80 is yellow (Warn) and 80-100 is red (Error).

TextLog 1 TextLog charts are used to represent a log of events or notifications.
2 The Gauge chart expects one or more rows of data with the following fields in the output (See result) - ts (timestamp), msg (the notification).

Pivot 1 Pivot (pivot table) is the most versatile chart and can be used to depict data in a variety of formats.
Widget types in Visualization app.

selection

Selection allows you to attach filters to widgets, allowing the user to control the widget in the dashboard.

Property Description
name The short name of the selection. The value selected for this filter will be accessible in your Widget query (DB SQL) by this name.

label A short label for the selection

values Comma separated list of values, if this is drop-down. Special values #period can be used to specify a period selection (last week, last month etc.). Special value #longperiod can be used for selection over a long period (last quarter, last year etc.)

type dynamic or fixed. A fixed selection is set at the time of creating the widget in the dashboard and cannot be changed when viewing the dashboard. A dynamic selection can be changed when viewing the dashboard.

render select/multiselect/dropdown/checkbox/radio/date - how to render the selection control.

multiple true/false - whether multiple values can be selected.

default The default value for the selection.
Selection properties.

query

The data for the widget is sourced from a database query (SQL), which you must provide. Database programming skills are required. If you are not skilled in writing SQL queries, take the help of a colleague.

  1. You can reference the entities as tables in the query by prefixing them with '@'. E.g., "Select column from @myentity"" implies selection from the table corresponding to the "myentity". The SQL can reference multiple entities using SQL JOIN them in the same way.
  2. You can reference the values of the widget selection filters by prefixing the name with '#'. E.g., #PROP refers to the value of selection with name PROP.
  3. When using the filter selection values do not use the comparison operator - it is automatically added depending upon the type of the selection value. So instead of "Where ColX = #PROP", say "Where ColX #PROP". The Wizard with use either '=' or 'In' SQL operator depending upon whether the value is a string or a list.

result

Here you describe the data contained in the query output.

Property Description
name The name of the field.

label A short label for the field

type string or number - this is important. See Widget Types above.

data The data value. It can be a fixed string or '@' followed by the name of a column output from the query.
Result properties.

options

Additional options can be passed to control the rendering out the widget.

Property Description
name The name of the option.

value The value of the option.
Option properties.

dashboards

This defines canned dashboards in the application - this is an advanced subject and you can come back to it after you have tried creating some dashboards from the visualization app UI.

Property Description
name The name of the dashboard.

owner The Username from the app users designated as the owner of the dashboard. This value can be populated after the users have been added to the visualization app. To update the config after the application had been generated and deployed - use the "Update Artifacts" option. See Regenerating the Application.

This attribute and 'ownergroup' and 'access' below together determine who all can see the dashboard.


ownergroup Same as the owner, except for the Owner group.

classification Not applicable - leave it blank.

access An eight character access control code for the dashboard. See Access Control

description A brief description.
'dashboards' properties.

widgets

The widgets in the canned dashboard.

Property Description
name This must be the name of the widget as given in the Widget definition.

title A short title.

x This attribute and the y, w, h below together determine the location and space occupied by the widget. The dashboard area is divided into a 6x6 grid. x,y must be numbers between 0 and 5 representing the x,y coordinates of the starting block. w,h must be numbers between 1 and 6 representing the width and height in number of blocks respectively.

y See x above.

w See x above.

h See x above.
'widgets' (under dashboards) properties.

Screen shots

The wizard designer

The Application Wizard view.

The generated application

Sample Screen from the generated app.