Skip to main content

Code packages tutorials

JSON ADMIN API tutorials for using code packages

Tutorials on using JSON ADMIN API code packages with FairCom databases. It focuses on two primary types: `integrationTableTransform` for modifying data as it enters the database (e.g., converting temperatures, creating status fields), and `getRecordsTransform` for altering data as it's retrieved without changing the original database entries (e.g., rounding numbers, trimming strings, or generating analytics). The tutorials include practical examples with JSON payloads for creating code packages, defining transforms, inserting and retrieving records, and specific JavaScript code snippets for performing data manipulations.

code packages
codePackages API
create code package
manage code package
how to insert code package
how to alter code package
json admin api
JSON Action tutorials
json api tutorials
data transformation tutorials
data enrichment
mqtt processing
data conversion
database integration
code package tutorial
how to manage code package
data transformation

Tutorial on creating a JavaScript transform

This tutorial creates a transform that processes MQTT messages sent to the topic "robot1_temperature". The transform extracts a Celsius temperature from the JSON payload, converts it to Fahrenheit, and writes each temperature to the appropriate fields in the table "temperature_celsius" and "temperature_fahrenheit". The transform also creates a status field named temperature_status and populates it with information about the temperature, such as "cold" and "normal".

Send an MQTT message

Send a message to the MQTT topic named "robot1_temperature".

{
  "temperature": 21.111
}

When a record is inserted into an integration table, the source_payload field contains JSON data, such as the following JSON object, which contains a Fahrenheit temperature.

Create JavaScript code

Use the following tutorial to convert a Fahrenheit temperature to Celsius

The following code extracts the "temperature_celsius" property from the source_payload field of the current record in an integration table. It converts the Celsius temperature to Fahrenheit and stores the result in the temperature_fahrenheit field in the current record of an integration table. The temperature in Celsius is then stored in the temperature_celsius field in the integration table.

record.temperature_celsius = record.source_payload.temperature
record.temperature_fahrenheit = getFahrenheitFromCelsius(record.temperature_celsius)
record.temperature_status = calculateTemperatureStatus(record.temperature_fahrenheit)

function getFahrenheitFromCelsius(temperature_celsius){
  return (temperature_celsius * 1.8) + 32;
}
function calculateTemperatureStatus(temperature_fahrenheit){
  switch (true) {
    case (temperature_fahrenheit < 0):
      return "alert: too cold";
    case (temperature_fahrenheit < 32):
      return "cold";
    case (temperature_fahrenheit > 80):
      return "hot";
    case (temperature_fahrenheit > 140):
      return "alert: too hot";
    default:
      return "normal";
  }
}

Create a code package

Run the following "createCodePackage" action to create a JavaScript code package named "convertTemperature".

{
  "api": "admin",
  "action": "createCodePackage",
  "params": {
    "databaseName": "faircom",
    "ownerName": "admin",
    "codeName": "convertTemperature",
    "codeLanguage": "javascript",
    "codeType": "integrationTableTransform",
    "codeStatus": "developing",
    "description": "1. Copies the value from the temperature property in source_payload to the temperature_fahrenheit field.\n2. Converts temperature_fahrenheit into Celsius and stores it in the temperature_celsius field.\n3. Stores alerts about temperature in temperature_status field.",
    "metadata": {},
    "comment": "optional comment about the current version",
    "codeFormat": "utf8",
    "code": "// The server runs the following JavaScript code, which is registered in the server as a transform method.\nrecord.temperature_fahrenheit = record.source_payload.temperature\nrecord.temperature_celsius = getCelsiusFromFahrenheit(record.temperature_fahrenheit)\nrecord.temperature_status = calculateTemperatureStatus(record.temperature_fahrenheit)\n\nfunction getCelsiusFromFahrenheit(temperature_fahrenheit){\n    return (temperature_fahrenheit - 32) / 1.8;\n}\n\nfunction calculateTemperatureStatus(temperature_fahrenheit){\n    switch (true) {\n        case (temperature_fahrenheit < 0):\n            return \"alert: too cold\";\n        case (temperature_fahrenheit < 32):\n            return \"cold\";\n        case (temperature_fahrenheit > 80):\n            return \"hot\";\n        case (temperature_fahrenheit > 140):\n            return \"alert: too hot\";\n        default:\n            return \"normal\";\n    }\n}"  
  },
  "authToken": "replaceWithAuthTokenFromCreateSession"
}

Note that for the "code" value to be valid JSON, it must be a single string with no line breaks.

Create a transform

{
  "api": "hub",
  "action": "createTransform",
  "params": {
    "transformName": "transformFahrenheitToCelsius",
    "transformActions": [
      {
        "transformStepMethod": "javascript",
        "transformService": "v8TransformService",
        "inputFields": ["source_payload"],
        "outputFields": [
          "temperature_celsius",
          "temperature_fahrenheit",
          "temperature_status"
        ],
        "transformParams": {
          "codeName": "convertTemperature",
          "databaseName": "ctreeSQL",
          "ownerName": "admin",
          "codePackagePoperties": {},
          "outputFieldDefinitions": [
            {
              "name": "temperature_celsius",
              "type": "float"
            },
            {
              "name": "temperature_fahrenheit",
              "type": "float"
            },
            {
              "name": "temperature_status",
              "type": "varchar",
              "length": 100
            }
          ]
        }
      }
    ]
  },
  "authToken": "replaceWithAuthTokenFromCreateSession"
}

"codePackageProperties" is an optional parameter that lets the user add run-time properties and values to JavaScript before the server runs the code When the value is an object, it contains properties and values that the server adds to the JavaScript engine.

"codePackageProperties": {
  "myCustomProperty1": "a string that the JavaScript code will use",
  "myCustomProperty2": 42
},

transform codepackage tutorial

code packagecodepackagetransformintegrationTableTransformintegration table transformtutorial

Tutorial on creating a JavaScript transform

This tutorial creates a Code Package to use when retrieving records from JSON DB. The advantage of this method is that the data in the database is not affected. Use this method when you want to preserve the original data.

This tutorial assumes that you have already logged in using createSession and have a valid authToken.

Create a table to use this Code Package against

POST the following JSON to the FairCom endpoint:

{
  "api": "db",
  "action": "createTable",
  "params": {
    "databaseName": "faircom",
    "tableName": "testTable",
    "fields": [
      {
        "name": "numberField",
        "type": "float"
      },
      {
        "name": "numberFieldRounded",
        "type": "float"
      },
      {
        "name": "stringField",
        "type": "varchar",
        "length": 50
      },
      {
        "name": "stringFieldTrimmed",
        "type": "varchar",
        "length": 50
      }
    ]
  },
  "authToken": "replaceWithAuthTokenFromCreateSession"
}

Replace the authToken with the one from your current session

Insert records into the table

POST the following JSON to the FairCom endpoint:

{
  "api": "db",
  "action": "insertRecords",
  "params": {
    "databaseName": "faircom",
    "tableName": "testTable",
    "dataFormat": "objects",
    "sourceData": [
      {
        "numberField": 1234.5678,
        "numberFieldRounded": 1234.5678,
        "stringField": "   FairCom   ",
        "stringFieldTrimmed": "   FairCom   "
      },
      {
        "numberField": 8765.4321,
        "numberFieldRounded": 8765.4321,
        "stringField": "   Edge   ",
        "stringFieldTrimmed": "   Edge   "
      },
      {
        "numberField": 2112.2112,
        "numberFieldRounded": 2112.2112,
        "stringField": "   Rush   ",
        "stringFieldTrimmed": "   Rush   "
      }
    ]
  },
  "authToken": "replaceWithAuthTokenFromCreateSession"
}

Note that each varchar field has spaces around the record and that each float field has four digits after the decimal place.

Create JavaScript code

POST the following JSON to the FairCom endpoint:

{
  "api": "admin",
  "action": "createCodePackage",
  "params": {
    "ownerName": "admin",
    "databaseName": "faircom",
    "codeName": "roundAndTrim",
    "codeLanguage": "javascript",
    "serviceName": "javascript",
    "codeType": "getRecordsTransform",
    "codeStatus": "active",
    "description": "Test transform for rounding a numeric field and trimming a string field",
    "codeFormat": "utf8",
    "comment": "Original code",
    "code": "records.forEach( function( record ){ record.numberFieldRounded = parseFloat( record.numberFieldRounded.toFixed( 2 ) );record.stringFieldTrimmed = record.stringField.trim(); } );\n"
  },
  "authToken": "replaceWithAuthTokenFromCreateSession"
}

Note that the actual code when formatted looks like this:

records.forEach
(
  function( record )
  {
    record.numberFieldRounded = parseFloat( record.numberFieldRounded.toFixed( 2 ) );
    record.stringFieldTrimmed = record.stringField.trim();
  }
);

Here is a pseudocode explanation for this Code Package:

For each record processed:

  1. Modify the numberFieldRounded field to two fixed decimal places

  2. Modify the stringFieldTrimmed field by trimming leading and trailing whitespace

Retrieve records using the code package

POST the following JSON to the FairCom endpoint:

{
  "api": "db",
  "action": "getRecordsByTable",
  "params": {
    "databaseName": "faircom",
    "tableName": "testTable",
    "transformCodeName": "roundAndTrim"
  },
  "authToken": "replaceWithAuthTokenFromCreateSession"
}

Note that this is a normal getRecordsByTable request with one exception: the params block contains the "transformCodeName" property, which is set to the name of our Code Package "roundAndTrim".

The response from that getRecordsByTable will contain a data block that looks like this:

[
  {
    "changeId": 1525,
    "id": 1,
    "numberField": 1234.5678,
    "numberFieldRounded": 1234.57,
    "stringField": "   FairCom   ",
    "stringFieldTrimmed": "FairCom"  },
  {
    "changeId": 1525,
    "id": 2,
    "numberField": 8765.4321,
    "numberFieldRounded": 8765.43,
    "stringField": "   Edge   ",
    "stringFieldTrimmed": "Edge"  },
  {
    "changeId": 1525,
    "id": 3,
    "numberField": 2112.2112,
    "numberFieldRounded": 2112.21,
    "stringField": "   Rush   ",
    "stringFieldTrimmed": "Rush"
  }
]

Note that the "numberFieldRounded" fields have only two decimal places and all of the "stringFieldTrimmed" fields have no leading or trailing whitespace.

You can confirm that the Code Package worked by calling getRecordsByTable again, but without the "transformCodeName" property:

{
  "api": "db",
  "action": "getRecordsByTable",
  "params": {
    "databaseName": "faircom",
    "tableName": "testTable"
  },
  "authToken": "replaceWithAuthTokenFromCreateSession"
}

The response will contain a data block that looks like this:

[
  {
    "changeId": 3362,
    "id": 1,
    "numberField": 1234.5678,
    "numberFieldRounded": 1234.5678,
    "stringField": "   FairCom   ",
    "stringFieldTrimmed": "   FairCom   "  },
  {
    "changeId": 3362,
    "id": 2,
    "numberField": 8765.4321,
    "numberFieldRounded": 8765.4321,
    "stringField": "   Edge   ",
    "stringFieldTrimmed": "   Edge   "  },
  {
    "changeId": 3362,
    "id": 3,
    "numberField": 2112.2112,
    "numberFieldRounded": 2112.2112,
    "stringField": "   Rush   ",
    "stringFieldTrimmed": "   Rush   "
  }
]

The "numberFieldRounded" fields and the "stringFieldTrimmed" fields show that the data in the database has not been affected by the Code Package.

Return modified records

Embed this JavaScript code in a "createCodePackage" action to save the code to the server. To transform the results of the query, put the code package name in the "transformCodeName" property in any "getRecords..." query.

records.forEach(record => {

  try {
    record.temperatureRounded = Number(record.temperature.toFixed(2))
    record.deviceName = record.deviceName.trim();
  }
  catch(err) {
    record.transformError = err.message
  }
});

A "codeType": "getRecordsTransform" example returning analytics

Embed this JavaScript code in a "createCodePackage" action to save the code to the server. To return the analytics of the query, put the code package name in the "transformCodeName" property in any "getRecords..." query.

try {

  count = 0;
  temperatureTotal = 0.0;
  averageTemperature = 0.0;

  //Calculate analytics
  records.forEach(record => {
    count++;
    temperatureTotal = temperatureTotal + record.temperature;   
  });

  //Return analytics
  averageTemperature = temperatureTotal / count;
  records = [
    {
      "recordCount": count,
      "averageTemperature": averageTemperature
    }
  ];
  
}
catch(err) {
  //Return an error
  records = [
    {
      "transformError": err.message,
      "recordCount": null,
      "averageTemperature": null
    }
  ];
}

getRecordsTransform javascript transform tutorial for code packages

code packagecodepackgetutorialgetRecordsTransformjavascript transform

This tutorial creates a JavaScript transform that automatically responds to a published message by looking up a record in another table and changing its state. It uses FairCom's built-in runJsonAction(request) function to perform these tasks. 

Using the techniques in this tutorial, you can write JavaScript code to perform any type of JSON action in response to MQTT messages, inserted records, and collected data.

Demo

You can preview the results of the tutorial by looking at the screenshots now. After you have finished the tutorial, you can run this demo and publish messages to the controlMachine topic to change the state of machines in the machine table.

  1. Use MQ Explorer to publish the following JSON message to the controlMachine topic. The purpose of the message is to update the status of machine 3 to the "ready" state.

    {
        "machineID": 3,
        "newStatus": "ready"
    }
    
    Demo1.png
  2. Use Edge Explorer to view the record in the mqtt_msg_controlmachine table. You can see that the record's  source_payload field contains the published message. You can also see two columns in the table, machine_name and machine_status, which contain the machine's name and status. The JavaScript transform populated these fields.

    Demo2.png
  3. Use Data Explorer to view the machine table and verify its record matches the new status of "ready" as requested in the MQTT message. The JavaScript transform used FairCom's built-in runJsonAction(request) function to run JSON actions to look up the record with an ID of 3 and update its status to "ready".

    Demo3.png
  4. Use MQ Explorer to publish a new JSON message to the controlMachine topic to update machine 3 to the "offline" state.

    {
        "machineID": 3,
        "newStatus": "offline"
    }
    
    Demo4.png
  5. Use Edge Explorer to refresh the records to view the new record in the mqtt_msg_controlmachine table that contains the published message. Notice the machine_status field now has a state of "offline".

    Demo5.png
  6. Use Data Explorer to refresh the records in the machine table and verify that its record matches the new status of "offline".

    Demo6.png

Run the tutorial

Use the API Explorer in FairCom's Data Explorer web application to run the following commands.

Run1.png
  1. Create the machine table to track the state of machines.

    {
      "api": "db",
      "action": "createTable",
      "params": {
        "tableName": "machine",
        "fields": [
          {
            "name": "name",
            "type": "varchar",
            "length": 50
          },
          {
            "name": "status",
            "type": "varchar",
            "length": 16
          }
        ]
      },
      "authToken": "replaceWithAuthTokenFromCreateSession"
    }
    
  2. Insert records into the machine table.

    {
      "api": "db",
      "action": "insertRecords",
      "params": {
        "tableName": "machine",
        "dataFormat": "objects",
        "sourceData": [
          {
            "name": "machine1",
            "status": "normal"
          },
          {
            "name": "machine2",
            "status": "off"
          },
          {
            "name": "machine3",
            "status": "normal"
          }
        ]
      },
      "authToken": "replaceWithAuthTokenFromCreateSession"
    }
    
  3. Create the code package that the JavaScript transform will run. It is named "changeMachineState".

    {
      "api": "admin",
      "action": "createCodePackage",
      "params": {
        "codeName": "changeMachineState",
        "codeLanguage": "javascript",
        "codeType": "integrationTableTransform",
        "codeStatus": "active",
        "description": "1. Lookup for the machine by ID.\n2. If new status is different from the machine record, update it.\n3. Populate the machine_name and machine_status fields.",
        "code": "//language=JavaScript\n//1. Lookup for the machine by ID.\n//2. If new status is different from the machine record, update it.\n//3. Populate the machine_name and machine_status fields.\n\n\n// Lookup the machine register by the ID in the payload\nconst machineId = record.source_payload.machineID;\nconst newStatus = record.source_payload.newStatus;\nlet machine = getMachineByID(machineId);\n\n\n// If machine status from the payload is different, update the machine table\nif ( machine.status !== newStatus )\n  updateMachineStatus(machineId, newStatus);\n\n\n// Set the machine_name and machine_status fields\nrecord.machine_name = machine.name;\nrecord.machine_status = newStatus;\n\n\n//==================================================================\n// Function to retrieve the machine record by ID through JSON Action\nfunction getMachineByID(machineID)\n{\n  const request =\n    {\n      \"api\": \"db\",\n      \"action\": \"getRecordsByIds\",\n      \"params\":\n        {\n          \"tableName\": \"machine\",\n          \"ids\": [machineID]\n        }\n    };\n\n\n  const response = runJsonAction(request);\n\n\n  if (response.errorCode !== 0)\n    throw \"ERROR: getMachineByID() cannot find machine with ID \" + machineId + \".\" +\n    \" errorCode: \" + response.errorCode +\n    \" errorMessage: \" + response.errorMessage ;\n\n\n  return response.result.data[0];\n}\n\n\n//==================================================================\n// Function to update the machine status by ID through JSON Action\nfunction updateMachineStatus(machineID, newMachineStatus)\n{\n  const request =\n    {\n      \"api\": \"db\",\n      \"action\": \"updateRecords\",\n      \"params\": {\n        \"tableName\": \"machine\",\n        \"ignoreChangeIdProtection\": true,\n        \"sourceData\": [\n          {\n            \"id\": machineID,\n            \"status\": newMachineStatus\n          }\n        ]\n      }\n    };\n\n\n  const response = runJsonAction(request);\n\n\n  if (response.errorCode !== 0)\n    throw \"ERROR: updateMachineStatus() cannot update Machine with ID \" + machineId + \".\" +\n    \" errorCode: \" + response.errorCode +\n    \" errorMessage: \" + response.errorMessage ;\n}"
      },
      "authToken": "replaceWithAuthTokenFromCreateSession"
    }
    

    The code embedded in the code package's "code" property looks like the following:

    //1. Lookup for the machine by ID.
    //2. If new status is different from the machine record, update it.
    //3. Populate the machine_name and machine_status fields.
    
    
    // Lookup the machine register by the ID in the payload
    const machineId = record.source_payload.machineID;
    const newStatus = record.source_payload.newStatus;
    let machine = getMachineByID(machineId);
    
    
    // If machine status from the payload is different, update the machine table
    if ( machine.status !== newStatus )
        updateMachineStatus(machineId, newStatus);
    
    
    // Set the machine_name and machine_status fields
    record.machine_name = machine.name;
    record.machine_status = newStatus;
    
    
    //==================================================================
    // Function to retrieve the machine record by ID through JSON Action
    function getMachineByID(machineID)
    {
      const request =
        {
          "api": "db",
          "action": "getRecordsByIds",
          "params":
            {
              "tableName": "machine",
              "ids": [machineID]
            }
        };
    
    
      const response = runJsonAction(request);
    
    
      if (response.errorCode !== 0)
        throw "ERROR: getMachineByID() cannot find machine with ID " + machineId + "." +
              " errorCode: " + response.errorCode +
              " errorMessage: " + response.errorMessage ;
    
    
      return response.result.data[0];
    }
    
    
    //==================================================================
    // Function to update the machine status by ID through JSON Action
    function updateMachineStatus(machineID, newMachineStatus)
    {
      const request =
      {
        "api": "db",
        "action": "updateRecords",
        "params": {
          "tableName": "machine",
          "ignoreChangeIdProtection": true,
          "sourceData": [
            {
              "id": machineID,
              "status": newMachineStatus
            }
          ]
        }
      };
    
    
      const response = runJsonAction(request);
    
    
      if (response.errorCode !== 0)
        throw "ERROR: updateMachineStatus() cannot update Machine with ID " + machineId + "." +
        " errorCode: " + response.errorCode +
        " errorMessage: " + response.errorMessage ;
    }
    

    Tip

    In JetBrains IDEs, you can paste the previous code into an empty string "" in a JSON file and the IDE will automatically escape the code to work in the string. There are also online stringify and unstringify tools that do the same.

  4. Create the transform named "machineStateTransform".

    {
      "api": "transform",
      "action": "createTransform",
      "params": {
        "transformName": "machineStateTransform",
        "transformActions": [
          {
            "inputFields": [
              "source_payload"
            ],
            "outputFields": [
              "machine_name",
              "machine_status"
            ],
            "transformService": "v8TransformService",
            "transformStepMethod": "javascript",
            "transformParams": {
              "codeName": "changeMachineState",
              "outputFieldDefinitions": [
                {
                  "name": "machine_name",
                  "type": "varchar",
                  "length": 50
                },
                {
                  "name": "machine_status",
                  "type": "varchar",
                  "length": 16
                }
              ]
            }
          }
        ]
      },
      "authToken": "replaceWithAuthTokenFromCreateSession"
    }
    
  5. Create and configure the MQTT topic named "controlMachine" that runs the "machineStateTransform" when it receives messages.

    {
      "api": "mq",
      "action": "configureTopic",
      "params": {
        "topic": "controlMachine",
        "transformName": "machineStateTransform"
      },
      "authToken": "replaceWithAuthTokenFromCreateSession"
    }

    You have completed the tutorial and can run the demo to publish messages to the "controlMachine" topic that automatically update the state of machines in the machine table.