github.com/instill-ai/component@v0.16.0-beta/pkg/operator/json/v0/main.go (about) 1 //go:generate compogen readme --operator ./config ./README.mdx 2 package json 3 4 import ( 5 _ "embed" 6 "encoding/json" 7 "fmt" 8 "sync" 9 10 "github.com/itchyny/gojq" 11 "go.uber.org/zap" 12 "google.golang.org/protobuf/encoding/protojson" 13 "google.golang.org/protobuf/types/known/structpb" 14 15 "github.com/instill-ai/component/pkg/base" 16 "github.com/instill-ai/x/errmsg" 17 ) 18 19 const ( 20 taskMarshal = "TASK_MARSHAL" 21 taskUnmarshal = "TASK_UNMARSHAL" 22 taskJQ = "TASK_JQ" 23 ) 24 25 var ( 26 //go:embed config/definition.json 27 definitionJSON []byte 28 //go:embed config/tasks.json 29 tasksJSON []byte 30 31 once sync.Once 32 op *operator 33 ) 34 35 type operator struct { 36 base.BaseOperator 37 } 38 39 type execution struct { 40 base.BaseOperatorExecution 41 42 execute func(*structpb.Struct) (*structpb.Struct, error) 43 } 44 45 // Init returns an implementation of IOperator that processes JSON objects. 46 func Init(l *zap.Logger, u base.UsageHandler) *operator { 47 once.Do(func() { 48 op = &operator{ 49 BaseOperator: base.BaseOperator{ 50 Logger: l, 51 UsageHandler: u, 52 }, 53 } 54 err := op.LoadOperatorDefinition(definitionJSON, tasksJSON, nil) 55 if err != nil { 56 panic(err) 57 } 58 }) 59 return op 60 } 61 62 func (o *operator) CreateExecution(sysVars map[string]any, task string) (*base.ExecutionWrapper, error) { 63 e := &execution{ 64 BaseOperatorExecution: base.BaseOperatorExecution{Operator: o, SystemVariables: sysVars, Task: task}, 65 } 66 67 switch task { 68 case taskMarshal: 69 e.execute = e.marshal 70 case taskUnmarshal: 71 e.execute = e.unmarshal 72 case taskJQ: 73 e.execute = e.jq 74 default: 75 return nil, errmsg.AddMessage( 76 fmt.Errorf("not supported task: %s", task), 77 fmt.Sprintf("%s task is not supported.", task), 78 ) 79 } 80 return &base.ExecutionWrapper{Execution: e}, nil 81 } 82 83 func (e *execution) marshal(in *structpb.Struct) (*structpb.Struct, error) { 84 out := new(structpb.Struct) 85 86 b, err := protojson.Marshal(in.Fields["json"]) 87 if err != nil { 88 return nil, errmsg.AddMessage(err, "Couldn't convert the provided object to JSON.") 89 } 90 91 out.Fields = map[string]*structpb.Value{ 92 "string": structpb.NewStringValue(string(b)), 93 } 94 95 return out, nil 96 } 97 98 func (e *execution) unmarshal(in *structpb.Struct) (*structpb.Struct, error) { 99 out := new(structpb.Struct) 100 101 b := []byte(in.Fields["string"].GetStringValue()) 102 obj := new(structpb.Struct) 103 if err := protojson.Unmarshal(b, obj); err != nil { 104 return nil, errmsg.AddMessage(err, "Couldn't parse the JSON string. Please check the syntax is correct.") 105 } 106 107 out.Fields = map[string]*structpb.Value{ 108 "json": structpb.NewStructValue(obj), 109 } 110 111 return out, nil 112 } 113 114 func (e *execution) jq(in *structpb.Struct) (*structpb.Struct, error) { 115 out := new(structpb.Struct) 116 117 b := []byte(in.Fields["jsonInput"].GetStringValue()) 118 var input any 119 if err := json.Unmarshal(b, &input); err != nil { 120 return nil, errmsg.AddMessage(err, "Couldn't parse the JSON input. Please check the syntax is correct.") 121 } 122 123 queryStr := in.Fields["jqFilter"].GetStringValue() 124 q, err := gojq.Parse(queryStr) 125 if err != nil { 126 // Error messages from gojq are human-friendly enough. 127 msg := fmt.Sprintf("Couldn't parse the jq filter: %s. Please check the syntax is correct.", err.Error()) 128 return nil, errmsg.AddMessage(err, msg) 129 } 130 131 results := []any{} 132 iter := q.Run(input) 133 for { 134 v, ok := iter.Next() 135 if !ok { 136 break 137 } 138 139 if err, ok := v.(error); ok { 140 msg := fmt.Sprintf("Couldn't apply the jq filter: %s.", err.Error()) 141 return nil, errmsg.AddMessage(err, msg) 142 } 143 144 results = append(results, v) 145 } 146 147 list, err := structpb.NewList(results) 148 if err != nil { 149 return nil, err 150 } 151 152 out.Fields = map[string]*structpb.Value{ 153 "results": structpb.NewListValue(list), 154 } 155 156 return out, nil 157 } 158 159 func (e *execution) Execute(inputs []*structpb.Struct) ([]*structpb.Struct, error) { 160 outputs := make([]*structpb.Struct, len(inputs)) 161 162 for i, input := range inputs { 163 output, err := e.execute(input) 164 if err != nil { 165 return nil, err 166 } 167 168 outputs[i] = output 169 } 170 171 return outputs, nil 172 }