Created
November 24, 2023 14:50
-
-
Save Zenithar/e8953f0cf046b7afadb066d0cf3ff696 to your computer and use it in GitHub Desktop.
Transform JSON with GO
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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