github.com/instill-ai/component@v0.16.0-beta/pkg/base/formats.go (about) 1 package base 2 3 import ( 4 "encoding/base64" 5 "strings" 6 7 "github.com/gabriel-vasile/mimetype" 8 "github.com/santhosh-tekuri/jsonschema/v5" 9 "google.golang.org/protobuf/types/known/structpb" 10 ) 11 12 type InstillAcceptFormatsCompiler struct{} 13 14 func (InstillAcceptFormatsCompiler) Compile(ctx jsonschema.CompilerContext, m map[string]interface{}) (jsonschema.ExtSchema, error) { 15 if instillAcceptFormats, ok := m["instillAcceptFormats"]; ok { 16 17 formats := []string{} 18 for _, instillAcceptFormat := range instillAcceptFormats.([]interface{}) { 19 formats = append(formats, instillAcceptFormat.(string)) 20 } 21 return InstillAcceptFormatsSchema(formats), nil 22 } 23 24 return nil, nil 25 } 26 27 type InstillAcceptFormatsSchema []string 28 29 func (s InstillAcceptFormatsSchema) Validate(ctx jsonschema.ValidationContext, v interface{}) error { 30 31 // TODO: We should design a better approach to validate the Base64 data. 32 33 switch v := v.(type) { 34 35 case string: 36 mimeType := "" 37 for _, instillAcceptFormat := range s { 38 39 switch { 40 case instillAcceptFormat == "string", 41 instillAcceptFormat == "*", 42 instillAcceptFormat == "*/*", 43 strings.HasPrefix(instillAcceptFormat, "semi-structured"), 44 strings.HasPrefix(instillAcceptFormat, "structured"): 45 return nil 46 47 // For other types, we assume they are Base64 strings and need to validate the Base64 encoding. 48 default: 49 50 b, err := base64.StdEncoding.DecodeString(TrimBase64Mime(v)) 51 if err != nil { 52 return ctx.Error("instillAcceptFormats", "can not decode file") 53 } 54 55 mimeType = strings.Split(mimetype.Detect(b).String(), ";")[0] 56 if strings.Split(mimeType, "/")[0] == strings.Split(instillAcceptFormat, "/")[0] && strings.Split(instillAcceptFormat, "/")[1] == "*" { 57 return nil 58 } else if mimeType == instillAcceptFormat { 59 return nil 60 } 61 } 62 } 63 return ctx.Error("instillAcceptFormats", "expected one of %v, but got %s", s, mimeType) 64 65 default: 66 return nil 67 } 68 } 69 70 var InstillAcceptFormatsMeta = jsonschema.MustCompileString("instillAcceptFormats.json", `{ 71 "properties" : { 72 "instillAcceptFormats": { 73 "type": "array", 74 "items": { 75 "type": "string" 76 } 77 } 78 } 79 }`) 80 81 type InstillFormatCompiler struct{} 82 83 func (InstillFormatCompiler) Compile(ctx jsonschema.CompilerContext, m map[string]interface{}) (jsonschema.ExtSchema, error) { 84 if _, ok := m["instillFormat"]; ok { 85 86 return InstillFormatSchema(m["instillFormat"].(string)), nil 87 } 88 89 return nil, nil 90 } 91 92 type InstillFormatSchema string 93 94 func (s InstillFormatSchema) Validate(ctx jsonschema.ValidationContext, v interface{}) error { 95 96 // TODO: We should design a better approach to validate the Base64 data. 97 switch v := v.(type) { 98 99 case string: 100 switch { 101 case s == "string", 102 s == "*", 103 s == "*/*", 104 strings.HasPrefix(string(s), "semi-structured"), 105 strings.HasPrefix(string(s), "structured"): 106 return nil 107 108 // For other types, we assume they are Base64 strings and need to validate the Base64 encoding. 109 default: 110 mimeType := "" 111 if !strings.HasPrefix(v, "data:") { 112 b, err := base64.StdEncoding.DecodeString(TrimBase64Mime(v)) 113 if err != nil { 114 return ctx.Error("instillFormat", "can not decode file") 115 } 116 mimeType = strings.Split(mimetype.Detect(b).String(), ";")[0] 117 } else { 118 mimeType = strings.Split(strings.Split(v, ";")[0], ":")[1] 119 } 120 121 if strings.Split(mimeType, "/")[0] == strings.Split(string(s), "/")[0] && strings.Split(string(s), "/")[1] == "*" { 122 return nil 123 } else if mimeType == string(s) { 124 return nil 125 } else { 126 return ctx.Error("instillFormat", "expected %v, but got %s", s, mimeType) 127 } 128 129 } 130 131 default: 132 return nil 133 } 134 } 135 136 var InstillFormatMeta = jsonschema.MustCompileString("instillFormat.json", `{ 137 "properties" : { 138 "instillFormat": { 139 "type": "string" 140 } 141 } 142 }`) 143 144 func CompileInstillAcceptFormats(sch *structpb.Struct) error { 145 var err error 146 for k, v := range sch.Fields { 147 if v.GetStructValue() != nil { 148 err = CompileInstillAcceptFormats(v.GetStructValue()) 149 if err != nil { 150 return err 151 } 152 } 153 if k == "instillAcceptFormats" { 154 itemInstillAcceptFormats := []interface{}{} 155 for _, item := range v.GetListValue().AsSlice() { 156 if strings.HasPrefix(item.(string), "array:") { 157 itemInstillAcceptFormats = append(itemInstillAcceptFormats, strings.Split(item.(string), ":")[1]) 158 } 159 } 160 if len(itemInstillAcceptFormats) > 0 { 161 sch.Fields["items"].GetStructValue().Fields["instillAcceptFormats"], err = structpb.NewValue(itemInstillAcceptFormats) 162 if err != nil { 163 return err 164 } 165 } 166 } 167 168 } 169 return nil 170 } 171 172 func CompileInstillFormat(sch *structpb.Struct) error { 173 var err error 174 for k, v := range sch.Fields { 175 if v.GetStructValue() != nil { 176 err = CompileInstillFormat(v.GetStructValue()) 177 if err != nil { 178 return err 179 } 180 } 181 if k == "instillFormat" { 182 if strings.HasPrefix(v.GetStringValue(), "array:") { 183 itemInstillFormat := strings.Split(v.GetStringValue(), ":")[1] 184 sch.Fields["items"].GetStructValue().Fields["instillFormat"], err = structpb.NewValue(itemInstillFormat) 185 if err != nil { 186 return err 187 } 188 } 189 } 190 191 } 192 return nil 193 } 194 195 func TrimBase64Mime(b64 string) string { 196 splitB64 := strings.Split(b64, ",") 197 return splitB64[len(splitB64)-1] 198 }