Skip to content

Instantly share code, notes, and snippets.

@Zenithar
Created November 24, 2023 14:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zenithar/e8953f0cf046b7afadb066d0cf3ff696 to your computer and use it in GitHub Desktop.
Save Zenithar/e8953f0cf046b7afadb066d0cf3ff696 to your computer and use it in GitHub Desktop.
Transform JSON with GO
package main
import (
"bufio"
"encoding/json"
"fmt"
"io"
"os"
"time"
"github.com/robertkrimen/otto"
"github.com/robertkrimen/otto/parser"
)
const (
maxInputSize = 1024 * 1024 // 1MB
inputScript = `function transform(input) {
input["hello"] = "world";
return input;
}`
)
func main() {
if err := run(); err != nil {
panic(err)
}
}
func run() error {
// Create a new VM instance
vm := otto.New()
vm.Interrupt = make(chan func(), 1)
// Parse the script
program, err := parser.ParseFile(nil, "", inputScript, 0)
if err != nil {
return fmt.Errorf("failed to parse script: %w", err)
}
if _, err := vm.Eval(program); err != nil {
return fmt.Errorf("failed to eval script: %w", err)
}
// Drain stdin as input for the script
payload, err := io.ReadAll(io.LimitReader(bufio.NewReader(os.Stdin), maxInputSize+1))
if err != nil {
return fmt.Errorf("failed to read input: %w", err)
}
if len(payload) > maxInputSize {
return fmt.Errorf("input too large")
}
// Parse the input as JSON
// If the input type is a known type, you should use a struct instead with
// strict decoding to prevent unexpected fields.
var input map[string]any
if err := json.Unmarshal(payload, &input); err != nil {
return fmt.Errorf("failed to parse input: %w", err)
}
// Set the input variable
if err := vm.Set("input", input); err != nil {
return fmt.Errorf("failed to set input: %w", err)
}
// Kill the VM is it takes too long
go func() {
time.Sleep(5 * time.Second)
vm.Interrupt <- func() {
panic("interrupted")
}
}()
// Run the script
out, err := vm.Run(`transform(input)`)
if err != nil {
return fmt.Errorf("failed to run script: %w", err)
}
// Print the result
result, err := out.Export()
if err != nil {
return fmt.Errorf("failed to export result: %w", err)
}
// Encode the result as JSON
if err := json.NewEncoder(os.Stdout).Encode(result); err != nil {
return fmt.Errorf("failed to encode result: %w", err)
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment