Compliance example

Since Nodelab is based on the Edgescript® programming language, Nodelab can handle fairly complex and complicated tasks.

For instance, compliance standards can be massive. The PCI DSS compliance standard contains more than 200 requirements, each with a number of controls, looking into logs and snapshot files. The status for many of these controls must be evaluated for every configurable item in the environment.
This means that tens of thousands of status points must be calculated every time you perform an environment review. You want to do this continuously, not once per year, to keep track of improvements or other changes.
Since each control definition is unique, it must be dynamically implemented. A configuration definition that includes every control difference would be too complicated and hard to maintain.

Nodelab can automate these tasks, using parameterized Edgescript® calls to a number of methods, and present the result as a hierarchical traffic-light tree view.

This example starts with an Excel file. The first column is used for determining which database table the row is stored in, with references to other row types above.

Excel is a very good tool for manual bulk editing and for viewing current states of complicated systems. It is one of many ways to input data into Nodelab.
When calculating status, a control can look into any other data source, both automatically collected or manually edited.

This Excel requirement definition file can either be added to a file structure that is part of the Nodelab resources, or it can be periodically or manually downloaded to a specific file directory.

Class

First, we define different clases for the rows. These classes are used by the server for automatically creating or modifying corresponding database tables. They are also used by the server and the browser in Edgescript® code.
This is the Requirement class.

class Requirement {
  @propertyTitle('Requirement Code')
  let reqCode: Char<255>

  @propertyTitle('Order Id')
  let orderId: Int

  @propertyTitle('Name')
  let name: Char<255>

  @propertyTitle('Review Time')
  let reviewTime: Datetime

  @propertyTitle('Description')
  let description: Text

  @propertyTitle('Comment')
  let comment: Text
}

Pattern

This is an Edgescript® example of a pattern parsing definition for the Excel file above.

    RecordPattern(
        isResource: true,
        pattern: 'Requirement(?:-.*)\.xlsx',
        processRecord: (storage: RecordStorage, record: [String], pathData: [String: Node], env: [String: Node]): Boolean -> {
            let std = ReqStandard()
            let version = ReqVersion()
            let req = Requirement()
            var rowType, stdOrg, parentOrg, parentCode, ctrlCode: String
            RecordPattern.parse(record, \.rowType, \.stdOrg, \.std.stdCode, \.version.versionCode, \.version.effectiveFrom,
                \.req.reqCode, \.req.name, \.parentOrg, \.parentCode, \.req.reviewTime, \.ctrlCode, \.req.description,
                \.req.comment)
            if (rowType=='STD') {
                std._orgCode = env['orgCode']
                std.name = std.stdCode
                storage.store(std)
                env['stdCode'] = std.stdCode
            } else if (rowType=='VER') {
                version._orgCode = env['orgCode']
                version.stdCode = env['stdCode']
                version.name = version.versionCode
                storage.store(version)
                env['versionCode'] = version.versionCode
            } else if (rowType=='REQ') {
                req._orgCode = env['orgCode']
                req.orderId = if env['orderId'] Int(env['orderId']) + 1 else 1
                storage.store(req)
                let content = ReqContent()
                content._orgCode = env['orgCode']
                content.stdCode = env['stdCode']
                content.versionCode = env['versionCode']
                content.reqCode = req.reqCode
                storage.store(content)
                env['reqCode'] = req.reqCode
                env['orderId'] = req.orderId
                if (parentCode) {
                  let parent = ReqParent()
                  parent._orgCode = env['orgCode']
                  parent.parentCode = parentCode
                  parent.childCode = req.reqCode
                  storage.store(parent)
                }
            } else if (rowType=='CTRL') {
                let ctrl = ReqControl()
                ctrl._orgCode = env['orgCode']
                ctrl.reqCode = env['reqCode']
                ctrl.ctrlCode = ctrlCode
                storage.store(ctrl)
            }
            return true
        })

Evaluation

The script associated with each control is evaluated, periodically or at request, to produce status points for each control or configurable item, depending on the nature of the control.

Visualization

This is an example of code for a browser page. It looks similar to SwiftUI.
Note that the tree view uses a single query instance for the collection of a whole tree consisting of data from ten different database tables, where each table can have its own parent-child dependency.
The status category, which determines the traffic-light color of each node, is aggregated from children to parents.

class ComplianceDashboard: Page {
  state layer = QueryState(valuesQuery: Query(
    paths: [\StatusLayer.layerId, \StatusLayer.name],
    orderPaths: [\StatusLayer.layerId],
    limit: 1000,
    isDistinct: true
  ))
  state reqCode = State()
  state req = QueryState(Query(
    paths: [\Requirement.name, \Requirement.description],
    filters: ['\Requirement.recCode==\(reqCode)'],
    limit: 1
  ))
  init() {
    let treeQuery = Query(
      titlePath: \ReqStandard.name,
      paths: [\ReqStandard.stdCode],
      orderPaths: [\ReqStandard.name],
      levels: [(
        paths: [(
          parent: \ReqStandard.stdCode,
          child: \ReqVersion.stdCode
        )],
        query: Query(
          titlePath: \ReqVersion.name,
          paths: [\ReqVersion.versionCode],
          maxClassCount: 2,
          levels: [(
            paths: [(
              parent: \ReqVersion.stdCode,
              child: \ReqContent.stdCode
            ), (
              parent: \ReqVersion.versionCode,
              child: \ReqContent.versionCode
            ), (
              parent: \ReqContent.reqCode,
              child: \Requirement.reqCode
            )],
            query: Query(
              titlePath: \Requirement.name,
              paths: [\Requirement.reqCode],
              orderPaths: [\Requirement.orderId],
              limit: 100000,
              maxClassCount: 4,
              tree: [(
                parent: \Requirement.reqCode,
                child: \ReqParent.parentCode
              ), (
                parent: \ReqParent.childCode,
                child: \ReqParent.childCode
              )],
              levels: [(
                paths: [(
                  parent: \Requirement.reqCode,
                  child: \ReqControl.reqCode
                ), (
                  parent: \ReqControl.ctrlCode,
                  child: \Control.ctrlCode
                )],
                query: Query(
                  edgeSpaces: [
                    [ status: (
                      className: "Status",
                      filter: "\Control.ctrlCode==\status.ctrlCode"
                    )],
                    [ statusCat: (
                      className: "StatusCategory",
                      filter: "\status.category==\statusCat.name"
                    )]
                  ],
                  titlePath: \Control.ctrlCode,
                  paths: [\Control.ctrlCode, \StatusCategory.level..max],
                  limit: 100000,
                  maxClassCount: 12,
                  tree: [(
                    parent: \Control.ctrlCode,
                    child: \ControlParent.parentCode
                  ), (
                    parent: \ControlParent.childCode,
                    child: \Control.ctrlCode
                  )],
                  layers: [(
                    parent: \StatusLayer.parentId,
                    child: \StatusLayer.layerId
                  )],
                  layerPaths: [(
                    parent: \Status.layerId,
                    child: \StatusLayer.layerId
                  )]
                )
              )]
            )
           )]
         )
      )]
    )
    super(
      VStack(
        HStack(
          StateSelector(\.layer, title: 'Layer', titlePath: \StatusLayer.name, defaultSelectionIndex: 0, css: 'col-md-2')
        ),
        HStack(
          TreeView(
            title: 'Standards',
            stateIcons: ['status-none', 'status-valid', 'status-partial', 'status-pending', 'status-invalid'],
            stateScript: '\StatusCategory.level|0',
            query: treeQuery
          )
        )
      )
    )
  }
}

UNQL

The query is translated into a UNQL request:

{
  "siteCode": "ref",
  "packCode": "ref.test",
  "orgCode": "org.test",
  "appCode": "analytics",
  "joinBranches": 1,
  "lang": "en",
  "limit": 100,
  "query": {
    "titlePath": "\\ReqStandard.name",
    "paths": [
      "\\ReqStandard.stdCode"
    ],
    "orderPaths": [
      "\\ReqStandard.name"
    ],
    "limit": 100,
    "maxClassCount": 1,
    "levels": [
      {
        "paths": [
          {
            "parent": "\\ReqStandard.stdCode",
            "child": "\\ReqVersion.stdCode"
          }
        ],
        "query": {
          "titlePath": "\\ReqVersion.name",
          "paths": [
            "\\ReqVersion.versionCode"
          ],
          "orderPaths": [
            "\\ReqVersion.name"
          ],
          "limit": 100,
          "maxClassCount": 2,
          "levels": [
            {
              "paths": [
                {
                  "parent": "\\ReqVersion.stdCode",
                  "child": "\\ReqContent.stdCode"
                },
                {
                  "parent": "\\ReqVersion.versionCode",
                  "child": "\\ReqContent.versionCode"
                },
                {
                  "parent": "\\ReqContent.reqCode",
                  "child": "\\Requirement.reqCode"
                }
              ],
              "query": {
                "titlePath": "\\Requirement.name",
                "paths": [
                  "\\Requirement.reqCode"
                ],
                "orderPaths": [
                  "\\Requirement.orderId"
                ],
                "limit": 100000,
                "maxClassCount": 5,
                "tree": [
                  {
                    "parent": "\\ReqParent.childCode",
                    "child": "\\Requirement.reqCode"
                  },
                  {
                    "parent": "\\Requirement.reqCode",
                    "child": "\\ReqParent.parentCode"
                  }
                ],
                "levels": [
                  {
                    "paths": [
                      {
                        "parent": "\\Requirement.reqCode",
                        "child": "\\ReqControl.reqCode"
                      },
                      {
                        "parent": "\\ReqControl.ctrlCode",
                        "child": "\\Control.ctrlCode"
                      }
                    ],
                    "query": {
                      "edgeSpaces": [
                        { "status": {
                          "className": "Status",
                          "filters": ["\\Control.ctrlCode==\\status.ctrlCode"]
                        }},
                        { "statusCat": {
                          "className": "StatusCategory",
                          "filters": ["\\status.category==\\statusCat.name"]
                        }}
                      ],
                      "titlePath": "\\Control.ctrlCode",
                      "paths": [
                        "\\Control.ctrlCode",
                        "\\statusCat.level..max"
                      ],
                      "limit": 100000,
                      "maxClassCount": 11,
                      "tree": [
                        {
                          "parent": "\\ControlParent.childCode",
                          "child": "\\Control.ctrlCode"
                        },
                        {
                          "parent": "\\Control.ctrlCode",
                          "child": "\\ControlParent.parentCode"
                        }
                      ],
                      "layers": [
                        {
                          "parent": "\\StatusLayer.parentId",
                          "child": "\\StatusLayer.layerId"
                        }
                      ],
                      "layerPaths": [
                        {
                          "parent": "\\StatusLayer.layerId",
                          "child": "\\status.layerId"
                        }
                      ]
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    ]
  }
}

SQL

The UNQL request is translated into four SQL queries.

SELECT T1.NAME AS T1_NAME,
	T1.STDCODE AS T1_STDCODE
FROM ORG_TEST_REF_TEST_REQSTANDARD T1
ORDER BY T1.NAME

SELECT T1.STDCODE AS T1_STDCODE,
	T2.NAME AS T2_NAME,
	T2.STDCODE AS T2_STDCODE,
	T2.VERSIONCODE AS T2_VERSIONCODE
FROM ORG_TEST_REF_TEST_REQSTANDARD T1
LEFT JOIN ORG_TEST_REF_TEST_REQVERSION T2 ON (T1.STDCODE = T2.STDCODE)
ORDER BY T2.NAME

SELECT T4.REQCODE AS T4_REQCODE,
	T4.STDCODE AS T4_STDCODE,
	T4.VERSIONCODE AS T4_VERSIONCODE,
	T5.CHILDCODE AS T5_CHILDCODE,
	T5.PARENTCODE AS T5_PARENTCODE,
	T1.NAME AS T1_NAME,
	T1.STDCODE AS T1_STDCODE,
	T2.NAME AS T2_NAME,
	T2.STDCODE AS T2_STDCODE,
	T2.VERSIONCODE AS T2_VERSIONCODE,
	T3.NAME AS T3_NAME,
	T3.ORDERID AS T3_ORDERID,
	T3.REQCODE AS T3_REQCODE
FROM ORG_TEST_REF_TEST_REQSTANDARD T1
LEFT JOIN ORG_TEST_REF_TEST_REQVERSION T2 ON (T1.STDCODE = T2.STDCODE)
LEFT JOIN ORG_TEST_REF_TEST_REQCONTENT T4 ON (T2.STDCODE = T4.STDCODE)
LEFT JOIN ORG_TEST_REF_TEST_REQUIREMENT T3 ON (T4.REQCODE = T3.REQCODE)
LEFT JOIN ORG_TEST_REF_TEST_REQPARENT T5 ON (T5.CHILDCODE = T3.REQCODE)
ORDER BY T3.ORDERID

SELECT T6.CTRLCODE AS T6_CTRLCODE,
	T6.ORDERID AS T6_ORDERID,
	T10.CHILDCODE AS T10_CHILDCODE,
	T10.PARENTCODE AS T10_PARENTCODE,
	T4.REQCODE AS T4_REQCODE,
	T4.STDCODE AS T4_STDCODE,
	T4.VERSIONCODE AS T4_VERSIONCODE,
	T9.CTRLCODE AS T9_CTRLCODE,
	T9.REQCODE AS T9_REQCODE,
	T5.CHILDCODE AS T5_CHILDCODE,
	T5.PARENTCODE AS T5_PARENTCODE,
	T1.NAME AS T1_NAME,
	T1.STDCODE AS T1_STDCODE,
	T2.NAME AS T2_NAME,
	T2.STDCODE AS T2_STDCODE,
	T2.VERSIONCODE AS T2_VERSIONCODE,
	T3.NAME AS T3_NAME,
	T3.ORDERID AS T3_ORDERID,
	T3.REQCODE AS T3_REQCODE,
	T11.LAYERID AS T11_LAYERID,
	STATUS_T7.LAYERID AS STATUS_T7_LAYERID,
	MAX(STATUSCAT_T8.LEVEL) AS STATUSCAT_T8_LEVEL
FROM ORG_TEST_REF_TEST_REQSTANDARD T1
LEFT JOIN ORG_TEST_REF_TEST_REQVERSION T2 ON (T1.STDCODE = T2.STDCODE)
LEFT JOIN ORG_TEST_REF_TEST_REQCONTENT T4 ON (T2.STDCODE = T4.STDCODE)
LEFT JOIN ORG_TEST_REF_TEST_REQUIREMENT T3 ON (T4.REQCODE = T3.REQCODE)
LEFT JOIN ORG_TEST_REF_TEST_REQPARENT T5 ON (T5.CHILDCODE = T3.REQCODE)
LEFT JOIN ORG_TEST_REF_TEST_REQCONTROL T9 ON (T3.REQCODE = T9.REQCODE)
LEFT JOIN ORG_TEST_REF_TEST_CONTROL T6 ON (T9.CTRLCODE = T6.CTRLCODE)
LEFT JOIN ORG_TEST_REF_TEST_STATUS STATUS_T7 ON (T6.CTRLCODE = STATUS_T7.CTRLCODE)
LEFT JOIN ORG_TEST_REF_TEST_STATUSCATEGORY STATUSCAT_T8 ON (STATUS_T7.CATEGORY = STATUSCAT_T8.NAME)
LEFT JOIN ORG_TEST_REF_TEST_CONTROLPARENT T10 ON (T10.CHILDCODE = T6.CTRLCODE)
LEFT JOIN ORG_TEST_REF_TEST_STATUSLAYER T11 ON (T11.LAYERID = STATUS_T7.LAYERID)
GROUP BY T6_CTRLCODE,
	T6_ORDERID,
	T10_CHILDCODE,
	T10_PARENTCODE,
	T4_REQCODE,
	T4_STDCODE,
	T4_VERSIONCODE,
	T9_CTRLCODE,
	T9_REQCODE,
	T5_CHILDCODE,
	T5_PARENTCODE,
	T1_NAME,
	T1_STDCODE,
	T2_NAME,
	T2_STDCODE,
	T2_VERSIONCODE,
	T3_NAME,
	T3_ORDERID,
	T3_REQCODE,
	T11_LAYERID,
	STATUS_T7_LAYERID
ORDER BY T6.ORDERID

The results are combined into a tree structure, and the server returns a JSON tree structure based on data from twelve different database tables.