github.com/instill-ai/component@v0.16.0-beta/pkg/connector/restapi/v0/main.go (about) 1 //go:generate compogen readme --connector ./config ./README.mdx 2 package restapi 3 4 import ( 5 _ "embed" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "net/http" 10 "sync" 11 12 "github.com/instill-ai/component/pkg/base" 13 "github.com/instill-ai/x/errmsg" 14 "go.uber.org/zap" 15 "google.golang.org/protobuf/proto" 16 "google.golang.org/protobuf/types/known/structpb" 17 18 pipelinePB "github.com/instill-ai/protogen-go/vdp/pipeline/v1beta" 19 ) 20 21 const ( 22 taskGet = "TASK_GET" 23 taskPost = "TASK_POST" 24 taskPatch = "TASK_PATCH" 25 taskPut = "TASK_PUT" 26 taskDelete = "TASK_DELETE" 27 taskHead = "TASK_HEAD" 28 taskOptions = "TASK_OPTIONS" 29 ) 30 31 var ( 32 //go:embed config/definition.json 33 definitionJSON []byte 34 35 //go:embed config/tasks.json 36 tasksJSON []byte 37 38 once sync.Once 39 con *connector 40 41 taskMethod = map[string]string{ 42 taskGet: http.MethodGet, 43 taskPost: http.MethodPost, 44 taskPatch: http.MethodPatch, 45 taskPut: http.MethodPut, 46 taskDelete: http.MethodDelete, 47 taskHead: http.MethodHead, 48 taskOptions: http.MethodOptions, 49 } 50 ) 51 52 type connector struct { 53 base.BaseConnector 54 } 55 56 type execution struct { 57 base.BaseConnectorExecution 58 } 59 60 func Init(l *zap.Logger, u base.UsageHandler) *connector { 61 once.Do(func() { 62 con = &connector{ 63 BaseConnector: base.BaseConnector{ 64 Logger: l, 65 UsageHandler: u, 66 }, 67 } 68 err := con.LoadConnectorDefinition(definitionJSON, tasksJSON, nil) 69 if err != nil { 70 panic(err) 71 } 72 }) 73 return con 74 } 75 76 func (c *connector) CreateExecution(sysVars map[string]any, connection *structpb.Struct, task string) (*base.ExecutionWrapper, error) { 77 return &base.ExecutionWrapper{Execution: &execution{ 78 BaseConnectorExecution: base.BaseConnectorExecution{Connector: c, SystemVariables: sysVars, Connection: connection, Task: task}, 79 }}, nil 80 } 81 82 func getAuthentication(config *structpb.Struct) (authentication, error) { 83 auth := config.GetFields()["authentication"].GetStructValue() 84 authType := auth.GetFields()["auth_type"].GetStringValue() 85 86 switch authType { 87 case string(noAuthType): 88 authStruct := noAuth{} 89 err := base.ConvertFromStructpb(auth, &authStruct) 90 if err != nil { 91 return nil, err 92 } 93 return authStruct, nil 94 case string(basicAuthType): 95 authStruct := basicAuth{} 96 err := base.ConvertFromStructpb(auth, &authStruct) 97 if err != nil { 98 return nil, err 99 } 100 return authStruct, nil 101 case string(apiKeyType): 102 authStruct := apiKeyAuth{} 103 err := base.ConvertFromStructpb(auth, &authStruct) 104 if err != nil { 105 return nil, err 106 } 107 return authStruct, nil 108 case string(bearerTokenType): 109 authStruct := bearerTokenAuth{} 110 err := base.ConvertFromStructpb(auth, &authStruct) 111 if err != nil { 112 return nil, err 113 } 114 return authStruct, nil 115 default: 116 return nil, errors.New("invalid authentication type") 117 } 118 } 119 120 func (e *execution) Execute(inputs []*structpb.Struct) ([]*structpb.Struct, error) { 121 122 method, ok := taskMethod[e.Task] 123 if !ok { 124 return nil, errmsg.AddMessage( 125 fmt.Errorf("not supported task: %s", e.Task), 126 fmt.Sprintf("%s task is not supported.", e.Task), 127 ) 128 } 129 130 outputs := []*structpb.Struct{} 131 for _, input := range inputs { 132 taskIn := TaskInput{} 133 taskOut := TaskOutput{} 134 135 if err := base.ConvertFromStructpb(input, &taskIn); err != nil { 136 return nil, err 137 } 138 139 // We may have different url in batch. 140 client, err := newClient(e.Connection, e.GetLogger()) 141 if err != nil { 142 return nil, err 143 } 144 145 // An API error is a valid output in this connector. 146 req := client.R().SetResult(&taskOut.Body).SetError(&taskOut.Body) 147 if taskIn.Body != nil { 148 req.SetBody(taskIn.Body) 149 } 150 151 resp, err := req.Execute(method, taskIn.EndpointURL) 152 if err != nil { 153 return nil, err 154 } 155 156 taskOut.StatusCode = resp.StatusCode() 157 taskOut.Header = resp.Header() 158 159 output, err := base.ConvertToStructpb(taskOut) 160 if err != nil { 161 return nil, err 162 } 163 164 outputs = append(outputs, output) 165 } 166 return outputs, nil 167 } 168 169 func (c *connector) Test(sysVars map[string]any, connection *structpb.Struct) error { 170 // we don't need to validate the connection since no url setting here 171 return nil 172 } 173 174 // Generate the model_name enum based on the task 175 func (c *connector) GetConnectorDefinition(sysVars map[string]any, component *pipelinePB.ConnectorComponent) (*pipelinePB.ConnectorDefinition, error) { 176 oriDef, err := c.BaseConnector.GetConnectorDefinition(nil, nil) 177 if err != nil { 178 return nil, err 179 } 180 181 def := proto.Clone(oriDef).(*pipelinePB.ConnectorDefinition) 182 if component == nil { 183 return def, nil 184 } 185 if component.Task == "" { 186 return def, nil 187 } 188 if _, ok := component.Input.Fields["output_body_schema"]; !ok { 189 return def, nil 190 } 191 192 schStr := component.Input.Fields["output_body_schema"].GetStringValue() 193 sch := &structpb.Struct{} 194 _ = json.Unmarshal([]byte(schStr), sch) 195 spec := def.Spec.DataSpecifications[component.Task] 196 spec.Output = sch 197 return def, nil 198 }