|
@@ -0,0 +1,184 @@
|
|
|
+package yaml_checker
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "gopkg.in/yaml.v3"
|
|
|
+ "os"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ schemaNodeTypeObject = "object"
|
|
|
+ schemaNodeTypeField = "field"
|
|
|
+)
|
|
|
+
|
|
|
+type SchemaDoc struct {
|
|
|
+ rootSchemas []SchemaNode
|
|
|
+}
|
|
|
+
|
|
|
+type SchemaDocYaml struct {
|
|
|
+ RootSchemas []SchemaYaml `yaml:"schema"`
|
|
|
+}
|
|
|
+
|
|
|
+type SchemaYaml struct {
|
|
|
+ Type string `yaml:"type"`
|
|
|
+ Name string `yaml:"name"`
|
|
|
+ Describe string `yaml:"describe"`
|
|
|
+ Required bool `yaml:"required"`
|
|
|
+ AsArray bool `yaml:"as_array"`
|
|
|
+
|
|
|
+
|
|
|
+ Fields []SchemaYaml `yaml:"fields"`
|
|
|
+
|
|
|
+
|
|
|
+ FieldType string `yaml:"field_type"`
|
|
|
+}
|
|
|
+
|
|
|
+func NewSchemaDoc(schemaFilePath string) (*SchemaDoc, error) {
|
|
|
+ if isStringEmpty(schemaFilePath) || !pathExists(schemaFilePath) {
|
|
|
+ return nil, errors.New("schema文件不存在")
|
|
|
+ }
|
|
|
+
|
|
|
+ validatorFileBytes, err := os.ReadFile(schemaFilePath)
|
|
|
+ if err != nil {
|
|
|
+ return nil, errors.New("读取schema文件错误: " + err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ schemaDocYaml := new(SchemaDocYaml)
|
|
|
+ err = yaml.Unmarshal(validatorFileBytes, schemaDocYaml)
|
|
|
+ if err != nil {
|
|
|
+ return nil, errors.New("schema文件unmarshal错误: " + err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ if schemaDocYaml.RootSchemas == nil || len(schemaDocYaml.RootSchemas) == 0 {
|
|
|
+ return nil, errors.New("schema文件结构错误: 没有根节点")
|
|
|
+ }
|
|
|
+
|
|
|
+ doc := new(SchemaDoc)
|
|
|
+ for _, rootSchema := range schemaDocYaml.RootSchemas {
|
|
|
+ rootSchemaNode, err := newSchemaNode(&rootSchema)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ doc.rootSchemas = append(doc.rootSchemas, rootSchemaNode)
|
|
|
+ }
|
|
|
+
|
|
|
+ return doc, nil
|
|
|
+}
|
|
|
+
|
|
|
+func newSchemaNode(schemaNodeYaml *SchemaYaml) (SchemaNode, error) {
|
|
|
+ switch schemaNodeYaml.Type {
|
|
|
+ case schemaNodeTypeObject:
|
|
|
+ return newObjectSchemaNode(schemaNodeYaml)
|
|
|
+ case schemaNodeTypeField:
|
|
|
+ return newFieldSchemaNode(schemaNodeYaml)
|
|
|
+ default:
|
|
|
+ return nil, errors.New("不支持的schema节点类型")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func newObjectSchemaNode(schemaNodeYaml *SchemaYaml) (*ObjectSchemaNode, error) {
|
|
|
+ if schemaNodeYaml.Type != schemaNodeTypeObject {
|
|
|
+ return nil, errors.New("schema节点类型错误: 期望类型" + schemaNodeTypeObject + " 实际类型" + schemaNodeYaml.Type)
|
|
|
+ }
|
|
|
+
|
|
|
+ if isStringEmpty(schemaNodeYaml.Name) {
|
|
|
+ return nil, errors.New(schemaNodeTypeObject + "schema类型必须有name属性")
|
|
|
+ }
|
|
|
+
|
|
|
+ if isStringEmpty(schemaNodeYaml.Describe) {
|
|
|
+ return nil, errors.New(schemaNodeTypeObject + "schema类型必须有describe属性")
|
|
|
+ }
|
|
|
+
|
|
|
+ if schemaNodeYaml.Fields == nil || len(schemaNodeYaml.Fields) == 0 {
|
|
|
+ return nil, errors.New(schemaNodeTypeObject + "schema类型必须有fields属性")
|
|
|
+ }
|
|
|
+
|
|
|
+ fieldSchemaNodes := make([]SchemaNode, 0)
|
|
|
+ for _, field := range schemaNodeYaml.Fields {
|
|
|
+ fieldSchemaNode, err := newSchemaNode(&field)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ fieldSchemaNodes = append(fieldSchemaNodes, fieldSchemaNode)
|
|
|
+ }
|
|
|
+
|
|
|
+ return &ObjectSchemaNode{
|
|
|
+ BaseNamingSchemaNode: BaseNamingSchemaNode{
|
|
|
+ Type: schemaNodeYaml.Type,
|
|
|
+ Name: schemaNodeYaml.Name,
|
|
|
+ Describe: schemaNodeYaml.Describe,
|
|
|
+ Required: schemaNodeYaml.Required,
|
|
|
+ },
|
|
|
+ AsArray: schemaNodeYaml.AsArray,
|
|
|
+ Fields: fieldSchemaNodes,
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+func newFieldSchemaNode(schemaNodeYaml *SchemaYaml) (*FieldSchemaNode, error) {
|
|
|
+ if schemaNodeYaml.Type != schemaNodeTypeField {
|
|
|
+ return nil, errors.New("schema节点类型错误: 期望类型" + schemaNodeTypeField + " 实际类型" + schemaNodeYaml.Type)
|
|
|
+ }
|
|
|
+
|
|
|
+ if isStringEmpty(schemaNodeYaml.Name) {
|
|
|
+ return nil, errors.New(schemaNodeTypeObject + "schema类型必须有name属性")
|
|
|
+ }
|
|
|
+
|
|
|
+ if isStringEmpty(schemaNodeYaml.Describe) {
|
|
|
+ return nil, errors.New(schemaNodeTypeObject + "schema类型必须有describe属性")
|
|
|
+ }
|
|
|
+
|
|
|
+ if isStringEmpty(schemaNodeYaml.FieldType) {
|
|
|
+ return nil, errors.New(schemaNodeTypeObject + "schema类型必须有field_type属性")
|
|
|
+ }
|
|
|
+
|
|
|
+ return &FieldSchemaNode{
|
|
|
+ BaseNamingSchemaNode: BaseNamingSchemaNode{
|
|
|
+ Type: schemaNodeYaml.Type,
|
|
|
+ Name: schemaNodeYaml.Name,
|
|
|
+ Describe: schemaNodeYaml.Describe,
|
|
|
+ Required: schemaNodeYaml.Required,
|
|
|
+ },
|
|
|
+ AsArray: schemaNodeYaml.AsArray,
|
|
|
+ FieldType: schemaNodeYaml.FieldType,
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (schemaDoc *SchemaDoc) ValidateFile(filePath string) error {
|
|
|
+ if !pathExists(filePath) {
|
|
|
+ return errors.New("文件不存在: " + filePath)
|
|
|
+ }
|
|
|
+
|
|
|
+ fileBytes, err := os.ReadFile(filePath)
|
|
|
+ if err != nil {
|
|
|
+ return errors.New("读取文件错误: " + err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ yamlMap := make(map[string]any)
|
|
|
+ err = yaml.Unmarshal(fileBytes, yamlMap)
|
|
|
+ if err != nil {
|
|
|
+ return errors.New("unmarshal文件错误: " + err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ return schemaDoc.validate(yamlMap)
|
|
|
+}
|
|
|
+
|
|
|
+func (schemaDoc *SchemaDoc) ValidateMap(yamlMap map[string]any) error {
|
|
|
+ if yamlMap == nil || len(yamlMap) == 0 {
|
|
|
+ return errors.New("没有yaml的map")
|
|
|
+ }
|
|
|
+
|
|
|
+ return schemaDoc.validate(yamlMap)
|
|
|
+}
|
|
|
+
|
|
|
+func (schemaDoc *SchemaDoc) validate(yamlMap map[string]any) error {
|
|
|
+ for _, rootSchemaNode := range schemaDoc.rootSchemas {
|
|
|
+ err := rootSchemaNode.Validate(yamlMap)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|