@@ -3,6 +3,7 @@ package core
33import (
44 "bytes"
55 "database/sql"
6+ "encoding/json"
67 "errors"
78 "fmt"
89 "net"
@@ -176,10 +177,108 @@ func indentString(input string, indentSpaces int, numbering bool) string {
176177 return strings .Join (lines , "\n " )
177178}
178179
180+ const (
181+ // web-safe hex equivalents for the frontend
182+ WebColorRed = "#FF5555" // for color.FgRed
183+ WebColorYellow = "#FFFF55" // for color.FgYellow
184+ WebColorCyan = "#8BE9FD" // for color.FgCyan
185+ WebColorMagenta = "#FF79C6" // for color.FgMagenta
186+ )
187+
188+ type CtxMsg struct {
189+ Message string `json:"msg"`
190+ Depth int `json:"level"`
191+ FullPath string `json:"fullPath"`
192+ }
193+
194+ type WebErrorPayload struct {
195+ IsLeafError bool `json:"isLeafError"`
196+ Context []CtxMsg `json:"context,omitempty"`
197+
198+ Error string `json:"error"`
199+ ErrorColor string `json:"errorColor"`
200+
201+ Hint string `json:"hint,omitempty"`
202+ HintColor string `json:"hintColor,omitempty"`
203+
204+ StackTrace []string `json:"stackTrace,omitempty"`
205+ }
206+
179207func (e * LeafError ) Format (f fmt.State , c rune ) {
180208 switch c {
181209 case 'v' :
182210
211+ // Below is the JSON output formatting for the web editor console
212+ if f .Flag ('#' ) {
213+ payload := WebErrorPayload {
214+ IsLeafError : true ,
215+ Error : e .ErrorWithCauses (),
216+ ErrorColor : "#FF5555" ,
217+ }
218+
219+ if e .Context != nil && len (e .Context .Visited ) > 0 {
220+ var previousNode NodeBaseInterface
221+ for _ , item := range e .Context .Visited {
222+ currentNode := item .Node
223+
224+ // TODO: (Seb) improve this
225+ // In the logs, group nodes appear twice, once when entered, and once
226+ // when the group-outputs node comes back and executes the group node again.
227+ // While this is expected for the execution flow, we don't want to have that
228+ // in the logs, it looks very confusing.
229+ if previousNode != nil &&
230+ strings .HasPrefix (previousNode .GetNodeTypeId (), "core/group-outputs@" ) &&
231+ strings .HasPrefix (currentNode .GetNodeTypeId (), "core/group@" ) {
232+
233+ previousNode = currentNode
234+ continue
235+ }
236+
237+ nodeNameOrLabel := currentNode .GetLabel ()
238+ if nodeNameOrLabel == "" {
239+ nodeNameOrLabel = currentNode .GetName ()
240+ }
241+
242+ var msg string
243+ if item .Execute {
244+ msg = fmt .Sprintf ("execute '%s'" , nodeNameOrLabel )
245+ } else {
246+ msg = fmt .Sprintf ("request input from '%s'" , nodeNameOrLabel )
247+ }
248+
249+ payload .Context = append (payload .Context , CtxMsg {
250+ Message : msg ,
251+ Depth : strings .Count (currentNode .GetFullPath (), "/" ) + 1 ,
252+ FullPath : currentNode .GetFullPath (),
253+ })
254+
255+ previousNode = item .Node
256+ }
257+ }
258+
259+ payload .Hint = getErrorHint (e )
260+ if payload .Hint != "" {
261+ payload .HintColor = "#FFFF55"
262+ }
263+
264+ if f .Flag ('+' ) {
265+ rawStack := e .StackTrace ()
266+ payload .StackTrace = strings .Split (rawStack , "\n " )
267+ }
268+
269+ jsonBytes , err := json .Marshal (payload )
270+ if err != nil {
271+ // Fallback in case of JSON error
272+ fmt .Fprint (f , e .Error ())
273+ return
274+ }
275+
276+ fmt .Fprint (f , string (jsonBytes ))
277+ return
278+ }
279+
280+ // This below is the regular terminal output formatting
281+
183282 var (
184283 tmpErrEmoji string
185284 tmpHintEmoji string
@@ -245,6 +344,7 @@ func (e *LeafError) Format(f fmt.State, c rune) {
245344
246345 fmt .Fprint (f , output )
247346 return
347+
248348 case 's' :
249349 fmt .Fprint (f , e .Error ())
250350 }
0 commit comments