Transform Basics

Transforms use Go's text/template syntax to restructure JSON data. Here's a simple example:

{
  "user": {
    "name": "{{.name}}",
    "id": "{{.id}}",
    "email": "{{.email}}"
  }
}

The transform above extracts fields from the input and places them in a new structure.

Template Variables

Use dot notation to access fields from the input data:

"name": "{{.user.profile.firstName}} {{.user.profile.lastName}}"

Loops

Use range to iterate over arrays:

"items": [
  {{range $index, $item := .items}}
  {{if $index}},{{end}}
  {
    "id": {{$item.id}},
    "name": "{{$item.name}}"
  }
  {{end}}
]

Conditionals

Use if statements for conditional logic:

{{if .isAdmin}}
  "role": "administrator"
{{else}}
  "role": "user"
{{end}}

Advanced Features

Function Composition

Use the pipe operator (|) to chain operations:

"name": "{{.name | uppercase}}"
"description": "{{.description | trim | lowercase}}"

Creating New Data Structures

Create and manipulate arrays and objects:

{{$users := createSlice}}
{{range .members}}
  {{if eq .status "active"}}
    {{$users = append $users .name}}
  {{end}}
{{end}}
"activeUsers": [
  {{range $i, $name := $users}}
  {{if $i}},{{end}}
  "{{$name}}"
  {{end}}
]

Data Transformation

Group, filter, and transform data:

{{$categories := groupBy .products "category"}}
"categories": [
  {{range $i, $category := keys $categories}}
  {{if $i}},{{end}}
  {
    "name": "{{$category}}",
    "count": {{count (index $categories $category)}},
    "items": [
      {{range $j, $product := index $categories $category}}
      {{if $j}},{{end}}
      "{{$product.name}}"
      {{end}}
    ]
  }
  {{end}}
]

SGNL-to-PlainID Example

This transform converts SGNL authorization responses to PlainID format:

SGNL Auth Response Format

{
  "decisions": [
    {
      "action": "PlainID_Dev",
      "assetAttributes": {
        "app_id": "256",
        "approle_id": "1885",
        "permission_id": "4798",
        "permission_name": "Admin",
        "permission_status": "Released"
      },
      "assetId": "Admin",
      "decision": "Allow"
    },
    // More decisions...
  ],
  "evaluationDuration": 83,
  "issuedAt": "2025-04-18T05:26:29.412264083Z",
  "principalId": "user123@example.com"
}

Transformation Template

The following example shows how to transform SGNL authorization data to PlainID format using advanced template features:

{
          "tokenValidity": 0,
          "response": [
            {
              "access": [
                {{- /* Get unique asset IDs (paths) from decisions */ -}}
                {{- $uniqueAssetIds := createSlice -}}
                {{- range $i, $decision := .Decisions -}}
                  {{- if eq $decision.Decision "Allow" -}}
                    {{- $found := false -}}
                    {{- range $uniqueAssetIds -}}
                      {{- if eq . $decision.AssetID -}}
                        {{- $found = true -}}
                      {{- end -}}
                    {{- end -}}
                    {{- if not $found -}}
                      {{- $uniqueAssetIds = append $uniqueAssetIds $decision.AssetID -}}
                    {{- end -}}
                  {{- end -}}
                {{- end -}}
                
                {{- /* Loop through unique paths */ -}}
                {{- range $pathIdx, $path := $uniqueAssetIds -}}
                {{- if ne $pathIdx 0 }},{{ end }}
                {
                  "path": "{{ $path }}",
                  "attributes": {
                    "RoleName": [
                      "{{ $path }}"
                    ],
                    "IPControl_ID": [
                      0
                    ],
                    "PermissionName": [
                      {{- /* Find all permissions related to this path */ -}}
                      {{- $permissionFound := false -}}
                      {{- range $i, $decision := $.Decisions -}}
                        {{- if and (eq $decision.Decision "Allow") (eq $decision.AssetID $path) -}}
                          {{- if $permissionFound -}},{{- end -}}
                          {{- $permissionFound = true -}}
                          "{{ $decision.AssetAttributes.permission_name }}"
                        {{- end -}}
                      {{- end -}}
                      
                      {{- /* If no permissions found, use the path itself */ -}}
                      {{- if not $permissionFound -}}
                        "{{ $path }}"
                      {{- end -}}
                    ],
                    "UserID": [
                      "{{ $.PrincipalID }}"
                    ],
                    "EmployeeID": [
                      "{{ $.PrincipalID }}"
                    ],
                    "AppName": [
                      {{- /* Find the action/AppName for this path */ -}}
                      {{- $appName := "" -}}
                      {{- range $i, $decision := $.Decisions -}}
                        {{- if and (eq $decision.Decision "Allow") (eq $decision.AssetID $path) -}}
                          {{- $appName = $decision.Action -}}
                          {{- break -}}
                        {{- end -}}
                      {{- end -}}
                      "{{ $appName }}"
                    ]
                  }
                }
                {{- end }}
              ]
            }
          ]
        }

Explanation

This transformation demonstrates several advanced techniques:

  • Creating arrays with createSlice and append
  • Checking for duplicates with nested loops
  • Conditional output with if statements
  • Accessing data across nested scopes with $. prefix
  • Controlling whitespace with special syntax
  • Using comment blocks in templates

Function Reference

String Functions

  • uppercase - Convert string to uppercase
  • lowercase - Convert string to lowercase
  • trim - Remove whitespace
  • substring "hello" 0 5 - Extract part of a string
  • replace "hello world" "world" "there" - Replace a substring
  • split "a,b,c" "," - Split string into array
  • join array "-" - Join array to string
  • contains "hello world" "world" - Check if string contains pattern

Array Functions

  • createSlice - Create a new slice
  • append slice elem - Append to a slice
  • sort array - Sort an array
  • reverse array - Reverse an array
  • distinct array - Remove duplicates
  • count array - Count elements
  • filter array predicate - Select matching elements
  • map array function - Transform each element
  • flatten nested - Flatten nested arrays
  • slice array 1 3 - Extract subset of array
  • groupBy items "property" - Group by property
  • orderBy items "property" - Sort by properties

Object Functions

  • keys object - Get object keys
  • values object - Get object values
  • entries object - Convert to key-value pairs
  • merge obj1 obj2 - Combine objects
  • pick obj "key1" "key2" - Select properties
  • omit obj "key" - Remove properties

Numeric Functions

  • add x y - Add numbers
  • sub x y - Subtract numbers
  • mul x y - Multiply numbers
  • div x y - Divide numbers
  • abs -42 - Absolute value
  • floor 3.7 - Round down
  • ceil 3.2 - Round up
  • round 3.14159 2 - Round to precision

Conversion Functions

  • toJSON obj - Convert to JSON string
  • fromJSON str - Parse JSON string