github.com/instill-ai/component@v0.16.0-beta/pkg/connector/numbers/v0/main.go (about) 1 //go:generate compogen readme --connector ./config ./README.mdx 2 package numbers 3 4 import ( 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "io" 9 "mime/multipart" 10 "net/http" 11 "sync" 12 13 _ "embed" 14 b64 "encoding/base64" 15 16 "github.com/gabriel-vasile/mimetype" 17 "github.com/gofrs/uuid" 18 "go.uber.org/zap" 19 "google.golang.org/protobuf/types/known/structpb" 20 21 "github.com/instill-ai/component/pkg/base" 22 ) 23 24 const urlRegisterAsset = "https://api.numbersprotocol.io/api/v3/assets/" 25 const urlUserMe = "https://api.numbersprotocol.io/api/v3/auth/users/me" 26 27 var once sync.Once 28 var con *connector 29 30 //go:embed config/definition.json 31 var definitionJSON []byte 32 33 //go:embed config/tasks.json 34 var tasksJSON []byte 35 36 type connector struct { 37 base.BaseConnector 38 } 39 40 type execution struct { 41 base.BaseConnectorExecution 42 } 43 44 type CommitCustomLicense struct { 45 Name *string `json:"name,omitempty"` 46 Document *string `json:"document,omitempty"` 47 } 48 type CommitCustom struct { 49 AssetCreator *string `json:"assetCreator,omitempty"` 50 DigitalSourceType *string `json:"digitalSourceType,omitempty"` 51 MiningPreference *string `json:"miningPreference,omitempty"` 52 GeneratedThrough string `json:"generatedThrough"` 53 GeneratedBy *string `json:"generatedBy,omitempty"` 54 CreatorWallet *string `json:"creatorWallet,omitempty"` 55 License *CommitCustomLicense `json:"license,omitempty"` 56 Metadata *CommitCustomMetadata `json:"instillMetadata,omitempty"` 57 } 58 59 type CommitCustomMetadata struct { 60 Pipeline struct { 61 UID string 62 Recipe interface{} 63 } 64 } 65 66 type Meta struct { 67 Proof struct { 68 Hash string `json:"hash"` 69 MIMEType string `json:"mimeType"` 70 Timestamp string `json:"timestamp"` 71 } `json:"proof"` 72 } 73 74 type Register struct { 75 Caption *string `json:"caption,omitempty"` 76 Headline *string `json:"headline,omitempty"` 77 NITCommitCustom *CommitCustom `json:"nit_commit_custom,omitempty"` 78 Meta 79 } 80 81 type Input struct { 82 Images []string `json:"images"` 83 AssetCreator *string `json:"asset_creator,omitempty"` 84 Caption *string `json:"caption,omitempty"` 85 Headline *string `json:"headline,omitempty"` 86 DigitalSourceType *string `json:"digital_source_type,omitempty"` 87 MiningPreference *string `json:"mining_preference,omitempty"` 88 GeneratedBy *string `json:"generated_by,omitempty"` 89 License *struct { 90 Name *string `json:"name,omitempty"` 91 Document *string `json:"document,omitempty"` 92 } `json:"license,omitempty"` 93 } 94 95 type Output struct { 96 AssetUrls []string `json:"asset_urls"` 97 } 98 99 func Init(l *zap.Logger, u base.UsageHandler) *connector { 100 once.Do(func() { 101 con = &connector{ 102 BaseConnector: base.BaseConnector{ 103 Logger: l, 104 UsageHandler: u, 105 }, 106 } 107 err := con.LoadConnectorDefinition(definitionJSON, tasksJSON, nil) 108 if err != nil { 109 panic(err) 110 } 111 }) 112 return con 113 } 114 115 func (c *connector) CreateExecution(sysVars map[string]any, connection *structpb.Struct, task string) (*base.ExecutionWrapper, error) { 116 return &base.ExecutionWrapper{Execution: &execution{ 117 BaseConnectorExecution: base.BaseConnectorExecution{Connector: c, SystemVariables: sysVars, Connection: connection, Task: task}, 118 }}, nil 119 } 120 121 func getToken(config *structpb.Struct) string { 122 return fmt.Sprintf("token %s", config.GetFields()["capture_token"].GetStringValue()) 123 } 124 125 func (e *execution) registerAsset(data []byte, reg Register) (string, error) { 126 127 var b bytes.Buffer 128 129 w := multipart.NewWriter(&b) 130 var fw io.Writer 131 var err error 132 133 fileName, _ := uuid.NewV4() 134 if fw, err = w.CreateFormFile("asset_file", fileName.String()+mimetype.Detect(data).Extension()); err != nil { 135 return "", err 136 } 137 138 if _, err := io.Copy(fw, bytes.NewReader(data)); err != nil { 139 return "", err 140 } 141 142 if reg.Caption != nil { 143 _ = w.WriteField("caption", *reg.Caption) 144 } 145 146 if reg.Headline != nil { 147 _ = w.WriteField("headline", *reg.Headline) 148 } 149 150 if reg.NITCommitCustom != nil { 151 commitBytes, _ := json.Marshal(*reg.NITCommitCustom) 152 _ = w.WriteField("nit_commit_custom", string(commitBytes)) 153 } 154 metaBytes, _ := json.Marshal(Meta{}) 155 _ = w.WriteField("meta", string(metaBytes)) 156 157 w.Close() 158 159 req, err := http.NewRequest("POST", urlRegisterAsset, &b) 160 if err != nil { 161 return "", err 162 } 163 req.Header.Set("Content-Type", w.FormDataContentType()) 164 req.Header.Set("Authorization", getToken(e.Connection)) 165 166 tr := &http.Transport{ 167 DisableKeepAlives: true, 168 } 169 client := &http.Client{Transport: tr} 170 res, err := client.Do(req) 171 if res != nil && res.Body != nil { 172 defer res.Body.Close() 173 } 174 if err != nil { 175 return "", err 176 } 177 178 if res.StatusCode == http.StatusCreated { 179 bodyBytes, err := io.ReadAll(res.Body) 180 if err != nil { 181 return "", err 182 } 183 var jsonRes map[string]interface{} 184 _ = json.Unmarshal(bodyBytes, &jsonRes) 185 if cid, ok := jsonRes["cid"]; ok { 186 return cid.(string), nil 187 } else { 188 return "", fmt.Errorf("register file failed") 189 } 190 191 } else { 192 bodyBytes, err := io.ReadAll(res.Body) 193 if err != nil { 194 return "", err 195 } 196 return "", fmt.Errorf(string(bodyBytes)) 197 } 198 } 199 200 func (e *execution) Execute(inputs []*structpb.Struct) ([]*structpb.Struct, error) { 201 202 var outputs []*structpb.Struct 203 204 for _, input := range inputs { 205 206 assetUrls := []string{} 207 208 inputStruct := Input{} 209 err := base.ConvertFromStructpb(input, &inputStruct) 210 if err != nil { 211 return nil, err 212 } 213 214 for _, image := range inputStruct.Images { 215 imageBytes, err := b64.StdEncoding.DecodeString(base.TrimBase64Mime(image)) 216 if err != nil { 217 return nil, err 218 } 219 220 var commitLicense *CommitCustomLicense 221 if inputStruct.License != nil { 222 commitLicense = &CommitCustomLicense{ 223 Name: inputStruct.License.Name, 224 Document: inputStruct.License.Document, 225 } 226 } 227 228 meta := CommitCustomMetadata{ 229 Pipeline: struct { 230 UID string 231 Recipe interface{} 232 }{ 233 UID: e.GetSystemVariables()["__PIPELINE_UID"].(string), 234 Recipe: e.GetSystemVariables()["__PIPELINE_RECIPE"], 235 }, 236 } 237 commitCustom := CommitCustom{ 238 AssetCreator: inputStruct.AssetCreator, 239 DigitalSourceType: inputStruct.DigitalSourceType, 240 MiningPreference: inputStruct.MiningPreference, 241 GeneratedThrough: "https://instill.tech", //TODO: support Core Host 242 GeneratedBy: inputStruct.GeneratedBy, 243 License: commitLicense, 244 Metadata: &meta, 245 } 246 247 reg := Register{ 248 Caption: inputStruct.Caption, 249 Headline: inputStruct.Headline, 250 NITCommitCustom: &commitCustom, 251 } 252 assetCid, err := e.registerAsset(imageBytes, reg) 253 if err != nil { 254 return nil, err 255 } 256 257 assetUrls = append(assetUrls, fmt.Sprintf("https://verify.numbersprotocol.io/asset-profile?nid=%s", assetCid)) 258 } 259 260 outputStruct := Output{ 261 AssetUrls: assetUrls, 262 } 263 264 output, err := base.ConvertToStructpb(outputStruct) 265 if err != nil { 266 return nil, err 267 } 268 outputs = append(outputs, output) 269 270 } 271 272 return outputs, nil 273 274 } 275 276 func (c *connector) Test(sysVars map[string]any, connection *structpb.Struct) error { 277 278 req, err := http.NewRequest("GET", urlUserMe, nil) 279 if err != nil { 280 return err 281 } 282 req.Header.Set("Authorization", getToken(connection)) 283 284 tr := &http.Transport{ 285 DisableKeepAlives: true, 286 } 287 client := &http.Client{Transport: tr} 288 res, err := client.Do(req) 289 if res != nil && res.Body != nil { 290 defer res.Body.Close() 291 } 292 if err != nil { 293 return err 294 } 295 if res.StatusCode == http.StatusOK { 296 return fmt.Errorf("connection error") 297 } 298 return nil 299 }