Field NationDeveloper Platform
Field NationDeveloper Platform
Pre-built ConnectorsGetting Started
Integration Broker Architecture

Configuration

Field MappingsCustom Actions with JSONNETEvents & Synchronization

Troubleshooting

Troubleshooting
Concepts

Custom Actions with JSONNET

Write advanced data transformation logic using JSONNET for complex field mapping scenarios.


JSONNET Overview

JSONNET is a data templating language developed by Google that generates JSON through programmable logic. It's designed for configuration management and data transformation.

Functions

Define reusable transformation logic with parameters

Variables

Store intermediate values using local bindings

Conditionals

Implement branching with if-then-else

Standard Library

Built-in functions for strings, arrays, objects, math

Imports

Include external libraries for code reuse

Learn More

For complete language reference, visit the Official JSONNET Documentation →


Execution Context

Every custom action executes within a special context providing access to data and utilities:

Prop

Type

Description

$.inputobject

Complete input payload containing work order or external record data. Access nested fields with dot notation: `$.input.workorder.id`

$.utilobject

Utility library with helper functions for common transformations (field lookup, date conversion, mapping)

stdobject

JSONNET standard library with array, string, math, and type functions


Basic Custom Action

The simplest custom action directly accesses input fields:

{
  full_name: $.util.lookup_field($.input, 'assignee.user.first_name', '') + ' ' +
             $.util.lookup_field($.input, 'assignee.user.last_name', '')
}

Result: Concatenates first and last name with space, using safe defaults for missing fields.


Utility Library ($.util)

The Integration Broker provides comprehensive utility functions optimized for field mappings.

Field Lookup Functions


Mapping Functions


Date Conversion Functions


Array Processing Functions


Expense Aggregation Functions


Common Patterns

Use if-then-else with local variables for complex decisions:

{
  priority:
    local hours = $.util.lookup_field($.input, 'time_logs.hours', 0);
    local is_urgent = $.util.lookup_field($.input, 'priority', '') == 'urgent';

    if is_urgent && hours > 2 then
      "Critical"
    else if is_urgent then
      "High"
    else if hours > 4 then
      "Medium"
    else
      "Low"
}

Common string operations using the standard library:

{
  // Uppercase
  title_upper: std.asciiUpper($.util.lookup_field($.input, 'workorder.title', '')),

  // Extract email domain
  email_domain:
    local email = $.util.lookup_field($.input, 'user.email', '');
    local parts = std.split(email, '@');
    if std.length(parts) > 1 then parts[1] else '',

  // Remove whitespace
  clean_text: std.stripChars($.util.lookup_field($.input, 'description', ''), ' \t\n')
}

Filter, map, and find operations:

{
  // Filter array
  high_value_expenses:
    local all_expenses = $.input.pay.expense.results;
    std.filter(function(e) e.amount > 100, all_expenses),

  // Map array
  expense_amounts:
    std.map(
      function(e) e.amount,
      $.input.pay.expense.results
    ),

  // Find element
  first_travel_expense:
    local expenses = $.input.pay.expense.results;
    local travel = std.filter(
      function(e) e.category.name == 'Travel',
      expenses
    );
    if std.length(travel) > 0 then travel[0] else null
}

Round, format, and calculate percentages:

{
  // Round to 2 decimal places
  rounded_cost: std.round($.input.pay.amount * 100) / 100,

  // Format as currency
  formatted_amount:
    local amount = $.util.lookup_field($.input, 'pay.amount', 0);
    "$" + std.toString(std.round(amount * 100) / 100),

  // Percentage calculation
  completion_rate:
    local completed = $.util.lookup_field($.input, 'tasks_completed', 0);
    local total = $.util.lookup_field($.input, 'tasks_total', 1);
    std.round((completed / total) * 100) + "%"
}

Build nested objects from flat data:

{
  // Build nested object
  contact_info: {
    name: $.util.lookup_field($.input, 'assignee.user.first_name', '') + ' ' +
          $.util.lookup_field($.input, 'assignee.user.last_name', ''),
    email: $.util.lookup_field($.input, 'assignee.user.email', ''),
    phone: $.util.lookup_field($.input, 'assignee.user.phone', ''),
    address: {
      street: $.util.lookup_field($.input, 'location.address1', ''),
      city: $.util.lookup_field($.input, 'location.city', ''),
      state: $.util.lookup_field($.input, 'location.state', ''),
      zip: $.util.lookup_field($.input, 'location.zip', '')
    }
  }
}

Advanced Examples


Best Practices

Use Local Variables

Store intermediate values for readability and performance:

{
  result:
    // Reusable, clear
    local hours = $.util.lookup_field($.input, 'time_logs.hours', 0);
    local rate = $.util.lookup_field($.input, 'pay.hourly_rate', 0);
    local total = hours * rate;
    std.round(total * 100) / 100
}
{
  result:
    // Repeated lookups, hard to read
    std.round(($.util.lookup_field($.input, 'time_logs.hours', 0) *
               $.util.lookup_field($.input, 'pay.hourly_rate', 0)) * 100) / 100
}

Always Provide Defaults

Prevent null pointer errors with safe defaults:

{
  // Safe with defaults
  total: $.util.lookup_field($.input, 'amount', 0) +
         $.util.lookup_field($.input, 'tax', 0)
}
{
  // May crash if fields missing
  total: $.input.amount + $.input.tax
}

Cache Expensive Operations

Calculate once, use multiple times:

{
  result:
    local all_expenses = $.input.pay.expense.results;
    local total = $.util.sum_expense_field(all_expenses, "amount");
    local count = std.length(all_expenses);
    {
      total: total,
      count: count,
      average: if count > 0 then total / count else 0
    }
}

Check Types Before Operations

Validate types to prevent runtime errors:

{
  safe_division:
    local numerator = $.util.lookup_field($.input, 'completed', 0);
    local denominator = $.util.lookup_field($.input, 'total', 1);

    if std.isNumber(numerator) && std.isNumber(denominator) && denominator != 0 then
      numerator / denominator
    else
      0
}

Testing Custom Actions

Test in Isolation

Create a test payload and validate output:

// Test payload
local test_input = {
  workorder: {
    id: 12345,
    title: "Test WO",
    pay: { amount: 150.50 }
  }
};

// Your transformation
local result = {
  id: test_input.workorder.id,
  formatted_amount: "$" + std.toString(test_input.workorder.pay.amount)
};

// Output for testing
result

Debug with std.trace

Add trace statements to inspect values (output appears in integration logs):

{
  result:
    local value = $.util.lookup_field($.input, 'some.field', 'default');
    local traced = std.trace("Field value: " + std.toString(value), value);
    // Transformation continues with traced value
    traced
}

Troubleshooting

Last updated on

Field Mappings

Configure how data transforms between Field Nation and external platforms using flexible mapping actions.

Events & Synchronization

Understanding work order events that trigger connector synchronization and how to configure event-based data flows.

On this page

JSONNET Overview
Execution Context
Basic Custom Action
Utility Library ($.util)
Field Lookup Functions
Mapping Functions
Date Conversion Functions
Array Processing Functions
Expense Aggregation Functions
Common Patterns
Advanced Examples
Best Practices
Use Local Variables
Always Provide Defaults
Cache Expensive Operations
Check Types Before Operations
Testing Custom Actions
Test in Isolation
Debug with std.trace
Troubleshooting