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  }