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  }