123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- // Copyright 2017 Google LLC. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package jsonschema
- import (
- "fmt"
- "log"
- "strings"
- )
- //
- // OPERATIONS
- // The following methods perform operations on Schemas.
- //
- // IsEmpty returns true if no members of the Schema are specified.
- func (schema *Schema) IsEmpty() bool {
- return (schema.Schema == nil) &&
- (schema.ID == nil) &&
- (schema.MultipleOf == nil) &&
- (schema.Maximum == nil) &&
- (schema.ExclusiveMaximum == nil) &&
- (schema.Minimum == nil) &&
- (schema.ExclusiveMinimum == nil) &&
- (schema.MaxLength == nil) &&
- (schema.MinLength == nil) &&
- (schema.Pattern == nil) &&
- (schema.AdditionalItems == nil) &&
- (schema.Items == nil) &&
- (schema.MaxItems == nil) &&
- (schema.MinItems == nil) &&
- (schema.UniqueItems == nil) &&
- (schema.MaxProperties == nil) &&
- (schema.MinProperties == nil) &&
- (schema.Required == nil) &&
- (schema.AdditionalProperties == nil) &&
- (schema.Properties == nil) &&
- (schema.PatternProperties == nil) &&
- (schema.Dependencies == nil) &&
- (schema.Enumeration == nil) &&
- (schema.Type == nil) &&
- (schema.AllOf == nil) &&
- (schema.AnyOf == nil) &&
- (schema.OneOf == nil) &&
- (schema.Not == nil) &&
- (schema.Definitions == nil) &&
- (schema.Title == nil) &&
- (schema.Description == nil) &&
- (schema.Default == nil) &&
- (schema.Format == nil) &&
- (schema.Ref == nil)
- }
- // IsEqual returns true if two schemas are equal.
- func (schema *Schema) IsEqual(schema2 *Schema) bool {
- return schema.String() == schema2.String()
- }
- // SchemaOperation represents a function that can be applied to a Schema.
- type SchemaOperation func(schema *Schema, context string)
- // Applies a specified function to a Schema and all of the Schemas that it contains.
- func (schema *Schema) applyToSchemas(operation SchemaOperation, context string) {
- if schema.AdditionalItems != nil {
- s := schema.AdditionalItems.Schema
- if s != nil {
- s.applyToSchemas(operation, "AdditionalItems")
- }
- }
- if schema.Items != nil {
- if schema.Items.SchemaArray != nil {
- for _, s := range *(schema.Items.SchemaArray) {
- s.applyToSchemas(operation, "Items.SchemaArray")
- }
- } else if schema.Items.Schema != nil {
- schema.Items.Schema.applyToSchemas(operation, "Items.Schema")
- }
- }
- if schema.AdditionalProperties != nil {
- s := schema.AdditionalProperties.Schema
- if s != nil {
- s.applyToSchemas(operation, "AdditionalProperties")
- }
- }
- if schema.Properties != nil {
- for _, pair := range *(schema.Properties) {
- s := pair.Value
- s.applyToSchemas(operation, "Properties")
- }
- }
- if schema.PatternProperties != nil {
- for _, pair := range *(schema.PatternProperties) {
- s := pair.Value
- s.applyToSchemas(operation, "PatternProperties")
- }
- }
- if schema.Dependencies != nil {
- for _, pair := range *(schema.Dependencies) {
- schemaOrStringArray := pair.Value
- s := schemaOrStringArray.Schema
- if s != nil {
- s.applyToSchemas(operation, "Dependencies")
- }
- }
- }
- if schema.AllOf != nil {
- for _, s := range *(schema.AllOf) {
- s.applyToSchemas(operation, "AllOf")
- }
- }
- if schema.AnyOf != nil {
- for _, s := range *(schema.AnyOf) {
- s.applyToSchemas(operation, "AnyOf")
- }
- }
- if schema.OneOf != nil {
- for _, s := range *(schema.OneOf) {
- s.applyToSchemas(operation, "OneOf")
- }
- }
- if schema.Not != nil {
- schema.Not.applyToSchemas(operation, "Not")
- }
- if schema.Definitions != nil {
- for _, pair := range *(schema.Definitions) {
- s := pair.Value
- s.applyToSchemas(operation, "Definitions")
- }
- }
- operation(schema, context)
- }
- // CopyProperties copies all non-nil properties from the source Schema to the schema Schema.
- func (schema *Schema) CopyProperties(source *Schema) {
- if source.Schema != nil {
- schema.Schema = source.Schema
- }
- if source.ID != nil {
- schema.ID = source.ID
- }
- if source.MultipleOf != nil {
- schema.MultipleOf = source.MultipleOf
- }
- if source.Maximum != nil {
- schema.Maximum = source.Maximum
- }
- if source.ExclusiveMaximum != nil {
- schema.ExclusiveMaximum = source.ExclusiveMaximum
- }
- if source.Minimum != nil {
- schema.Minimum = source.Minimum
- }
- if source.ExclusiveMinimum != nil {
- schema.ExclusiveMinimum = source.ExclusiveMinimum
- }
- if source.MaxLength != nil {
- schema.MaxLength = source.MaxLength
- }
- if source.MinLength != nil {
- schema.MinLength = source.MinLength
- }
- if source.Pattern != nil {
- schema.Pattern = source.Pattern
- }
- if source.AdditionalItems != nil {
- schema.AdditionalItems = source.AdditionalItems
- }
- if source.Items != nil {
- schema.Items = source.Items
- }
- if source.MaxItems != nil {
- schema.MaxItems = source.MaxItems
- }
- if source.MinItems != nil {
- schema.MinItems = source.MinItems
- }
- if source.UniqueItems != nil {
- schema.UniqueItems = source.UniqueItems
- }
- if source.MaxProperties != nil {
- schema.MaxProperties = source.MaxProperties
- }
- if source.MinProperties != nil {
- schema.MinProperties = source.MinProperties
- }
- if source.Required != nil {
- schema.Required = source.Required
- }
- if source.AdditionalProperties != nil {
- schema.AdditionalProperties = source.AdditionalProperties
- }
- if source.Properties != nil {
- schema.Properties = source.Properties
- }
- if source.PatternProperties != nil {
- schema.PatternProperties = source.PatternProperties
- }
- if source.Dependencies != nil {
- schema.Dependencies = source.Dependencies
- }
- if source.Enumeration != nil {
- schema.Enumeration = source.Enumeration
- }
- if source.Type != nil {
- schema.Type = source.Type
- }
- if source.AllOf != nil {
- schema.AllOf = source.AllOf
- }
- if source.AnyOf != nil {
- schema.AnyOf = source.AnyOf
- }
- if source.OneOf != nil {
- schema.OneOf = source.OneOf
- }
- if source.Not != nil {
- schema.Not = source.Not
- }
- if source.Definitions != nil {
- schema.Definitions = source.Definitions
- }
- if source.Title != nil {
- schema.Title = source.Title
- }
- if source.Description != nil {
- schema.Description = source.Description
- }
- if source.Default != nil {
- schema.Default = source.Default
- }
- if source.Format != nil {
- schema.Format = source.Format
- }
- if source.Ref != nil {
- schema.Ref = source.Ref
- }
- }
- // TypeIs returns true if the Type of a Schema includes the specified type
- func (schema *Schema) TypeIs(typeName string) bool {
- if schema.Type != nil {
- // the schema Type is either a string or an array of strings
- if schema.Type.String != nil {
- return (*(schema.Type.String) == typeName)
- } else if schema.Type.StringArray != nil {
- for _, n := range *(schema.Type.StringArray) {
- if n == typeName {
- return true
- }
- }
- }
- }
- return false
- }
- // ResolveRefs resolves "$ref" elements in a Schema and its children.
- // But if a reference refers to an object type, is inside a oneOf, or contains a oneOf,
- // the reference is kept and we expect downstream tools to separately model these
- // referenced schemas.
- func (schema *Schema) ResolveRefs() {
- rootSchema := schema
- count := 1
- for count > 0 {
- count = 0
- schema.applyToSchemas(
- func(schema *Schema, context string) {
- if schema.Ref != nil {
- resolvedRef, err := rootSchema.resolveJSONPointer(*(schema.Ref))
- if err != nil {
- log.Printf("%+v", err)
- } else if resolvedRef.TypeIs("object") {
- // don't substitute for objects, we'll model the referenced schema with a class
- } else if context == "OneOf" {
- // don't substitute for references inside oneOf declarations
- } else if resolvedRef.OneOf != nil {
- // don't substitute for references that contain oneOf declarations
- } else if resolvedRef.AdditionalProperties != nil {
- // don't substitute for references that look like objects
- } else {
- schema.Ref = nil
- schema.CopyProperties(resolvedRef)
- count++
- }
- }
- }, "")
- }
- }
- // resolveJSONPointer resolves JSON pointers.
- // This current implementation is very crude and custom for OpenAPI 2.0 schemas.
- // It panics for any pointer that it is unable to resolve.
- func (schema *Schema) resolveJSONPointer(ref string) (result *Schema, err error) {
- parts := strings.Split(ref, "#")
- if len(parts) == 2 {
- documentName := parts[0] + "#"
- if documentName == "#" && schema.ID != nil {
- documentName = *(schema.ID)
- }
- path := parts[1]
- document := schemas[documentName]
- pathParts := strings.Split(path, "/")
- // we currently do a very limited (hard-coded) resolution of certain paths and log errors for missed cases
- if len(pathParts) == 1 {
- return document, nil
- } else if len(pathParts) == 3 {
- switch pathParts[1] {
- case "definitions":
- dictionary := document.Definitions
- for _, pair := range *dictionary {
- if pair.Name == pathParts[2] {
- result = pair.Value
- }
- }
- case "properties":
- dictionary := document.Properties
- for _, pair := range *dictionary {
- if pair.Name == pathParts[2] {
- result = pair.Value
- }
- }
- default:
- break
- }
- }
- }
- if result == nil {
- return nil, fmt.Errorf("unresolved pointer: %+v", ref)
- }
- return result, nil
- }
- // ResolveAllOfs replaces "allOf" elements by merging their properties into the parent Schema.
- func (schema *Schema) ResolveAllOfs() {
- schema.applyToSchemas(
- func(schema *Schema, context string) {
- if schema.AllOf != nil {
- for _, allOf := range *(schema.AllOf) {
- schema.CopyProperties(allOf)
- }
- schema.AllOf = nil
- }
- }, "resolveAllOfs")
- }
- // ResolveAnyOfs replaces all "anyOf" elements with "oneOf".
- func (schema *Schema) ResolveAnyOfs() {
- schema.applyToSchemas(
- func(schema *Schema, context string) {
- if schema.AnyOf != nil {
- schema.OneOf = schema.AnyOf
- schema.AnyOf = nil
- }
- }, "resolveAnyOfs")
- }
- // return a pointer to a copy of a passed-in string
- func stringptr(input string) (output *string) {
- return &input
- }
- // CopyOfficialSchemaProperty copies a named property from the official JSON Schema definition
- func (schema *Schema) CopyOfficialSchemaProperty(name string) {
- *schema.Properties = append(*schema.Properties,
- NewNamedSchema(name,
- &Schema{Ref: stringptr("http://json-schema.org/draft-04/schema#/properties/" + name)}))
- }
- // CopyOfficialSchemaProperties copies named properties from the official JSON Schema definition
- func (schema *Schema) CopyOfficialSchemaProperties(names []string) {
- for _, name := range names {
- schema.CopyOfficialSchemaProperty(name)
- }
- }
|