func resolveUrl(root *url.URL, documentURL string) (gojsonreference.JsonReference, error) { ref, err := gojsonreference.NewJsonReference(documentURL) if err != nil { return ref, err } resolvedURL := root.ResolveReference(ref.GetUrl()) return gojsonreference.NewJsonReference(resolvedURL.String()) }
func (l *jsonGoLoader) loadSchema() (*Schema, error) { var err error document, err := l.loadJSON() if err != nil { return nil, err } d := Schema{} d.pool = newSchemaPool(osFS) d.referencePool = newSchemaReferencePool() d.documentReference, err = gojsonreference.NewJsonReference("#") d.pool.SetStandaloneDocument(document) if err != nil { return nil, err } err = d.parse(document) if err != nil { return nil, err } return &d, nil }
func (l *jsonReferenceLoader) loadSchema() (*Schema, error) { var err error d := Schema{} d.pool = newSchemaPool(l.fs) d.referencePool = newSchemaReferencePool() d.documentReference, err = gojsonreference.NewJsonReference(l.jsonSource().(string)) if err != nil { return nil, err } spd, err := d.pool.GetDocument(d.documentReference) if err != nil { return nil, err } err = d.parse(spd.Document) if err != nil { return nil, err } return &d, nil }
func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) { reference, err := gojsonreference.NewJsonReference(l.source) if err != nil { return nil, err } refToUrl := reference refToUrl.GetUrl().Fragment = "" if reference.HasFileScheme { filename := strings.Replace(refToUrl.String(), "file://", "", -1) if runtime.GOOS == "windows" { // on Windows, a file URL may have an extra leading slash, use slashes // instead of backslashes, and have spaces escaped if strings.HasPrefix(filename, "/") { filename = filename[1:] } filename = filepath.FromSlash(filename) filename = strings.Replace(filename, "%20", " ", -1) } return l.loadFromFile(filename) } else { return l.loadFromHTTP(refToUrl.String()) } }
func (l *jsonReferenceLoader) loadJSON() (interface{}, error) { var err error reference, err := gojsonreference.NewJsonReference(l.jsonSource().(string)) if err != nil { return nil, err } refToUrl := reference refToUrl.GetUrl().Fragment = "" var document interface{} if reference.HasFileScheme { filename := strings.Replace(refToUrl.String(), "file://", "", -1) document, err = l.loadFromFile(filename) if err != nil { return nil, err } } else { document, err = l.loadFromHTTP(refToUrl.String()) if err != nil { return nil, err } } return document, nil }
func useLocalUrl(ref gojsonreference.JsonReference) gojsonreference.JsonReference { // Grab ninjablocks schemas locally local := strings.Replace(ref.GetUrl().String(), root, "file:///", 1) log.Debugf("Fetching document from %s", local) localURL, _ := gojsonreference.NewJsonReference(local) return localURL }
func (l *LocalSchemaLoader) JsonReference() (gojsonreference.JsonReference, error) { return gojsonreference.NewJsonReference(l.source) }
func (l *jsonReferenceLoader) JsonReference() (gojsonreference.JsonReference, error) { return gojsonreference.NewJsonReference(l.JsonSource().(string)) }
func (l *jsonGoLoader) JsonReference() (gojsonreference.JsonReference, error) { return gojsonreference.NewJsonReference("#") }
func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) (e error) { var err error jsonReference, err := gojsonreference.NewJsonReference(reference) if err != nil { return err } standaloneDocument := d.pool.GetStandaloneDocument() if jsonReference.HasFullUrl { currentSchema.ref = &jsonReference } else { inheritedReference, err := currentSchema.ref.Inherits(jsonReference) if err != nil { return err } currentSchema.ref = inheritedReference } jsonPointer := currentSchema.ref.GetPointer() var refdDocumentNode interface{} if standaloneDocument != nil { var err error refdDocumentNode, _, err = jsonPointer.Get(standaloneDocument) if err != nil { return err } } else { var err error dsp, err := d.pool.GetDocument(*currentSchema.ref) if err != nil { return err } refdDocumentNode, _, err = jsonPointer.Get(dsp.Document) if err != nil { return err } } newSchemaDocument, ok := refdDocumentNode.(map[string]interface{}) if !ok { return errors.New(formatErrorDescription( Locale.MustBeOfType(), ErrorDetails{"key": STRING_SCHEMA, "type": TYPE_OBJECT}, )) } // returns the loaded referenced subSchema for the caller to update its current subSchema newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref} d.referencePool.Add(currentSchema.ref.String()+reference, newSchema) err = d.parseSchema(newSchemaDocument, newSchema, true) if err != nil { return err } currentSchema.refSchema = newSchema return nil }
// Parses a subSchema // // Pretty long function ( sorry :) )... but pretty straight forward, repetitive and boring // Not much magic involved here, most of the job is to validate the key names and their values, // then the values are copied into subSchema struct // func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema, typeChecked bool) error { if !typeChecked && !isKind(documentNode, reflect.Map) { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_OBJECT, "given": STRING_SCHEMA, }, )) } m := documentNode.(map[string]interface{}) if currentSchema == d.rootSchema { currentSchema.ref = &d.documentReference } var ( schemaV, refV, definitionsV, idV, titleV, descriptionV, typeV, propsV, additionalPropsV, patternPropsV, dependenciesV, itemsV, additionalItemsV, multipleOfV, minimumV, exclusiveMinimumV, maximumV, exclusiveMaximumV, minLengthV, maxLengthV, patternV, formatV, minPropsV, maxPropsV, requiredV, minItemsV, maxItemsV, uniqueItemsV, enumV, oneOfV, anyOfV, allOfV, notV interface{} ) for k, v := range m { switch k { case KEY_SCHEMA: schemaV = v case KEY_REF: refV = v case KEY_DEFINITIONS: definitionsV = v case KEY_ID: idV = v case KEY_DESCRIPTION: descriptionV = v case KEY_TYPE: typeV = v case KEY_PROPERTIES: propsV = v case KEY_ADDITIONAL_PROPERTIES: additionalPropsV = v case KEY_PATTERN_PROPERTIES: patternPropsV = v case KEY_DEPENDENCIES: dependenciesV = v case KEY_ITEMS: itemsV = v case KEY_ADDITIONAL_ITEMS: additionalItemsV = v case KEY_MULTIPLE_OF: multipleOfV = v case KEY_MINIMUM: minimumV = v case KEY_EXCLUSIVE_MINIMUM: exclusiveMinimumV = v case KEY_MAXIMUM: maximumV = v case KEY_EXCLUSIVE_MAXIMUM: exclusiveMaximumV = v case KEY_MIN_LENGTH: minLengthV = v case KEY_MAX_LENGTH: maxLengthV = v case KEY_PATTERN: patternV = v case KEY_FORMAT: formatV = v case KEY_MIN_PROPERTIES: minPropsV = v case KEY_MAX_PROPERTIES: maxPropsV = v case KEY_REQUIRED: requiredV = v case KEY_MIN_ITEMS: minItemsV = v case KEY_MAX_ITEMS: maxItemsV = v case KEY_UNIQUE_ITEMS: uniqueItemsV = v case KEY_ENUM: enumV = v case KEY_ONE_OF: oneOfV = v case KEY_ANY_OF: anyOfV = v case KEY_ALL_OF: allOfV = v case KEY_NOT: notV = v } } // $subSchema if schemaV != nil { if schemaRef, ok := schemaV.(string); ok { schemaReference, err := gojsonreference.NewJsonReference(schemaRef) if err != nil { return err } currentSchema.subSchema = &schemaReference } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_SCHEMA, }, )) } } // $ref if refV != nil { if k, ok := refV.(string); ok { if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok { currentSchema.refSchema = sch } else { return d.parseReference(documentNode, currentSchema, k) } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_REF, }, )) } } // definitions if definitionsV != nil { if defs, ok := definitionsV.(map[string]interface{}); ok { currentSchema.definitions = make(map[string]*subSchema) for dk, dv := range defs { if isKind(dv, reflect.Map) { newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, ref: currentSchema.ref} currentSchema.definitions[dk] = newSchema err := d.parseSchema(dv, newSchema, true) if err != nil { return errors.New(err.Error()) } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_ARRAY_OF_SCHEMAS, "given": KEY_DEFINITIONS, }, )) } } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_ARRAY_OF_SCHEMAS, "given": KEY_DEFINITIONS, }, )) } } // id if idV != nil { if k, ok := idV.(string); ok { currentSchema.id = &k } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_ID, }, )) } } // title if titleV != nil { if k, ok := titleV.(string); ok { currentSchema.title = &k } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_TITLE, }, )) } } // description if descriptionV != nil { if k, ok := descriptionV.(string); ok { currentSchema.description = &k } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_DESCRIPTION, }, )) } } // type if typeV != nil { switch typeV.(type) { case string: if k, ok := typeV.(string); ok { if err := currentSchema.types.Add(k); err != nil { return err } } case []interface{}: arrayOfTypes := typeV.([]interface{}) for _, typeInArray := range arrayOfTypes { if k, ok := typeInArray.(string); ok { currentSchema.types.Add(k) } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS, "given": KEY_TYPE, }, )) } } default: return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS, "given": KEY_TYPE, }, )) } } // properties if propsV != nil { if err := d.parseProperties(propsV, currentSchema); err != nil { return err } } // additionalProperties if additionalPropsV != nil { switch reflect.ValueOf(additionalPropsV).Kind() { case reflect.Bool: currentSchema.additionalProperties = additionalPropsV.(bool) case reflect.Map: newSchema := &subSchema{property: KEY_ADDITIONAL_PROPERTIES, parent: currentSchema, ref: currentSchema.ref} currentSchema.additionalProperties = newSchema if err := d.parseSchema(additionalPropsV, newSchema, true); err != nil { return errors.New(err.Error()) } default: return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA, "given": KEY_ADDITIONAL_PROPERTIES, }, )) } } // patternProperties if patternPropsV != nil { if patternPropertiesMap, ok := patternPropsV.(map[string]interface{}); ok { if len(patternPropertiesMap) > 0 { currentSchema.patternProperties = make(map[string]*subSchema) for k, v := range patternPropertiesMap { if _, err := regexpCompile(k); err != nil { return errors.New(formatErrorDescription( Locale.RegexPattern(), ErrorDetails{"pattern": k}, )) } newSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref} if err := d.parseSchema(v, newSchema, false); err != nil { return errors.New(err.Error()) } currentSchema.patternProperties[k] = newSchema } } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_SCHEMA, "given": KEY_PATTERN_PROPERTIES, }, )) } } // dependencies if dependenciesV != nil { if err := d.parseDependencies(dependenciesV, currentSchema); err != nil { return err } } // items if itemsV != nil { switch reflect.ValueOf(itemsV).Kind() { case reflect.Slice: for _, itemElement := range itemsV.([]interface{}) { if isKind(itemElement, reflect.Map) { newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref currentSchema.AddItemsChild(newSchema) if err := d.parseSchema(itemElement, newSchema, true); err != nil { return err } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS, "given": KEY_ITEMS, }, )) } currentSchema.itemsChildrenIsSingleSchema = false } case reflect.Map: newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref currentSchema.AddItemsChild(newSchema) if err := d.parseSchema(itemsV, newSchema, true); err != nil { return err } currentSchema.itemsChildrenIsSingleSchema = true default: return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS, "given": KEY_ITEMS, }, )) } } // additionalItems if additionalItemsV != nil { switch reflect.ValueOf(additionalItemsV).Kind() { case reflect.Bool: currentSchema.additionalItems = additionalItemsV.(bool) case reflect.Map: newSchema := &subSchema{property: KEY_ADDITIONAL_ITEMS, parent: currentSchema, ref: currentSchema.ref} currentSchema.additionalItems = newSchema if err := d.parseSchema(additionalItemsV, newSchema, true); err != nil { return errors.New(err.Error()) } default: return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA, "given": KEY_ADDITIONAL_ITEMS, }, )) } } // validation : number / integer if multipleOfV != nil { multipleOfValue := mustBeNumber(multipleOfV) if multipleOfValue == nil { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_NUMBER, "given": KEY_MULTIPLE_OF, }, )) } if *multipleOfValue <= 0 { return errors.New(formatErrorDescription( Locale.GreaterThanZero(), ErrorDetails{"number": KEY_MULTIPLE_OF}, )) } currentSchema.multipleOf = multipleOfValue } if minimumV != nil { minimumValue := mustBeNumber(minimumV) if minimumValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_MINIMUM, "y": STRING_NUMBER}, )) } currentSchema.minimum = minimumValue } if exclusiveMinimumV != nil { if exclusiveMinimumValue, ok := exclusiveMinimumV.(bool); ok { if currentSchema.minimum == nil { return errors.New(formatErrorDescription( Locale.CannotBeUsedWithout(), ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM}, )) } currentSchema.exclusiveMinimum = exclusiveMinimumValue } else { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": TYPE_BOOLEAN}, )) } } if maximumV != nil { maximumValue := mustBeNumber(maximumV) if maximumValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_MAXIMUM, "y": STRING_NUMBER}, )) } currentSchema.maximum = maximumValue } if exclusiveMaximumV != nil { if exclusiveMaximumValue, ok := exclusiveMaximumV.(bool); ok { if currentSchema.maximum == nil { return errors.New(formatErrorDescription( Locale.CannotBeUsedWithout(), ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM}, )) } currentSchema.exclusiveMaximum = exclusiveMaximumValue } else { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": STRING_NUMBER}, )) } } if currentSchema.minimum != nil && currentSchema.maximum != nil { if *currentSchema.minimum > *currentSchema.maximum { return errors.New(formatErrorDescription( Locale.CannotBeGT(), ErrorDetails{"x": KEY_MINIMUM, "y": KEY_MAXIMUM}, )) } } // validation : string if minLengthV != nil { minLengthIntegerValue := mustBeInteger(minLengthV) if minLengthIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MIN_LENGTH, "y": TYPE_INTEGER}, )) } if *minLengthIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MIN_LENGTH}, )) } currentSchema.minLength = minLengthIntegerValue } if maxLengthV != nil { maxLengthIntegerValue := mustBeInteger(maxLengthV) if maxLengthIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MAX_LENGTH, "y": TYPE_INTEGER}, )) } if *maxLengthIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MAX_LENGTH}, )) } currentSchema.maxLength = maxLengthIntegerValue } if currentSchema.minLength != nil && currentSchema.maxLength != nil { if *currentSchema.minLength > *currentSchema.maxLength { return errors.New(formatErrorDescription( Locale.CannotBeGT(), ErrorDetails{"x": KEY_MIN_LENGTH, "y": KEY_MAX_LENGTH}, )) } } if patternV != nil { if k, ok := patternV.(string); ok { regexpObject, err := regexpCompile(k) if err != nil { return errors.New(formatErrorDescription( Locale.MustBeValidRegex(), ErrorDetails{"key": KEY_PATTERN}, )) } currentSchema.pattern = regexpObject } else { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_PATTERN, "y": TYPE_STRING}, )) } } if formatV != nil { formatString, ok := formatV.(string) if ok && FormatCheckers.Has(formatString) { currentSchema.format = formatString } else { return errors.New(formatErrorDescription( Locale.MustBeValidFormat(), ErrorDetails{"key": KEY_FORMAT, "given": formatV}, )) } } // validation : object if minPropsV != nil { minPropertiesIntegerValue := mustBeInteger(minPropsV) if minPropertiesIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MIN_PROPERTIES, "y": TYPE_INTEGER}, )) } if *minPropertiesIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MIN_PROPERTIES}, )) } currentSchema.minProperties = minPropertiesIntegerValue } if maxPropsV != nil { maxPropertiesIntegerValue := mustBeInteger(maxPropsV) if maxPropertiesIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MAX_PROPERTIES, "y": TYPE_INTEGER}, )) } if *maxPropertiesIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MAX_PROPERTIES}, )) } currentSchema.maxProperties = maxPropertiesIntegerValue } if currentSchema.minProperties != nil && currentSchema.maxProperties != nil { if *currentSchema.minProperties > *currentSchema.maxProperties { return errors.New(formatErrorDescription( Locale.KeyCannotBeGreaterThan(), ErrorDetails{"key": KEY_MIN_PROPERTIES, "y": KEY_MAX_PROPERTIES}, )) } } if requiredV != nil { if requiredValues, ok := requiredV.([]interface{}); ok { for _, requiredValue := range requiredValues { if k, ok := requiredValue.(string); ok { if err := currentSchema.AddRequired(k); err != nil { return err } } else { return errors.New(formatErrorDescription( Locale.KeyItemsMustBeOfType(), ErrorDetails{"key": KEY_REQUIRED, "type": TYPE_STRING}, )) } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_REQUIRED, "y": TYPE_ARRAY}, )) } } // validation : array if minItemsV != nil { minItemsIntegerValue := mustBeInteger(minItemsV) if minItemsIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MIN_ITEMS, "y": TYPE_INTEGER}, )) } if *minItemsIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MIN_ITEMS}, )) } currentSchema.minItems = minItemsIntegerValue } if maxItemsV != nil { maxItemsIntegerValue := mustBeInteger(maxItemsV) if maxItemsIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MAX_ITEMS, "y": TYPE_INTEGER}, )) } if *maxItemsIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MAX_ITEMS}, )) } currentSchema.maxItems = maxItemsIntegerValue } if uniqueItemsV != nil { if k, ok := uniqueItemsV.(bool); ok { currentSchema.uniqueItems = k } else { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_UNIQUE_ITEMS, "y": TYPE_BOOLEAN}, )) } } // validation : all if enumV != nil { if enumValue, ok := enumV.([]interface{}); ok { for _, v := range enumValue { if err := currentSchema.AddEnum(v); err != nil { return err } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_ENUM, "y": TYPE_ARRAY}, )) } } // validation : subSchema if oneOfV != nil { if oneOfValue, ok := oneOfV.([]interface{}); ok { for _, v := range oneOfValue { newSchema := &subSchema{property: KEY_ONE_OF, parent: currentSchema, ref: currentSchema.ref} currentSchema.AddOneOf(newSchema) if err := d.parseSchema(v, newSchema, false); err != nil { return err } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_ONE_OF, "y": TYPE_ARRAY}, )) } } if anyOfV != nil { if anyOfValue, ok := anyOfV.([]interface{}); ok { for _, v := range anyOfValue { newSchema := &subSchema{property: KEY_ANY_OF, parent: currentSchema, ref: currentSchema.ref} currentSchema.AddAnyOf(newSchema) if err := d.parseSchema(v, newSchema, false); err != nil { return err } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY}, )) } } if allOfV != nil { if allOfValue, ok := allOfV.([]interface{}); ok { for _, v := range allOfValue { newSchema := &subSchema{property: KEY_ALL_OF, parent: currentSchema, ref: currentSchema.ref} currentSchema.AddAllOf(newSchema) if err := d.parseSchema(v, newSchema, false); err != nil { return err } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY}, )) } } if notV != nil { if isKind(notV, reflect.Map) { newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref} currentSchema.SetNot(newSchema) if err := d.parseSchema(notV, newSchema, true); err != nil { return err } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_NOT, "y": TYPE_OBJECT}, )) } } return nil }
// Parses a subSchema // // Pretty long function ( sorry :) )... but pretty straight forward, repetitive and boring // Not much magic involved here, most of the job is to validate the key names and their values, // then the values are copied into subSchema struct // func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) error { if !isKind(documentNode, reflect.Map) { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_OBJECT, "given": STRING_SCHEMA, }, )) } m := documentNode.(map[string]interface{}) if currentSchema == d.rootSchema { currentSchema.ref = &d.documentReference } // $subSchema if existsMapKey(m, KEY_SCHEMA) { if !isKind(m[KEY_SCHEMA], reflect.String) { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_SCHEMA, }, )) } schemaRef := m[KEY_SCHEMA].(string) schemaReference, err := gojsonreference.NewJsonReference(schemaRef) currentSchema.subSchema = &schemaReference if err != nil { return err } } // $ref if existsMapKey(m, KEY_REF) && !isKind(m[KEY_REF], reflect.String) { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_REF, }, )) } if k, ok := m[KEY_REF].(string); ok { jsonReference, err := gojsonreference.NewJsonReference(k) if err != nil { return err } if jsonReference.HasFullUrl { currentSchema.ref = &jsonReference } else { inheritedReference, err := currentSchema.ref.Inherits(jsonReference) if err != nil { return err } currentSchema.ref = inheritedReference } if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok { currentSchema.refSchema = sch } else { err := d.parseReference(documentNode, currentSchema, k) if err != nil { return err } return nil } } // definitions if existsMapKey(m, KEY_DEFINITIONS) { if isKind(m[KEY_DEFINITIONS], reflect.Map) { currentSchema.definitions = make(map[string]*subSchema) for dk, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) { if isKind(dv, reflect.Map) { newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, ref: currentSchema.ref} currentSchema.definitions[dk] = newSchema err := d.parseSchema(dv, newSchema) if err != nil { return errors.New(err.Error()) } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_ARRAY_OF_SCHEMAS, "given": KEY_DEFINITIONS, }, )) } } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_ARRAY_OF_SCHEMAS, "given": KEY_DEFINITIONS, }, )) } } // id if existsMapKey(m, KEY_ID) && !isKind(m[KEY_ID], reflect.String) { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_ID, }, )) } if k, ok := m[KEY_ID].(string); ok { currentSchema.id = &k } // title if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_TITLE, }, )) } if k, ok := m[KEY_TITLE].(string); ok { currentSchema.title = &k } // description if existsMapKey(m, KEY_DESCRIPTION) && !isKind(m[KEY_DESCRIPTION], reflect.String) { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING, "given": KEY_DESCRIPTION, }, )) } if k, ok := m[KEY_DESCRIPTION].(string); ok { currentSchema.description = &k } // type if existsMapKey(m, KEY_TYPE) { if isKind(m[KEY_TYPE], reflect.String) { if k, ok := m[KEY_TYPE].(string); ok { err := currentSchema.types.Add(k) if err != nil { return err } } } else { if isKind(m[KEY_TYPE], reflect.Slice) { arrayOfTypes := m[KEY_TYPE].([]interface{}) for _, typeInArray := range arrayOfTypes { if reflect.ValueOf(typeInArray).Kind() != reflect.String { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS, "given": KEY_TYPE, }, )) } else { currentSchema.types.Add(typeInArray.(string)) } } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS, "given": KEY_TYPE, }, )) } } } // properties if existsMapKey(m, KEY_PROPERTIES) { err := d.parseProperties(m[KEY_PROPERTIES], currentSchema) if err != nil { return err } } // additionalProperties if existsMapKey(m, KEY_ADDITIONAL_PROPERTIES) { if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Bool) { currentSchema.additionalProperties = m[KEY_ADDITIONAL_PROPERTIES].(bool) } else if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Map) { newSchema := &subSchema{property: KEY_ADDITIONAL_PROPERTIES, parent: currentSchema, ref: currentSchema.ref} currentSchema.additionalProperties = newSchema err := d.parseSchema(m[KEY_ADDITIONAL_PROPERTIES], newSchema) if err != nil { return errors.New(err.Error()) } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA, "given": KEY_ADDITIONAL_PROPERTIES, }, )) } } // patternProperties if existsMapKey(m, KEY_PATTERN_PROPERTIES) { if isKind(m[KEY_PATTERN_PROPERTIES], reflect.Map) { patternPropertiesMap := m[KEY_PATTERN_PROPERTIES].(map[string]interface{}) if len(patternPropertiesMap) > 0 { currentSchema.patternProperties = make(map[string]*subSchema) for k, v := range patternPropertiesMap { _, err := regexp.MatchString(k, "") if err != nil { return errors.New(formatErrorDescription( Locale.RegexPattern(), ErrorDetails{"pattern": k}, )) } newSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref} err = d.parseSchema(v, newSchema) if err != nil { return errors.New(err.Error()) } currentSchema.patternProperties[k] = newSchema } } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_SCHEMA, "given": KEY_PATTERN_PROPERTIES, }, )) } } // dependencies if existsMapKey(m, KEY_DEPENDENCIES) { err := d.parseDependencies(m[KEY_DEPENDENCIES], currentSchema) if err != nil { return err } } // items if existsMapKey(m, KEY_ITEMS) { if isKind(m[KEY_ITEMS], reflect.Slice) { for _, itemElement := range m[KEY_ITEMS].([]interface{}) { if isKind(itemElement, reflect.Map) { newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref currentSchema.AddItemsChild(newSchema) err := d.parseSchema(itemElement, newSchema) if err != nil { return err } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS, "given": KEY_ITEMS, }, )) } currentSchema.itemsChildrenIsSingleSchema = false } } else if isKind(m[KEY_ITEMS], reflect.Map) { newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref currentSchema.AddItemsChild(newSchema) err := d.parseSchema(m[KEY_ITEMS], newSchema) if err != nil { return err } currentSchema.itemsChildrenIsSingleSchema = true } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS, "given": KEY_ITEMS, }, )) } } // additionalItems if existsMapKey(m, KEY_ADDITIONAL_ITEMS) { if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Bool) { currentSchema.additionalItems = m[KEY_ADDITIONAL_ITEMS].(bool) } else if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Map) { newSchema := &subSchema{property: KEY_ADDITIONAL_ITEMS, parent: currentSchema, ref: currentSchema.ref} currentSchema.additionalItems = newSchema err := d.parseSchema(m[KEY_ADDITIONAL_ITEMS], newSchema) if err != nil { return errors.New(err.Error()) } } else { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA, "given": KEY_ADDITIONAL_ITEMS, }, )) } } // validation : number / integer if existsMapKey(m, KEY_MULTIPLE_OF) { multipleOfValue := mustBeNumber(m[KEY_MULTIPLE_OF]) if multipleOfValue == nil { return errors.New(formatErrorDescription( Locale.InvalidType(), ErrorDetails{ "expected": STRING_NUMBER, "given": KEY_MULTIPLE_OF, }, )) } if *multipleOfValue <= 0 { return errors.New(formatErrorDescription( Locale.GreaterThanZero(), ErrorDetails{"number": KEY_MULTIPLE_OF}, )) } currentSchema.multipleOf = multipleOfValue } if existsMapKey(m, KEY_MINIMUM) { minimumValue := mustBeNumber(m[KEY_MINIMUM]) if minimumValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_MINIMUM, "y": STRING_NUMBER}, )) } currentSchema.minimum = minimumValue } if existsMapKey(m, KEY_EXCLUSIVE_MINIMUM) { if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) { if currentSchema.minimum == nil { return errors.New(formatErrorDescription( Locale.CannotBeUsedWithout(), ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM}, )) } exclusiveMinimumValue := m[KEY_EXCLUSIVE_MINIMUM].(bool) currentSchema.exclusiveMinimum = exclusiveMinimumValue } else { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": TYPE_BOOLEAN}, )) } } if existsMapKey(m, KEY_MAXIMUM) { maximumValue := mustBeNumber(m[KEY_MAXIMUM]) if maximumValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_MAXIMUM, "y": STRING_NUMBER}, )) } currentSchema.maximum = maximumValue } if existsMapKey(m, KEY_EXCLUSIVE_MAXIMUM) { if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) { if currentSchema.maximum == nil { return errors.New(formatErrorDescription( Locale.CannotBeUsedWithout(), ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM}, )) } exclusiveMaximumValue := m[KEY_EXCLUSIVE_MAXIMUM].(bool) currentSchema.exclusiveMaximum = exclusiveMaximumValue } else { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": STRING_NUMBER}, )) } } if currentSchema.minimum != nil && currentSchema.maximum != nil { if *currentSchema.minimum > *currentSchema.maximum { return errors.New(formatErrorDescription( Locale.CannotBeGT(), ErrorDetails{"x": KEY_MINIMUM, "y": KEY_MAXIMUM}, )) } } // validation : string if existsMapKey(m, KEY_MIN_LENGTH) { minLengthIntegerValue := mustBeInteger(m[KEY_MIN_LENGTH]) if minLengthIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MIN_LENGTH, "y": TYPE_INTEGER}, )) } if *minLengthIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MIN_LENGTH}, )) } currentSchema.minLength = minLengthIntegerValue } if existsMapKey(m, KEY_MAX_LENGTH) { maxLengthIntegerValue := mustBeInteger(m[KEY_MAX_LENGTH]) if maxLengthIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MAX_LENGTH, "y": TYPE_INTEGER}, )) } if *maxLengthIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MAX_LENGTH}, )) } currentSchema.maxLength = maxLengthIntegerValue } if currentSchema.minLength != nil && currentSchema.maxLength != nil { if *currentSchema.minLength > *currentSchema.maxLength { return errors.New(formatErrorDescription( Locale.CannotBeGT(), ErrorDetails{"x": KEY_MIN_LENGTH, "y": KEY_MAX_LENGTH}, )) } } if existsMapKey(m, KEY_PATTERN) { if isKind(m[KEY_PATTERN], reflect.String) { regexpObject, err := regexp.Compile(m[KEY_PATTERN].(string)) if err != nil { return errors.New(formatErrorDescription( Locale.MustBeValidRegex(), ErrorDetails{"key": KEY_PATTERN}, )) } currentSchema.pattern = regexpObject } else { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_PATTERN, "y": TYPE_STRING}, )) } } if existsMapKey(m, KEY_FORMAT) { formatString, ok := m[KEY_FORMAT].(string) if ok && FormatCheckers.Has(formatString) { currentSchema.format = formatString } else { return errors.New(formatErrorDescription( Locale.MustBeValidFormat(), ErrorDetails{"key": KEY_FORMAT, "given": m[KEY_FORMAT]}, )) } } // validation : object if existsMapKey(m, KEY_MIN_PROPERTIES) { minPropertiesIntegerValue := mustBeInteger(m[KEY_MIN_PROPERTIES]) if minPropertiesIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MIN_PROPERTIES, "y": TYPE_INTEGER}, )) } if *minPropertiesIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MIN_PROPERTIES}, )) } currentSchema.minProperties = minPropertiesIntegerValue } if existsMapKey(m, KEY_MAX_PROPERTIES) { maxPropertiesIntegerValue := mustBeInteger(m[KEY_MAX_PROPERTIES]) if maxPropertiesIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MAX_PROPERTIES, "y": TYPE_INTEGER}, )) } if *maxPropertiesIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MAX_PROPERTIES}, )) } currentSchema.maxProperties = maxPropertiesIntegerValue } if currentSchema.minProperties != nil && currentSchema.maxProperties != nil { if *currentSchema.minProperties > *currentSchema.maxProperties { return errors.New(formatErrorDescription( Locale.KeyCannotBeGreaterThan(), ErrorDetails{"key": KEY_MIN_PROPERTIES, "y": KEY_MAX_PROPERTIES}, )) } } if existsMapKey(m, KEY_REQUIRED) { if isKind(m[KEY_REQUIRED], reflect.Slice) { requiredValues := m[KEY_REQUIRED].([]interface{}) for _, requiredValue := range requiredValues { if isKind(requiredValue, reflect.String) { err := currentSchema.AddRequired(requiredValue.(string)) if err != nil { return err } } else { return errors.New(formatErrorDescription( Locale.KeyItemsMustBeOfType(), ErrorDetails{"key": KEY_REQUIRED, "type": TYPE_STRING}, )) } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_REQUIRED, "y": TYPE_ARRAY}, )) } } // validation : array if existsMapKey(m, KEY_MIN_ITEMS) { minItemsIntegerValue := mustBeInteger(m[KEY_MIN_ITEMS]) if minItemsIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MIN_ITEMS, "y": TYPE_INTEGER}, )) } if *minItemsIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MIN_ITEMS}, )) } currentSchema.minItems = minItemsIntegerValue } if existsMapKey(m, KEY_MAX_ITEMS) { maxItemsIntegerValue := mustBeInteger(m[KEY_MAX_ITEMS]) if maxItemsIntegerValue == nil { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_MAX_ITEMS, "y": TYPE_INTEGER}, )) } if *maxItemsIntegerValue < 0 { return errors.New(formatErrorDescription( Locale.MustBeGTEZero(), ErrorDetails{"key": KEY_MAX_ITEMS}, )) } currentSchema.maxItems = maxItemsIntegerValue } if existsMapKey(m, KEY_UNIQUE_ITEMS) { if isKind(m[KEY_UNIQUE_ITEMS], reflect.Bool) { currentSchema.uniqueItems = m[KEY_UNIQUE_ITEMS].(bool) } else { return errors.New(formatErrorDescription( Locale.MustBeOfA(), ErrorDetails{"x": KEY_UNIQUE_ITEMS, "y": TYPE_BOOLEAN}, )) } } // validation : all if existsMapKey(m, KEY_ENUM) { if isKind(m[KEY_ENUM], reflect.Slice) { for _, v := range m[KEY_ENUM].([]interface{}) { err := currentSchema.AddEnum(v) if err != nil { return err } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_ENUM, "y": TYPE_ARRAY}, )) } } // validation : subSchema if existsMapKey(m, KEY_ONE_OF) { if isKind(m[KEY_ONE_OF], reflect.Slice) { for _, v := range m[KEY_ONE_OF].([]interface{}) { newSchema := &subSchema{property: KEY_ONE_OF, parent: currentSchema, ref: currentSchema.ref} currentSchema.AddOneOf(newSchema) err := d.parseSchema(v, newSchema) if err != nil { return err } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_ONE_OF, "y": TYPE_ARRAY}, )) } } if existsMapKey(m, KEY_ANY_OF) { if isKind(m[KEY_ANY_OF], reflect.Slice) { for _, v := range m[KEY_ANY_OF].([]interface{}) { newSchema := &subSchema{property: KEY_ANY_OF, parent: currentSchema, ref: currentSchema.ref} currentSchema.AddAnyOf(newSchema) err := d.parseSchema(v, newSchema) if err != nil { return err } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY}, )) } } if existsMapKey(m, KEY_ALL_OF) { if isKind(m[KEY_ALL_OF], reflect.Slice) { for _, v := range m[KEY_ALL_OF].([]interface{}) { newSchema := &subSchema{property: KEY_ALL_OF, parent: currentSchema, ref: currentSchema.ref} currentSchema.AddAllOf(newSchema) err := d.parseSchema(v, newSchema) if err != nil { return err } } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY}, )) } } if existsMapKey(m, KEY_NOT) { if isKind(m[KEY_NOT], reflect.Map) { newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref} currentSchema.SetNot(newSchema) err := d.parseSchema(m[KEY_NOT], newSchema) if err != nil { return err } } else { return errors.New(formatErrorDescription( Locale.MustBeOfAn(), ErrorDetails{"x": KEY_NOT, "y": TYPE_OBJECT}, )) } } return nil }
// Parses a subSchema // // Pretty long function ( sorry :) )... but pretty straight forward, repetitive and boring // Not much magic involved here, most of the job is to validate the key names and their values, // then the values are copied into subSchema struct // func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) error { if !isKind(documentNode, reflect.Map) { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, STRING_SCHEMA, TYPE_OBJECT)) } m := documentNode.(map[string]interface{}) if currentSchema == d.rootSchema { currentSchema.ref = &d.documentReference } // $subSchema if existsMapKey(m, KEY_SCHEMA) { if !isKind(m[KEY_SCHEMA], reflect.String) { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_SCHEMA, TYPE_STRING)) } schemaRef := m[KEY_SCHEMA].(string) schemaReference, err := gojsonreference.NewJsonReference(schemaRef) currentSchema.subSchema = &schemaReference if err != nil { return err } } // $ref if existsMapKey(m, KEY_REF) && !isKind(m[KEY_REF], reflect.String) { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_REF, TYPE_STRING)) } if k, ok := m[KEY_REF].(string); ok { if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok { currentSchema.refSchema = sch } else { var err error err = d.parseReference(documentNode, currentSchema, k) if err != nil { return err } return nil } } // definitions if existsMapKey(m, KEY_DEFINITIONS) { if isKind(m[KEY_DEFINITIONS], reflect.Map) { currentSchema.definitions = make(map[string]*subSchema) for dk, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) { if isKind(dv, reflect.Map) { newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, ref: currentSchema.ref} currentSchema.definitions[dk] = newSchema err := d.parseSchema(dv, newSchema) if err != nil { return errors.New(err.Error()) } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_DEFINITIONS, STRING_ARRAY_OF_SCHEMAS)) } } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_DEFINITIONS, STRING_ARRAY_OF_SCHEMAS)) } } // id if existsMapKey(m, KEY_ID) && !isKind(m[KEY_ID], reflect.String) { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_ID, TYPE_STRING)) } if k, ok := m[KEY_ID].(string); ok { currentSchema.id = &k } // title if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_TITLE, TYPE_STRING)) } if k, ok := m[KEY_TITLE].(string); ok { currentSchema.title = &k } // description if existsMapKey(m, KEY_DESCRIPTION) && !isKind(m[KEY_DESCRIPTION], reflect.String) { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_DESCRIPTION, TYPE_STRING)) } if k, ok := m[KEY_DESCRIPTION].(string); ok { currentSchema.description = &k } // type if existsMapKey(m, KEY_TYPE) { if isKind(m[KEY_TYPE], reflect.String) { if k, ok := m[KEY_TYPE].(string); ok { err := currentSchema.types.Add(k) if err != nil { return err } } } else { if isKind(m[KEY_TYPE], reflect.Slice) { arrayOfTypes := m[KEY_TYPE].([]interface{}) for _, typeInArray := range arrayOfTypes { if reflect.ValueOf(typeInArray).Kind() != reflect.String { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_TYPE, TYPE_STRING+"/"+STRING_ARRAY_OF_STRINGS)) } else { currentSchema.types.Add(typeInArray.(string)) } } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_TYPE, TYPE_STRING+"/"+STRING_ARRAY_OF_STRINGS)) } } } // properties if existsMapKey(m, KEY_PROPERTIES) { err := d.parseProperties(m[KEY_PROPERTIES], currentSchema) if err != nil { return err } } // additionalProperties if existsMapKey(m, KEY_ADDITIONAL_PROPERTIES) { if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Bool) { currentSchema.additionalProperties = m[KEY_ADDITIONAL_PROPERTIES].(bool) } else if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Map) { newSchema := &subSchema{property: KEY_ADDITIONAL_PROPERTIES, parent: currentSchema, ref: currentSchema.ref} currentSchema.additionalProperties = newSchema err := d.parseSchema(m[KEY_ADDITIONAL_PROPERTIES], newSchema) if err != nil { return errors.New(err.Error()) } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_ADDITIONAL_PROPERTIES, TYPE_BOOLEAN+"/"+STRING_SCHEMA)) } } // patternProperties if existsMapKey(m, KEY_PATTERN_PROPERTIES) { if isKind(m[KEY_PATTERN_PROPERTIES], reflect.Map) { patternPropertiesMap := m[KEY_PATTERN_PROPERTIES].(map[string]interface{}) if len(patternPropertiesMap) > 0 { currentSchema.patternProperties = make(map[string]*subSchema) for k, v := range patternPropertiesMap { _, err := regexp.MatchString(k, "") if err != nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_INVALID_REGEX_PATTERN, k)) } newSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref} err = d.parseSchema(v, newSchema) if err != nil { return errors.New(err.Error()) } currentSchema.patternProperties[k] = newSchema } } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_PATTERN_PROPERTIES, STRING_SCHEMA)) } } // dependencies if existsMapKey(m, KEY_DEPENDENCIES) { err := d.parseDependencies(m[KEY_DEPENDENCIES], currentSchema) if err != nil { return err } } // items if existsMapKey(m, KEY_ITEMS) { if isKind(m[KEY_ITEMS], reflect.Slice) { for _, itemElement := range m[KEY_ITEMS].([]interface{}) { if isKind(itemElement, reflect.Map) { newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref currentSchema.AddItemsChild(newSchema) err := d.parseSchema(itemElement, newSchema) if err != nil { return err } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_ITEMS, STRING_SCHEMA+"/"+STRING_ARRAY_OF_SCHEMAS)) } currentSchema.itemsChildrenIsSingleSchema = false } } else if isKind(m[KEY_ITEMS], reflect.Map) { newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} newSchema.ref = currentSchema.ref currentSchema.AddItemsChild(newSchema) err := d.parseSchema(m[KEY_ITEMS], newSchema) if err != nil { return err } currentSchema.itemsChildrenIsSingleSchema = true } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_ITEMS, STRING_SCHEMA+"/"+STRING_ARRAY_OF_SCHEMAS)) } } // additionalItems if existsMapKey(m, KEY_ADDITIONAL_ITEMS) { if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Bool) { currentSchema.additionalItems = m[KEY_ADDITIONAL_ITEMS].(bool) } else if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Map) { newSchema := &subSchema{property: KEY_ADDITIONAL_ITEMS, parent: currentSchema, ref: currentSchema.ref} currentSchema.additionalItems = newSchema err := d.parseSchema(m[KEY_ADDITIONAL_ITEMS], newSchema) if err != nil { return errors.New(err.Error()) } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, KEY_ADDITIONAL_ITEMS, TYPE_BOOLEAN+"/"+STRING_SCHEMA)) } } // validation : number / integer if existsMapKey(m, KEY_MULTIPLE_OF) { multipleOfValue := mustBeNumber(m[KEY_MULTIPLE_OF]) if multipleOfValue == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_A_Y, KEY_MULTIPLE_OF, STRING_NUMBER)) } if *multipleOfValue <= 0 { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_STRICTLY_GREATER_THAN_0, KEY_MULTIPLE_OF)) } currentSchema.multipleOf = multipleOfValue } if existsMapKey(m, KEY_MINIMUM) { minimumValue := mustBeNumber(m[KEY_MINIMUM]) if minimumValue == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_A_Y, KEY_MINIMUM, STRING_NUMBER)) } currentSchema.minimum = minimumValue } if existsMapKey(m, KEY_EXCLUSIVE_MINIMUM) { if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) { if currentSchema.minimum == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_CANNOT_BE_USED_WITHOUT_Y, KEY_EXCLUSIVE_MINIMUM, KEY_MINIMUM)) } exclusiveMinimumValue := m[KEY_EXCLUSIVE_MINIMUM].(bool) currentSchema.exclusiveMinimum = exclusiveMinimumValue } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_A_Y, KEY_EXCLUSIVE_MINIMUM, TYPE_BOOLEAN)) } } if existsMapKey(m, KEY_MAXIMUM) { maximumValue := mustBeNumber(m[KEY_MAXIMUM]) if maximumValue == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_A_Y, KEY_MAXIMUM, STRING_NUMBER)) } currentSchema.maximum = maximumValue } if existsMapKey(m, KEY_EXCLUSIVE_MAXIMUM) { if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) { if currentSchema.maximum == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_CANNOT_BE_USED_WITHOUT_Y, KEY_EXCLUSIVE_MAXIMUM, KEY_MAXIMUM)) } exclusiveMaximumValue := m[KEY_EXCLUSIVE_MAXIMUM].(bool) currentSchema.exclusiveMaximum = exclusiveMaximumValue } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_A_Y, KEY_EXCLUSIVE_MAXIMUM, STRING_NUMBER)) } } if currentSchema.minimum != nil && currentSchema.maximum != nil { if *currentSchema.minimum > *currentSchema.maximum { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_CANNOT_BE_GREATER_THAN_Y, KEY_MINIMUM, KEY_MAXIMUM)) } } // validation : string if existsMapKey(m, KEY_MIN_LENGTH) { minLengthIntegerValue := mustBeInteger(m[KEY_MIN_LENGTH]) if minLengthIntegerValue == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_MIN_LENGTH, TYPE_INTEGER)) } if *minLengthIntegerValue < 0 { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_GREATER_OR_TO_0, KEY_MIN_LENGTH)) } currentSchema.minLength = minLengthIntegerValue } if existsMapKey(m, KEY_MAX_LENGTH) { maxLengthIntegerValue := mustBeInteger(m[KEY_MAX_LENGTH]) if maxLengthIntegerValue == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_MAX_LENGTH, TYPE_INTEGER)) } if *maxLengthIntegerValue < 0 { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_GREATER_OR_TO_0, KEY_MAX_LENGTH)) } currentSchema.maxLength = maxLengthIntegerValue } if currentSchema.minLength != nil && currentSchema.maxLength != nil { if *currentSchema.minLength > *currentSchema.maxLength { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_CANNOT_BE_GREATER_THAN_Y, KEY_MIN_LENGTH, KEY_MAX_LENGTH)) } } if existsMapKey(m, KEY_PATTERN) { if isKind(m[KEY_PATTERN], reflect.String) { regexpObject, err := regexp.Compile(m[KEY_PATTERN].(string)) if err != nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_VALID_REGEX, KEY_PATTERN)) } currentSchema.pattern = regexpObject } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_A_Y, KEY_PATTERN, TYPE_STRING)) } } // validation : object if existsMapKey(m, KEY_MIN_PROPERTIES) { minPropertiesIntegerValue := mustBeInteger(m[KEY_MIN_PROPERTIES]) if minPropertiesIntegerValue == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_MIN_PROPERTIES, TYPE_INTEGER)) } if *minPropertiesIntegerValue < 0 { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_GREATER_OR_TO_0, KEY_MIN_PROPERTIES)) } currentSchema.minProperties = minPropertiesIntegerValue } if existsMapKey(m, KEY_MAX_PROPERTIES) { maxPropertiesIntegerValue := mustBeInteger(m[KEY_MAX_PROPERTIES]) if maxPropertiesIntegerValue == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_MAX_PROPERTIES, TYPE_INTEGER)) } if *maxPropertiesIntegerValue < 0 { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_GREATER_OR_TO_0, KEY_MAX_PROPERTIES)) } currentSchema.maxProperties = maxPropertiesIntegerValue } if currentSchema.minProperties != nil && currentSchema.maxProperties != nil { if *currentSchema.minProperties > *currentSchema.maxProperties { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_CANNOT_BE_GREATER_THAN_Y, KEY_MIN_PROPERTIES, KEY_MAX_PROPERTIES)) } } if existsMapKey(m, KEY_REQUIRED) { if isKind(m[KEY_REQUIRED], reflect.Slice) { requiredValues := m[KEY_REQUIRED].([]interface{}) for _, requiredValue := range requiredValues { if isKind(requiredValue, reflect.String) { err := currentSchema.AddRequired(requiredValue.(string)) if err != nil { return err } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_ITEMS_MUST_BE_TYPE_Y, KEY_REQUIRED, TYPE_STRING)) } } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_REQUIRED, TYPE_ARRAY)) } } // validation : array if existsMapKey(m, KEY_MIN_ITEMS) { minItemsIntegerValue := mustBeInteger(m[KEY_MIN_ITEMS]) if minItemsIntegerValue == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_MIN_ITEMS, TYPE_INTEGER)) } if *minItemsIntegerValue < 0 { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_GREATER_OR_TO_0, KEY_MIN_ITEMS)) } currentSchema.minItems = minItemsIntegerValue } if existsMapKey(m, KEY_MAX_ITEMS) { maxItemsIntegerValue := mustBeInteger(m[KEY_MAX_ITEMS]) if maxItemsIntegerValue == nil { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_MAX_ITEMS, TYPE_INTEGER)) } if *maxItemsIntegerValue < 0 { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_GREATER_OR_TO_0, KEY_MAX_ITEMS)) } currentSchema.maxItems = maxItemsIntegerValue } if existsMapKey(m, KEY_UNIQUE_ITEMS) { if isKind(m[KEY_UNIQUE_ITEMS], reflect.Bool) { currentSchema.uniqueItems = m[KEY_UNIQUE_ITEMS].(bool) } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_A_Y, KEY_UNIQUE_ITEMS, TYPE_BOOLEAN)) } } // validation : all if existsMapKey(m, KEY_ENUM) { if isKind(m[KEY_ENUM], reflect.Slice) { for _, v := range m[KEY_ENUM].([]interface{}) { err := currentSchema.AddEnum(v) if err != nil { return err } } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_ENUM, TYPE_ARRAY)) } } // validation : subSchema if existsMapKey(m, KEY_ONE_OF) { if isKind(m[KEY_ONE_OF], reflect.Slice) { for _, v := range m[KEY_ONE_OF].([]interface{}) { newSchema := &subSchema{property: KEY_ONE_OF, parent: currentSchema, ref: currentSchema.ref} currentSchema.AddOneOf(newSchema) err := d.parseSchema(v, newSchema) if err != nil { return err } } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_ONE_OF, TYPE_ARRAY)) } } if existsMapKey(m, KEY_ANY_OF) { if isKind(m[KEY_ANY_OF], reflect.Slice) { for _, v := range m[KEY_ANY_OF].([]interface{}) { newSchema := &subSchema{property: KEY_ANY_OF, parent: currentSchema, ref: currentSchema.ref} currentSchema.AddAnyOf(newSchema) err := d.parseSchema(v, newSchema) if err != nil { return err } } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_ANY_OF, TYPE_ARRAY)) } } if existsMapKey(m, KEY_ALL_OF) { if isKind(m[KEY_ALL_OF], reflect.Slice) { for _, v := range m[KEY_ALL_OF].([]interface{}) { newSchema := &subSchema{property: KEY_ALL_OF, parent: currentSchema, ref: currentSchema.ref} currentSchema.AddAllOf(newSchema) err := d.parseSchema(v, newSchema) if err != nil { return err } } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_ANY_OF, TYPE_ARRAY)) } } if existsMapKey(m, KEY_NOT) { if isKind(m[KEY_NOT], reflect.Map) { newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref} currentSchema.SetNot(newSchema) err := d.parseSchema(m[KEY_NOT], newSchema) if err != nil { return err } } else { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_AN_Y, KEY_NOT, TYPE_OBJECT)) } } return nil }
func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) (e error) { var err error jsonReference, err := gojsonreference.NewJsonReference(reference) if err != nil { return err } standaloneDocument := d.pool.GetStandaloneDocument() if jsonReference.HasFullUrl { currentSchema.ref = &jsonReference } else { inheritedReference, err := currentSchema.ref.Inherits(jsonReference) if err != nil { return err } currentSchema.ref = inheritedReference } jsonPointer := currentSchema.ref.GetPointer() var refdDocumentNode interface{} if standaloneDocument != nil { var err error refdDocumentNode, _, err = jsonPointer.Get(standaloneDocument) if err != nil { return err } } else { var err error dsp, err := d.pool.GetDocument(*currentSchema.ref) if err != nil { return err } refdDocumentNode, _, err = jsonPointer.Get(dsp.Document) if err != nil { return err } } if !isKind(refdDocumentNode, reflect.Map) { return errors.New(fmt.Sprintf(ERROR_MESSAGE_X_MUST_BE_OF_TYPE_Y, STRING_SCHEMA, TYPE_OBJECT)) } // returns the loaded referenced subSchema for the caller to update its current subSchema newSchemaDocument := refdDocumentNode.(map[string]interface{}) newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref} d.referencePool.Add(currentSchema.ref.String()+reference, newSchema) err = d.parseSchema(newSchemaDocument, newSchema) if err != nil { return err } currentSchema.refSchema = newSchema return nil }