Add vendor folder to git

This commit is contained in:
Lucas Käldström 2017-06-26 19:23:05 +03:00
parent 66cf5eaafb
commit 183585f56f
No known key found for this signature in database
GPG key ID: 600FEFBBD0D40D21
6916 changed files with 2629581 additions and 1 deletions

View file

@ -0,0 +1,18 @@
go get github.com/golang/protobuf/protoc-gen-go
pushd $GOPATH/src/github.com/googleapis/gnostic/generator
go build
cd ..
./generator/generator
popd
pushd $GOPATH/src/github.com/googleapis/gnostic/generator
go install
popd
pushd $GOPATH/src/github.com/googleapis/gnostic/OpenAPIv2
protoc \
--go_out=Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. OpenAPIv2.proto
go build
go install
popd

View file

@ -0,0 +1,11 @@
# The gnostic compiler generator
This directory contains code that generates a protocol buffer
representation and supporting code for a JSON schema.
It is currently used to build models of OpenAPI specifications
and extensions which are described as "vendor extensions" in
OpenAPI 2.0 and "specification extensions" in OpenAPI 3.0.
For usage information, run the "generator" binary with no
options.

View file

@ -0,0 +1,620 @@
// Copyright 2017 Google Inc. 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 main
import (
"errors"
"fmt"
"log"
"sort"
"strings"
"github.com/googleapis/gnostic/jsonschema"
)
// models a collection of types that is defined by a schema
type Domain struct {
TypeModels map[string]*TypeModel // models of the types in the domain
Prefix string // type prefix to use
Schema *jsonschema.Schema // top-level schema
PatternNames map[string]string // a configured mapping from patterns to property names
ObjectTypeRequests map[string]*TypeRequest // anonymous types implied by type instantiation
MapTypeRequests map[string]string // "NamedObject" types that will be used to implement ordered maps
Version string // OpenAPI Version ("v2" or "v3")
}
func NewDomain(schema *jsonschema.Schema, version string) *Domain {
cc := &Domain{}
cc.TypeModels = make(map[string]*TypeModel, 0)
cc.PatternNames = make(map[string]string, 0)
cc.ObjectTypeRequests = make(map[string]*TypeRequest, 0)
cc.MapTypeRequests = make(map[string]string, 0)
cc.Schema = schema
cc.Version = version
return cc
}
// Returns a capitalized name to use for a generated type
func (domain *Domain) TypeNameForStub(stub string) string {
return domain.Prefix + strings.ToUpper(stub[0:1]) + stub[1:len(stub)]
}
// Returns a capitalized name to use for a generated type based on a JSON reference
func (domain *Domain) typeNameForReference(reference string) string {
parts := strings.Split(reference, "/")
first := parts[0]
last := parts[len(parts)-1]
if first == "#" {
return domain.TypeNameForStub(last)
} else {
return "Schema"
}
}
// Returns a property name to use for a JSON reference
func (domain *Domain) propertyNameForReference(reference string) *string {
parts := strings.Split(reference, "/")
first := parts[0]
last := parts[len(parts)-1]
if first == "#" {
return &last
} else {
return nil
}
return nil
}
// Determines the item type for arrays defined by a schema
func (domain *Domain) arrayItemTypeForSchema(propertyName string, schema *jsonschema.Schema) string {
// default
itemTypeName := "Any"
if schema.Items != nil {
if schema.Items.SchemaArray != nil {
if len(*(schema.Items.SchemaArray)) > 0 {
ref := (*schema.Items.SchemaArray)[0].Ref
if ref != nil {
itemTypeName = domain.typeNameForReference(*ref)
} else {
types := (*schema.Items.SchemaArray)[0].Type
if types == nil {
// do nothing
} else if (types.StringArray != nil) && len(*(types.StringArray)) == 1 {
itemTypeName = (*types.StringArray)[0]
} else if (types.StringArray != nil) && len(*(types.StringArray)) > 1 {
itemTypeName = fmt.Sprintf("%+v", types.StringArray)
} else if types.String != nil {
itemTypeName = *(types.String)
} else {
itemTypeName = "UNKNOWN"
}
}
}
} else if schema.Items.Schema != nil {
types := schema.Items.Schema.Type
if schema.Items.Schema.Ref != nil {
itemTypeName = domain.typeNameForReference(*schema.Items.Schema.Ref)
} else if schema.Items.Schema.OneOf != nil {
// this type is implied by the "oneOf"
itemTypeName = domain.TypeNameForStub(propertyName + "Item")
domain.ObjectTypeRequests[itemTypeName] =
NewTypeRequest(itemTypeName, propertyName, schema.Items.Schema)
} else if types == nil {
// do nothing
} else if (types.StringArray != nil) && len(*(types.StringArray)) == 1 {
itemTypeName = (*types.StringArray)[0]
} else if (types.StringArray != nil) && len(*(types.StringArray)) > 1 {
itemTypeName = fmt.Sprintf("%+v", types.StringArray)
} else if types.String != nil {
itemTypeName = *(types.String)
} else {
itemTypeName = "UNKNOWN"
}
}
}
return itemTypeName
}
func (domain *Domain) buildTypeProperties(typeModel *TypeModel, schema *jsonschema.Schema) {
if schema.Properties != nil {
for _, pair := range *(schema.Properties) {
propertyName := pair.Name
propertySchema := pair.Value
if propertySchema.Ref != nil {
// the property schema is a reference, so we will add a property with the type of the referenced schema
propertyTypeName := domain.typeNameForReference(*(propertySchema.Ref))
typeProperty := NewTypeProperty()
typeProperty.Name = propertyName
typeProperty.Type = propertyTypeName
typeModel.addProperty(typeProperty)
} else if propertySchema.Type != nil {
// the property schema specifies a type, so add a property with the specified type
if propertySchema.TypeIs("string") {
typeProperty := NewTypePropertyWithNameAndType(propertyName, "string")
if propertySchema.Description != nil {
typeProperty.Description = *propertySchema.Description
}
if propertySchema.Enumeration != nil {
allowedValues := make([]string, 0)
for _, enumValue := range *propertySchema.Enumeration {
if enumValue.String != nil {
allowedValues = append(allowedValues, *enumValue.String)
}
}
typeProperty.StringEnumValues = allowedValues
}
typeModel.addProperty(typeProperty)
} else if propertySchema.TypeIs("boolean") {
typeProperty := NewTypePropertyWithNameAndType(propertyName, "bool")
if propertySchema.Description != nil {
typeProperty.Description = *propertySchema.Description
}
typeModel.addProperty(typeProperty)
} else if propertySchema.TypeIs("number") {
typeProperty := NewTypePropertyWithNameAndType(propertyName, "float")
if propertySchema.Description != nil {
typeProperty.Description = *propertySchema.Description
}
typeModel.addProperty(typeProperty)
} else if propertySchema.TypeIs("integer") {
typeProperty := NewTypePropertyWithNameAndType(propertyName, "int")
if propertySchema.Description != nil {
typeProperty.Description = *propertySchema.Description
}
typeModel.addProperty(typeProperty)
} else if propertySchema.TypeIs("object") {
// the property has an "anonymous" object schema, so define a new type for it and request its creation
anonymousObjectTypeName := domain.TypeNameForStub(propertyName)
domain.ObjectTypeRequests[anonymousObjectTypeName] =
NewTypeRequest(anonymousObjectTypeName, propertyName, propertySchema)
// add a property with the type of the requested type
typeProperty := NewTypePropertyWithNameAndType(propertyName, anonymousObjectTypeName)
if propertySchema.Description != nil {
typeProperty.Description = *propertySchema.Description
}
typeModel.addProperty(typeProperty)
} else if propertySchema.TypeIs("array") {
// the property has an array type, so define it as a repeated property of the specified type
propertyTypeName := domain.arrayItemTypeForSchema(propertyName, propertySchema)
typeProperty := NewTypePropertyWithNameAndType(propertyName, propertyTypeName)
typeProperty.Repeated = true
if propertySchema.Description != nil {
typeProperty.Description = *propertySchema.Description
}
if typeProperty.Type == "string" {
itemSchema := propertySchema.Items.Schema
if itemSchema != nil {
if itemSchema.Enumeration != nil {
allowedValues := make([]string, 0)
for _, enumValue := range *itemSchema.Enumeration {
if enumValue.String != nil {
allowedValues = append(allowedValues, *enumValue.String)
}
}
typeProperty.StringEnumValues = allowedValues
}
}
}
typeModel.addProperty(typeProperty)
} else {
log.Printf("ignoring %+v, which has an unsupported property type '%s'", propertyName, propertySchema.Type.Description())
}
} else if propertySchema.IsEmpty() {
// an empty schema can contain anything, so add an accessor for a generic object
typeName := "Any"
typeProperty := NewTypePropertyWithNameAndType(propertyName, typeName)
typeModel.addProperty(typeProperty)
} else if propertySchema.OneOf != nil {
anonymousObjectTypeName := domain.TypeNameForStub(propertyName + "Item")
domain.ObjectTypeRequests[anonymousObjectTypeName] =
NewTypeRequest(anonymousObjectTypeName, propertyName, propertySchema)
typeProperty := NewTypePropertyWithNameAndType(propertyName, anonymousObjectTypeName)
typeModel.addProperty(typeProperty)
} else if propertySchema.AnyOf != nil {
anonymousObjectTypeName := domain.TypeNameForStub(propertyName + "Item")
domain.ObjectTypeRequests[anonymousObjectTypeName] =
NewTypeRequest(anonymousObjectTypeName, propertyName, propertySchema)
typeProperty := NewTypePropertyWithNameAndType(propertyName, anonymousObjectTypeName)
typeModel.addProperty(typeProperty)
} else {
log.Printf("ignoring %s.%s, which has an unrecognized schema:\n%+v", typeModel.Name, propertyName, propertySchema.String())
}
}
}
}
func (domain *Domain) buildTypeRequirements(typeModel *TypeModel, schema *jsonschema.Schema) {
if schema.Required != nil {
typeModel.Required = (*schema.Required)
}
}
func (domain *Domain) buildPatternPropertyAccessors(typeModel *TypeModel, schema *jsonschema.Schema) {
if schema.PatternProperties != nil {
typeModel.OpenPatterns = make([]string, 0)
for _, pair := range *(schema.PatternProperties) {
propertyPattern := pair.Name
propertySchema := pair.Value
typeModel.OpenPatterns = append(typeModel.OpenPatterns, propertyPattern)
typeName := "Any"
propertyName := domain.PatternNames[propertyPattern]
if propertySchema.Ref != nil {
typeName = domain.typeNameForReference(*propertySchema.Ref)
}
propertyTypeName := fmt.Sprintf("Named%s", typeName)
property := NewTypePropertyWithNameTypeAndPattern(propertyName, propertyTypeName, propertyPattern)
property.Implicit = true
property.MapType = typeName
property.Repeated = true
domain.MapTypeRequests[property.MapType] = property.MapType
typeModel.addProperty(property)
}
}
}
func (domain *Domain) buildAdditionalPropertyAccessors(typeModel *TypeModel, schema *jsonschema.Schema) {
if schema.AdditionalProperties != nil {
if schema.AdditionalProperties.Boolean != nil {
if *schema.AdditionalProperties.Boolean == true {
typeModel.Open = true
propertyName := "additionalProperties"
typeName := "NamedAny"
property := NewTypePropertyWithNameAndType(propertyName, typeName)
property.Implicit = true
property.MapType = "Any"
property.Repeated = true
domain.MapTypeRequests[property.MapType] = property.MapType
typeModel.addProperty(property)
return
}
} else if schema.AdditionalProperties.Schema != nil {
typeModel.Open = true
schema := schema.AdditionalProperties.Schema
if schema.Ref != nil {
propertyName := "additionalProperties"
mapType := domain.typeNameForReference(*schema.Ref)
typeName := fmt.Sprintf("Named%s", mapType)
property := NewTypePropertyWithNameAndType(propertyName, typeName)
property.Implicit = true
property.MapType = mapType
property.Repeated = true
domain.MapTypeRequests[property.MapType] = property.MapType
typeModel.addProperty(property)
return
} else if schema.Type != nil {
typeName := *schema.Type.String
if typeName == "string" {
propertyName := "additionalProperties"
typeName := "NamedString"
property := NewTypePropertyWithNameAndType(propertyName, typeName)
property.Implicit = true
property.MapType = "string"
property.Repeated = true
domain.MapTypeRequests[property.MapType] = property.MapType
typeModel.addProperty(property)
return
} else if typeName == "array" {
if schema.Items != nil {
itemType := *schema.Items.Schema.Type.String
if itemType == "string" {
propertyName := "additionalProperties"
typeName := "NamedStringArray"
property := NewTypePropertyWithNameAndType(propertyName, typeName)
property.Implicit = true
property.MapType = "StringArray"
property.Repeated = true
domain.MapTypeRequests[property.MapType] = property.MapType
typeModel.addProperty(property)
return
}
}
}
} else if schema.OneOf != nil {
propertyTypeName := domain.TypeNameForStub(typeModel.Name + "Item")
propertyName := "additionalProperties"
typeName := fmt.Sprintf("Named%s", propertyTypeName)
property := NewTypePropertyWithNameAndType(propertyName, typeName)
property.Implicit = true
property.MapType = propertyTypeName
property.Repeated = true
domain.MapTypeRequests[property.MapType] = property.MapType
typeModel.addProperty(property)
domain.ObjectTypeRequests[propertyTypeName] =
NewTypeRequest(propertyTypeName, propertyName, schema)
}
}
}
}
func (domain *Domain) buildOneOfAccessors(typeModel *TypeModel, schema *jsonschema.Schema) {
oneOfs := schema.OneOf
if oneOfs == nil {
return
}
typeModel.Open = true
typeModel.OneOfWrapper = true
for _, oneOf := range *oneOfs {
if oneOf.Ref != nil {
ref := *oneOf.Ref
typeName := domain.typeNameForReference(ref)
propertyName := domain.propertyNameForReference(ref)
if propertyName != nil {
typeProperty := NewTypePropertyWithNameAndType(*propertyName, typeName)
typeModel.addProperty(typeProperty)
}
} else if oneOf.Type != nil && oneOf.Type.String != nil {
switch *oneOf.Type.String {
case "boolean":
typeProperty := NewTypePropertyWithNameAndType("boolean", "bool")
typeModel.addProperty(typeProperty)
case "integer":
typeProperty := NewTypePropertyWithNameAndType("integer", "int")
typeModel.addProperty(typeProperty)
case "number":
typeProperty := NewTypePropertyWithNameAndType("number", "float")
typeModel.addProperty(typeProperty)
case "string":
typeProperty := NewTypePropertyWithNameAndType("string", "string")
typeModel.addProperty(typeProperty)
default:
log.Printf("Unsupported oneOf:\n%+v", oneOf.String())
}
} else {
log.Printf("Unsupported oneOf:\n%+v", oneOf.String())
}
}
}
func schemaIsContainedInArray(s1 *jsonschema.Schema, s2 *jsonschema.Schema) bool {
if s2.TypeIs("array") {
if s2.Items.Schema != nil {
if s1.IsEqual(s2.Items.Schema) {
return true
} else {
return false
}
} else {
return false
}
} else {
return false
}
}
func (domain *Domain) addAnonymousAccessorForSchema(
typeModel *TypeModel,
schema *jsonschema.Schema,
repeated bool) {
ref := schema.Ref
if ref != nil {
typeName := domain.typeNameForReference(*ref)
propertyName := domain.propertyNameForReference(*ref)
if propertyName != nil {
property := NewTypePropertyWithNameAndType(*propertyName, typeName)
property.Repeated = true
typeModel.addProperty(property)
typeModel.IsItemArray = true
}
} else {
typeName := "string"
propertyName := "value"
property := NewTypePropertyWithNameAndType(propertyName, typeName)
property.Repeated = true
typeModel.addProperty(property)
typeModel.IsStringArray = true
}
}
func (domain *Domain) buildAnyOfAccessors(typeModel *TypeModel, schema *jsonschema.Schema) {
anyOfs := schema.AnyOf
if anyOfs == nil {
return
}
if len(*anyOfs) == 2 {
if schemaIsContainedInArray((*anyOfs)[0], (*anyOfs)[1]) {
//log.Printf("ARRAY OF %+v", (*anyOfs)[0].String())
schema := (*anyOfs)[0]
domain.addAnonymousAccessorForSchema(typeModel, schema, true)
} else if schemaIsContainedInArray((*anyOfs)[1], (*anyOfs)[0]) {
//log.Printf("ARRAY OF %+v", (*anyOfs)[1].String())
schema := (*anyOfs)[1]
domain.addAnonymousAccessorForSchema(typeModel, schema, true)
} else {
for _, anyOf := range *anyOfs {
ref := anyOf.Ref
if ref != nil {
typeName := domain.typeNameForReference(*ref)
propertyName := domain.propertyNameForReference(*ref)
if propertyName != nil {
property := NewTypePropertyWithNameAndType(*propertyName, typeName)
typeModel.addProperty(property)
}
} else {
typeName := "bool"
propertyName := "boolean"
property := NewTypePropertyWithNameAndType(propertyName, typeName)
typeModel.addProperty(property)
}
}
}
} else {
log.Printf("Unhandled anyOfs:\n%s", schema.String())
}
}
func (domain *Domain) buildDefaultAccessors(typeModel *TypeModel, schema *jsonschema.Schema) {
typeModel.Open = true
propertyName := "additionalProperties"
typeName := "NamedAny"
property := NewTypePropertyWithNameAndType(propertyName, typeName)
property.MapType = "Any"
property.Repeated = true
domain.MapTypeRequests[property.MapType] = property.MapType
typeModel.addProperty(property)
}
func (domain *Domain) BuildTypeForDefinition(
typeName string,
propertyName string,
schema *jsonschema.Schema) *TypeModel {
if (schema.Type == nil) || (*schema.Type.String == "object") {
return domain.buildTypeForDefinitionObject(typeName, propertyName, schema)
} else {
return nil
}
}
func (domain *Domain) buildTypeForDefinitionObject(
typeName string,
propertyName string,
schema *jsonschema.Schema) *TypeModel {
typeModel := NewTypeModel()
typeModel.Name = typeName
if schema.IsEmpty() {
domain.buildDefaultAccessors(typeModel, schema)
} else {
if schema.Description != nil {
typeModel.Description = *schema.Description
}
domain.buildTypeProperties(typeModel, schema)
domain.buildTypeRequirements(typeModel, schema)
domain.buildPatternPropertyAccessors(typeModel, schema)
domain.buildAdditionalPropertyAccessors(typeModel, schema)
domain.buildOneOfAccessors(typeModel, schema)
domain.buildAnyOfAccessors(typeModel, schema)
}
return typeModel
}
func (domain *Domain) Build() (err error) {
if (domain.Schema == nil) || (domain.Schema.Definitions == nil) {
return errors.New("missing definitions section")
}
// create a type for the top-level schema
typeName := domain.Prefix + "Document"
typeModel := NewTypeModel()
typeModel.Name = typeName
domain.buildTypeProperties(typeModel, domain.Schema)
domain.buildTypeRequirements(typeModel, domain.Schema)
domain.buildPatternPropertyAccessors(typeModel, domain.Schema)
domain.buildAdditionalPropertyAccessors(typeModel, domain.Schema)
domain.buildOneOfAccessors(typeModel, domain.Schema)
domain.buildAnyOfAccessors(typeModel, domain.Schema)
if len(typeModel.Properties) > 0 {
domain.TypeModels[typeName] = typeModel
}
// create a type for each object defined in the schema
if domain.Schema.Definitions != nil {
for _, pair := range *(domain.Schema.Definitions) {
definitionName := pair.Name
definitionSchema := pair.Value
typeName := domain.TypeNameForStub(definitionName)
typeModel := domain.BuildTypeForDefinition(typeName, definitionName, definitionSchema)
if typeModel != nil {
domain.TypeModels[typeName] = typeModel
}
}
}
// iterate over anonymous object types to be instantiated and generate a type for each
for typeName, typeRequest := range domain.ObjectTypeRequests {
domain.TypeModels[typeRequest.Name] =
domain.buildTypeForDefinitionObject(typeName, typeRequest.PropertyName, typeRequest.Schema)
}
// iterate over map item types to be instantiated and generate a type for each
mapTypeNames := make([]string, 0)
for mapTypeName, _ := range domain.MapTypeRequests {
mapTypeNames = append(mapTypeNames, mapTypeName)
}
sort.Strings(mapTypeNames)
for _, mapTypeName := range mapTypeNames {
typeName := "Named" + strings.Title(mapTypeName)
typeModel := NewTypeModel()
typeModel.Name = typeName
typeModel.Description = fmt.Sprintf(
"Automatically-generated message used to represent maps of %s as ordered (name,value) pairs.",
mapTypeName)
typeModel.IsPair = true
typeModel.PairValueType = mapTypeName
nameProperty := NewTypeProperty()
nameProperty.Name = "name"
nameProperty.Type = "string"
nameProperty.Description = "Map key"
typeModel.addProperty(nameProperty)
valueProperty := NewTypeProperty()
valueProperty.Name = "value"
valueProperty.Type = mapTypeName
valueProperty.Description = "Mapped value"
typeModel.addProperty(valueProperty)
domain.TypeModels[typeName] = typeModel
}
// add a type for string arrays
stringArrayType := NewTypeModel()
stringArrayType.Name = "StringArray"
stringProperty := NewTypeProperty()
stringProperty.Name = "value"
stringProperty.Type = "string"
stringProperty.Repeated = true
stringArrayType.addProperty(stringProperty)
domain.TypeModels[stringArrayType.Name] = stringArrayType
// add a type for "Any"
anyType := NewTypeModel()
anyType.Name = "Any"
anyType.Open = true
anyType.IsBlob = true
valueProperty := NewTypeProperty()
valueProperty.Name = "value"
valueProperty.Type = "google.protobuf.Any"
anyType.addProperty(valueProperty)
yamlProperty := NewTypeProperty()
yamlProperty.Name = "yaml"
yamlProperty.Type = "string"
anyType.addProperty(yamlProperty)
domain.TypeModels[anyType.Name] = anyType
return err
}
func (domain *Domain) sortedTypeNames() []string {
typeNames := make([]string, 0)
for typeName, _ := range domain.TypeModels {
typeNames = append(typeNames, typeName)
}
sort.Strings(typeNames)
return typeNames
}
func (domain *Domain) Description() string {
typeNames := domain.sortedTypeNames()
result := ""
for _, typeName := range typeNames {
result += domain.TypeModels[typeName].description()
}
return result
}

View file

@ -0,0 +1,638 @@
// Copyright 2017 Google Inc. 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 main
import (
"fmt"
"sort"
"strings"
"github.com/googleapis/gnostic/printer"
)
func (domain *Domain) GenerateCompiler(packageName string, license string, imports []string) string {
code := &printer.Code{}
code.Print(license)
code.Print("// THIS FILE IS AUTOMATICALLY GENERATED.\n")
// generate package declaration
code.Print("package %s\n", packageName)
code.Print("import (")
for _, filename := range imports {
code.Print("\"" + filename + "\"")
}
code.Print(")\n")
// generate a simple Version() function
code.Print("func Version() string {")
code.Print(" return \"%s\"", packageName)
code.Print("}\n")
typeNames := domain.sortedTypeNames()
// generate NewX() constructor functions for each type
for _, typeName := range typeNames {
domain.generateConstructorForType(code, typeName)
}
// generate ResolveReferences() methods for each type
for _, typeName := range typeNames {
domain.generateResolveReferencesMethodsForType(code, typeName)
}
return code.String()
}
func (domain *Domain) generateConstructorForType(code *printer.Code, typeName string) {
code.Print("func New%s(in interface{}, context *compiler.Context) (*%s, error) {", typeName, typeName)
code.Print("errors := make([]error, 0)")
typeModel := domain.TypeModels[typeName]
parentTypeName := typeName
if typeModel.IsStringArray {
code.Print("x := &TypeItem{}")
code.Print("switch in := in.(type) {")
code.Print("case string:")
code.Print(" x.Value = make([]string, 0)")
code.Print(" x.Value = append(x.Value, in)")
code.Print("case []interface{}:")
code.Print(" x.Value = make([]string, 0)")
code.Print(" for _, v := range in {")
code.Print(" value, ok := v.(string)")
code.Print(" if ok {")
code.Print(" x.Value = append(x.Value, value)")
code.Print(" } else {")
code.Print(" message := fmt.Sprintf(\"has unexpected value for string array element: %%+v (%%T)\", value, value)")
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print(" }")
code.Print(" }")
code.Print("default:")
code.Print(" message := fmt.Sprintf(\"has unexpected value for string array: %%+v (%%T)\", in, in)")
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("}")
} else if typeModel.IsItemArray {
if domain.Version == "v2" {
code.Print("x := &ItemsItem{}")
code.Print("m, ok := compiler.UnpackMap(in)")
code.Print("if !ok {")
code.Print(" message := fmt.Sprintf(\"has unexpected value for item array: %%+v (%%T)\", in, in)")
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("} else {")
code.Print(" x.Schema = make([]*Schema, 0)")
code.Print(" y, err := NewSchema(m, compiler.NewContext(\"<array>\", context))")
code.Print(" if err != nil {")
code.Print(" return nil, err")
code.Print(" }")
code.Print(" x.Schema = append(x.Schema, y)")
code.Print("}")
} else if domain.Version == "v3" {
code.Print("x := &ItemsItem{}")
code.Print("m, ok := compiler.UnpackMap(in)")
code.Print("if !ok {")
code.Print(" message := fmt.Sprintf(\"has unexpected value for item array: %%+v (%%T)\", in, in)")
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("} else {")
code.Print(" x.SchemaOrReference = make([]*SchemaOrReference, 0)")
code.Print(" y, err := NewSchemaOrReference(m, compiler.NewContext(\"<array>\", context))")
code.Print(" if err != nil {")
code.Print(" return nil, err")
code.Print(" }")
code.Print(" x.SchemaOrReference = append(x.SchemaOrReference, y)")
code.Print("}")
}
} else if typeModel.IsBlob {
code.Print("x := &Any{}")
code.Print("bytes, _ := yaml.Marshal(in)")
code.Print("x.Yaml = string(bytes)")
} else if typeModel.Name == "StringArray" {
code.Print("x := &StringArray{}")
code.Print("a, ok := in.([]interface{})")
code.Print("if !ok {")
code.Print(" message := fmt.Sprintf(\"has unexpected value for StringArray: %%+v (%%T)\", in, in)")
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("} else {")
code.Print(" x.Value = make([]string, 0)")
code.Print(" for _, s := range a {")
code.Print(" x.Value = append(x.Value, s.(string))")
code.Print(" }")
code.Print("}")
} else if typeModel.Name == "Primitive" {
code.Print(" x := &Primitive{}")
code.Print(" matched := false")
code.Print(" switch in := in.(type) {")
code.Print(" case bool:")
code.Print(" x.Oneof = &Primitive_Boolean{Boolean: in}")
code.Print(" matched = true")
code.Print(" case string:")
code.Print(" x.Oneof = &Primitive_String_{String_: in}")
code.Print(" matched = true")
code.Print(" case int64:")
code.Print(" x.Oneof = &Primitive_Integer{Integer: in}")
code.Print(" matched = true")
code.Print(" case int32:")
code.Print(" x.Oneof = &Primitive_Integer{Integer: int64(in)}")
code.Print(" matched = true")
code.Print(" case int:")
code.Print(" x.Oneof = &Primitive_Integer{Integer: int64(in)}")
code.Print(" matched = true")
code.Print(" case float64:")
code.Print(" x.Oneof = &Primitive_Number{Number: in}")
code.Print(" matched = true")
code.Print(" case float32:")
code.Print(" x.Oneof = &Primitive_Number{Number: float64(in)}")
code.Print(" matched = true")
code.Print(" }")
code.Print(" if matched {")
code.Print(" // since the oneof matched one of its possibilities, discard any matching errors")
code.Print(" errors = make([]error, 0)")
code.Print(" }")
} else if typeModel.Name == "SpecificationExtension" {
code.Print(" x := &SpecificationExtension{}")
code.Print(" matched := false")
code.Print(" switch in := in.(type) {")
code.Print(" case bool:")
code.Print(" x.Oneof = &SpecificationExtension_Boolean{Boolean: in}")
code.Print(" matched = true")
code.Print(" case string:")
code.Print(" x.Oneof = &SpecificationExtension_String_{String_: in}")
code.Print(" matched = true")
code.Print(" case int64:")
code.Print(" x.Oneof = &SpecificationExtension_Integer{Integer: in}")
code.Print(" matched = true")
code.Print(" case int32:")
code.Print(" x.Oneof = &SpecificationExtension_Integer{Integer: int64(in)}")
code.Print(" matched = true")
code.Print(" case int:")
code.Print(" x.Oneof = &SpecificationExtension_Integer{Integer: int64(in)}")
code.Print(" matched = true")
code.Print(" case float64:")
code.Print(" x.Oneof = &SpecificationExtension_Number{Number: in}")
code.Print(" matched = true")
code.Print(" case float32:")
code.Print(" x.Oneof = &SpecificationExtension_Number{Number: float64(in)}")
code.Print(" matched = true")
code.Print(" }")
code.Print(" if matched {")
code.Print(" // since the oneof matched one of its possibilities, discard any matching errors")
code.Print(" errors = make([]error, 0)")
code.Print(" }")
} else {
oneOfWrapper := typeModel.OneOfWrapper
code.Print("x := &%s{}", typeName)
if oneOfWrapper {
code.Print("matched := false")
}
unpackAtTop := !oneOfWrapper || len(typeModel.Required) > 0
if unpackAtTop {
code.Print("m, ok := compiler.UnpackMap(in)")
code.Print("if !ok {")
code.Print(" message := fmt.Sprintf(\"has unexpected value: %%+v (%%T)\", in, in)")
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("} else {")
}
if len(typeModel.Required) > 0 {
// verify that map includes all required keys
keyString := ""
sort.Strings(typeModel.Required)
for _, k := range typeModel.Required {
if keyString != "" {
keyString += ","
}
keyString += "\""
keyString += k
keyString += "\""
}
code.Print("requiredKeys := []string{%s}", keyString)
code.Print("missingKeys := compiler.MissingKeysInMap(m, requiredKeys)")
code.Print("if len(missingKeys) > 0 {")
code.Print(" message := fmt.Sprintf(\"is missing required %%s: %%+v\", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, \", \"))")
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("}")
}
if !typeModel.Open {
// verify that map has no unspecified keys
allowedKeys := make([]string, 0)
for _, property := range typeModel.Properties {
if !property.Implicit {
allowedKeys = append(allowedKeys, property.Name)
}
}
sort.Strings(allowedKeys)
allowedKeyString := ""
for _, allowedKey := range allowedKeys {
if allowedKeyString != "" {
allowedKeyString += ","
}
allowedKeyString += "\""
allowedKeyString += allowedKey
allowedKeyString += "\""
}
allowedPatternString := ""
if typeModel.OpenPatterns != nil {
for _, pattern := range typeModel.OpenPatterns {
if allowedPatternString != "" {
allowedPatternString += ","
}
allowedPatternString += "\""
allowedPatternString += pattern
allowedPatternString += "\""
}
}
// verify that map includes only allowed keys and patterns
code.Print("allowedKeys := []string{%s}", allowedKeyString)
code.Print("allowedPatterns := []string{%s}", allowedPatternString)
code.Print("invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns)")
code.Print("if len(invalidKeys) > 0 {")
code.Print(" message := fmt.Sprintf(\"has invalid %%s: %%+v\", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, \", \"))")
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("}")
}
var fieldNumber = 0
for _, propertyModel := range typeModel.Properties {
propertyName := propertyModel.Name
fieldNumber += 1
propertyType := propertyModel.Type
if propertyType == "int" {
propertyType = "int64"
}
var displayName = propertyName
if displayName == "$ref" {
displayName = "_ref"
}
if displayName == "$schema" {
displayName = "_schema"
}
displayName = camelCaseToSnakeCase(displayName)
var line = fmt.Sprintf("%s %s = %d;", propertyType, displayName, fieldNumber)
if propertyModel.Repeated {
line = "repeated " + line
}
code.Print("// " + line)
fieldName := strings.Title(propertyName)
if propertyName == "$ref" {
fieldName = "XRef"
}
typeModel, typeFound := domain.TypeModels[propertyType]
if typeFound && !typeModel.IsPair {
if propertyModel.Repeated {
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
code.Print("if (v%d != nil) {", fieldNumber)
code.Print(" // repeated %s", typeModel.Name)
code.Print(" x.%s = make([]*%s, 0)", fieldName, typeModel.Name)
code.Print(" a, ok := v%d.([]interface{})", fieldNumber)
code.Print(" if ok {")
code.Print(" for _, item := range a {")
code.Print(" y, err := New%s(item, compiler.NewContext(\"%s\", context))", typeModel.Name, propertyName)
code.Print(" if err != nil {")
code.Print(" errors = append(errors, err)")
code.Print(" }")
code.Print(" x.%s = append(x.%s, y)", fieldName, fieldName)
code.Print(" }")
code.Print(" }")
code.Print("}")
} else {
if oneOfWrapper {
code.Print("{")
if !unpackAtTop {
code.Print(" m, ok := compiler.UnpackMap(in)")
code.Print(" if ok {")
}
code.Print(" // errors might be ok here, they mean we just don't have the right subtype")
code.Print(" t, matching_error := New%s(m, compiler.NewContext(\"%s\", context))", typeModel.Name, propertyName)
code.Print(" if matching_error == nil {")
code.Print(" x.Oneof = &%s_%s{%s: t}", parentTypeName, typeModel.Name, typeModel.Name)
code.Print(" matched = true")
code.Print(" } else {")
code.Print(" errors = append(errors, matching_error)")
code.Print(" }")
if !unpackAtTop {
code.Print(" }")
}
code.Print("}")
} else {
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
code.Print("if (v%d != nil) {", fieldNumber)
code.Print(" var err error")
code.Print(" x.%s, err = New%s(v%d, compiler.NewContext(\"%s\", context))",
fieldName, typeModel.Name, fieldNumber, propertyName)
code.Print(" if err != nil {")
code.Print(" errors = append(errors, err)")
code.Print(" }")
code.Print("}")
}
}
} else if propertyType == "string" {
if propertyModel.Repeated {
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
code.Print("if (v%d != nil) {", fieldNumber)
code.Print(" v, ok := v%d.([]interface{})", fieldNumber)
code.Print(" if ok {")
code.Print(" x.%s = compiler.ConvertInterfaceArrayToStringArray(v)", fieldName)
code.Print(" } else {")
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("}")
if propertyModel.StringEnumValues != nil {
code.Print("// check for valid enum values")
code.Print("// %+v", propertyModel.StringEnumValues)
stringArrayLiteral := "[]string{"
for i, item := range propertyModel.StringEnumValues {
if i > 0 {
stringArrayLiteral += ","
}
stringArrayLiteral += "\"" + item + "\""
}
stringArrayLiteral += "}"
code.Print("if ok && !compiler.StringArrayContainsValues(%s, x.%s) {", stringArrayLiteral, fieldName)
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v\", v%d)", propertyName, fieldNumber)
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("}")
}
code.Print("}")
} else {
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
code.Print("if (v%d != nil) {", fieldNumber)
code.Print(" x.%s, ok = v%d.(string)", fieldName, fieldNumber)
code.Print(" if !ok {")
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print(" }")
if propertyModel.StringEnumValues != nil {
code.Print("// check for valid enum values")
code.Print("// %+v", propertyModel.StringEnumValues)
stringArrayLiteral := "[]string{"
for i, item := range propertyModel.StringEnumValues {
if i > 0 {
stringArrayLiteral += ","
}
stringArrayLiteral += "\"" + item + "\""
}
stringArrayLiteral += "}"
code.Print("if ok && !compiler.StringArrayContainsValue(%s, x.%s) {", stringArrayLiteral, fieldName)
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print("}")
}
code.Print("}")
}
} else if propertyType == "float" {
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
code.Print("if (v%d != nil) {", fieldNumber)
code.Print(" switch v%d := v%d.(type) {", fieldNumber, fieldNumber)
code.Print(" case float64:")
code.Print(" x.%s = v%d", fieldName, fieldNumber)
code.Print(" case float32:")
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
code.Print(" case uint64:")
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
code.Print(" case uint32:")
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
code.Print(" case int64:")
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
code.Print(" case int32:")
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
code.Print(" case int:")
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
code.Print(" default:")
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print(" }")
code.Print("}")
} else if propertyType == "int64" {
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
code.Print("if (v%d != nil) {", fieldNumber)
code.Print(" t, ok := v%d.(int)", fieldNumber)
code.Print(" if ok {")
code.Print(" x.%s = int64(t)", fieldName)
code.Print(" } else {")
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print(" }")
code.Print("}")
} else if propertyType == "bool" {
if oneOfWrapper {
propertyName := "Boolean"
code.Print("boolValue, ok := in.(bool)")
code.Print("if ok {")
code.Print(" x.Oneof = &%s_%s{%s: boolValue}", parentTypeName, propertyName, propertyName)
code.Print("}")
} else {
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
code.Print("if (v%d != nil) {", fieldNumber)
code.Print(" x.%s, ok = v%d.(bool)", fieldName, fieldNumber)
code.Print(" if !ok {")
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
code.Print(" errors = append(errors, compiler.NewError(context, message))")
code.Print(" }")
code.Print("}")
}
} else {
mapTypeName := propertyModel.MapType
if mapTypeName != "" {
code.Print("// MAP: %s %s", mapTypeName, propertyModel.Pattern)
if mapTypeName == "string" {
code.Print("x.%s = make([]*NamedString, 0)", fieldName)
} else {
code.Print("x.%s = make([]*Named%s, 0)", fieldName, mapTypeName)
}
code.Print("for _, item := range m {")
code.Print("k, ok := item.Key.(string)")
code.Print("if ok {")
code.Print("v := item.Value")
if propertyModel.Pattern != "" {
code.Print("if compiler.PatternMatches(\"%s\", k) {", propertyModel.Pattern)
}
code.Print("pair := &Named" + strings.Title(mapTypeName) + "{}")
code.Print("pair.Name = k")
if mapTypeName == "string" {
code.Print("pair.Value = v.(string)")
} else if mapTypeName == "Any" {
code.Print("result := &Any{}")
code.Print("handled, resultFromExt, err := compiler.HandleExtension(context, v, k)")
code.Print("if handled {")
code.Print(" if err != nil {")
code.Print(" errors = append(errors, err)")
code.Print(" } else {")
code.Print(" bytes, _ := yaml.Marshal(v)")
code.Print(" result.Yaml = string(bytes)")
code.Print(" result.Value = resultFromExt")
code.Print(" pair.Value = result")
code.Print(" }")
code.Print("} else {")
code.Print(" pair.Value, err = NewAny(v, compiler.NewContext(k, context))")
code.Print(" if err != nil {")
code.Print(" errors = append(errors, err)")
code.Print(" }")
code.Print("}")
} else {
code.Print("var err error")
code.Print("pair.Value, err = New%s(v, compiler.NewContext(k, context))", mapTypeName)
code.Print("if err != nil {")
code.Print(" errors = append(errors, err)")
code.Print("}")
}
code.Print("x.%s = append(x.%s, pair)", fieldName, fieldName)
if propertyModel.Pattern != "" {
code.Print("}")
}
code.Print("}")
code.Print("}")
} else {
code.Print("// TODO: %s", propertyType)
}
}
}
if unpackAtTop {
code.Print("}")
}
if oneOfWrapper {
code.Print("if matched {")
code.Print(" // since the oneof matched one of its possibilities, discard any matching errors")
code.Print(" errors = make([]error, 0)")
code.Print("}")
}
}
// assumes that the return value is in a variable named "x"
code.Print(" return x, compiler.NewErrorGroupOrNil(errors)")
code.Print("}\n")
}
// ResolveReferences() methods
func (domain *Domain) generateResolveReferencesMethodsForType(code *printer.Code, typeName string) {
code.Print("func (m *%s) ResolveReferences(root string) (interface{}, error) {", typeName)
code.Print("errors := make([]error, 0)")
typeModel := domain.TypeModels[typeName]
if typeModel.OneOfWrapper {
// call ResolveReferences on whatever is in the Oneof.
for _, propertyModel := range typeModel.Properties {
propertyType := propertyModel.Type
_, typeFound := domain.TypeModels[propertyType]
if typeFound {
code.Print("{")
code.Print("p, ok := m.Oneof.(*%s_%s)", typeName, propertyType)
code.Print("if ok {")
if propertyType == "JsonReference" { // Special case for OpenAPI
code.Print("info, err := p.%s.ResolveReferences(root)", propertyType)
code.Print("if err != nil {")
code.Print(" return nil, err")
code.Print("} else if info != nil {")
code.Print(" n, err := New%s(info, nil)", typeName)
code.Print(" if err != nil {")
code.Print(" return nil, err")
code.Print(" } else if n != nil {")
code.Print(" *m = *n")
code.Print(" return nil, nil")
code.Print(" }")
code.Print("}")
} else {
code.Print("_, err := p.%s.ResolveReferences(root)", propertyType)
code.Print("if err != nil {")
code.Print(" return nil, err")
code.Print("}")
}
code.Print("}")
code.Print("}")
}
}
} else {
for _, propertyModel := range typeModel.Properties {
propertyName := propertyModel.Name
var displayName = propertyName
if displayName == "$ref" {
displayName = "_ref"
}
if displayName == "$schema" {
displayName = "_schema"
}
displayName = camelCaseToSnakeCase(displayName)
fieldName := strings.Title(propertyName)
if propertyName == "$ref" {
fieldName = "XRef"
code.Print("if m.XRef != \"\" {")
//code.Print("log.Printf(\"%s reference to resolve %%+v\", m.XRef)", typeName)
code.Print("info, err := compiler.ReadInfoForRef(root, m.XRef)")
code.Print("if err != nil {")
code.Print(" return nil, err")
code.Print("}")
//code.Print("log.Printf(\"%%+v\", info)")
if len(typeModel.Properties) > 1 {
code.Print("if info != nil {")
code.Print(" replacement, err := New%s(info, nil)", typeName)
code.Print(" if err == nil {")
code.Print(" *m = *replacement")
code.Print(" return m.ResolveReferences(root)")
code.Print(" }")
code.Print("}")
}
code.Print("return info, nil")
code.Print("}")
}
if !propertyModel.Repeated {
propertyType := propertyModel.Type
typeModel, typeFound := domain.TypeModels[propertyType]
if typeFound && !typeModel.IsPair {
code.Print("if m.%s != nil {", fieldName)
code.Print(" _, err := m.%s.ResolveReferences(root)", fieldName)
code.Print(" if err != nil {")
code.Print(" errors = append(errors, err)")
code.Print(" }")
code.Print("}")
}
} else {
propertyType := propertyModel.Type
_, typeFound := domain.TypeModels[propertyType]
if typeFound {
code.Print("for _, item := range m.%s {", fieldName)
code.Print("if item != nil {")
code.Print(" _, err := item.ResolveReferences(root)")
code.Print(" if err != nil {")
code.Print(" errors = append(errors, err)")
code.Print(" }")
code.Print("}")
code.Print("}")
}
}
}
}
code.Print(" return nil, compiler.NewErrorGroupOrNil(errors)")
code.Print("}\n")
}

View file

@ -0,0 +1,297 @@
// Copyright 2017 Google Inc. 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 main
import (
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
"github.com/googleapis/gnostic/compiler"
"github.com/googleapis/gnostic/jsonschema"
"github.com/googleapis/gnostic/printer"
)
var PROTO_OPTIONS_FOR_EXTENSION = []ProtoOption{
ProtoOption{
Name: "java_multiple_files",
Value: "true",
Comment: "// This option lets the proto compiler generate Java code inside the package\n" +
"// name (see below) instead of inside an outer class. It creates a simpler\n" +
"// developer experience by reducing one-level of name nesting and be\n" +
"// consistent with most programming languages that don't support outer classes.",
},
ProtoOption{
Name: "java_outer_classname",
Value: "VendorExtensionProto",
Comment: "// The Java outer classname should be the filename in UpperCamelCase. This\n" +
"// class is only used to hold proto descriptor, so developers don't need to\n" +
"// work with it directly.",
},
}
const additionalCompilerCodeWithMain = "" +
"func handleExtension(extensionName string, info yaml.MapSlice) (bool, proto.Message, error) {\n" +
" switch extensionName {\n" +
" // All supported extensions\n" +
" %s\n" +
" default:\n" +
" return false, nil, nil\n" +
" }\n" +
"}\n" +
"\n" +
"func main() {\n" +
" openapiextension_v1.ProcessExtension(handleExtension)\n" +
"}\n"
const caseString = "\n" +
"case \"%s\":\n" +
"newObject, err := %s.New%s(info, compiler.NewContext(\"$root\", nil))\n" +
"return true, newObject, err"
func GenerateMainFile(packageName string, license string, codeBody string, imports []string) string {
code := &printer.Code{}
code.Print(license)
code.Print("// THIS FILE IS AUTOMATICALLY GENERATED.\n")
// generate package declaration
code.Print("package %s\n", packageName)
code.Print("import (")
for _, filename := range imports {
code.Print("\"" + filename + "\"")
}
code.Print(")\n")
code.Print(codeBody)
return code.String()
}
func getBaseFileNameWithoutExt(filePath string) string {
tmp := filepath.Base(filePath)
return tmp[0 : len(tmp)-len(filepath.Ext(tmp))]
}
func toProtoPackageName(input string) string {
var out = ""
nonAlphaNumeric := regexp.MustCompile("[^0-9A-Za-z_]+")
input = nonAlphaNumeric.ReplaceAllString(input, "")
for index, character := range input {
if character >= 'A' && character <= 'Z' {
if index > 0 && input[index-1] != '_' {
out += "_"
}
out += string(character - 'A' + 'a')
} else {
out += string(character)
}
}
return out
}
func GenerateExtension(schemaFile string, outDir string) error {
outFileBaseName := getBaseFileNameWithoutExt(schemaFile)
extensionNameWithoutXDashPrefix := outFileBaseName[len("x-"):]
outDir = path.Join(outDir, "openapi_extensions_"+extensionNameWithoutXDashPrefix)
protoPackage := toProtoPackageName(extensionNameWithoutXDashPrefix)
protoPackageName := strings.ToLower(protoPackage)
goPackageName := protoPackageName
protoOutDirectory := outDir + "/" + "proto"
var err error
project_root := os.Getenv("GOPATH") + "/src/github.com/googleapis/gnostic/"
baseSchema, err := jsonschema.NewSchemaFromFile(project_root + "jsonschema/schema.json")
if err != nil {
return err
}
baseSchema.ResolveRefs()
baseSchema.ResolveAllOfs()
openapiSchema, err := jsonschema.NewSchemaFromFile(schemaFile)
if err != nil {
return err
}
openapiSchema.ResolveRefs()
openapiSchema.ResolveAllOfs()
// build a simplified model of the types described by the schema
cc := NewDomain(openapiSchema, "v2") // TODO fix for OpenAPI v3
// create a type for each object defined in the schema
extensionToMessage := make(map[string]string)
schemaErrors := make([]error, 0)
if cc.Schema.Definitions != nil {
for _, pair := range *(cc.Schema.Definitions) {
definitionName := pair.Name
definitionSchema := pair.Value
// ensure the id field is set
if definitionSchema.Id == nil || len(*(definitionSchema.Id)) == 0 {
schemaErrors = append(schemaErrors,
errors.New(
fmt.Sprintf("Schema %s has no 'id' field, which must match the name of the OpenAPI extension that the schema represents.\n", definitionName)))
} else {
if _, ok := extensionToMessage[*(definitionSchema.Id)]; ok {
schemaErrors = append(schemaErrors,
errors.New(
fmt.Sprintf("Schema %s and %s have the same 'id' field value.\n",
definitionName, extensionToMessage[*(definitionSchema.Id)])))
}
extensionToMessage[*(definitionSchema.Id)] = definitionName
}
typeName := cc.TypeNameForStub(definitionName)
typeModel := cc.BuildTypeForDefinition(typeName, definitionName, definitionSchema)
if typeModel != nil {
cc.TypeModels[typeName] = typeModel
}
}
}
if len(schemaErrors) > 0 {
// error has been reported.
return compiler.NewErrorGroupOrNil(schemaErrors)
}
err = os.MkdirAll(outDir, os.ModePerm)
if err != nil {
return err
}
err = os.MkdirAll(protoOutDirectory, os.ModePerm)
if err != nil {
return err
}
// generate the protocol buffer description
PROTO_OPTIONS := append(PROTO_OPTIONS_FOR_EXTENSION,
ProtoOption{Name: "java_package", Value: "org.openapi.extension." + strings.ToLower(protoPackage), Comment: "// The Java package name must be proto package name with proper prefix."},
ProtoOption{Name: "objc_class_prefix", Value: strings.ToLower(protoPackage),
Comment: "// A reasonable prefix for the Objective-C symbols generated from the package.\n" +
"// It should at a minimum be 3 characters long, all uppercase, and convention\n" +
"// is to use an abbreviation of the package name. Something short, but\n" +
"// hopefully unique enough to not conflict with things that may come along in\n" +
"// the future. 'GPB' is reserved for the protocol buffer implementation itself.",
})
proto := cc.GenerateProto(protoPackageName, LICENSE, PROTO_OPTIONS, nil)
protoFilename := path.Join(protoOutDirectory, outFileBaseName+".proto")
err = ioutil.WriteFile(protoFilename, []byte(proto), 0644)
if err != nil {
return err
}
// generate the compiler
compiler := cc.GenerateCompiler(goPackageName, LICENSE, []string{
"fmt",
"strings",
"github.com/googleapis/gnostic/compiler",
})
goFilename := path.Join(protoOutDirectory, outFileBaseName+".go")
err = ioutil.WriteFile(goFilename, []byte(compiler), 0644)
if err != nil {
return err
}
err = exec.Command(runtime.GOROOT()+"/bin/gofmt", "-w", goFilename).Run()
// generate the main file.
outDirRelativeToGoPathSrc := strings.Replace(outDir, path.Join(os.Getenv("GOPATH"), "src")+"/", "", 1)
imports := []string{
"gopkg.in/yaml.v2",
"github.com/golang/protobuf/proto",
"github.com/googleapis/gnostic/extensions",
"github.com/googleapis/gnostic/compiler",
outDirRelativeToGoPathSrc + "/" + "proto",
}
var extensionNameKeys []string
for k := range extensionToMessage {
extensionNameKeys = append(extensionNameKeys, k)
}
sort.Strings(extensionNameKeys)
var cases string
for _, extensionName := range extensionNameKeys {
cases += fmt.Sprintf(caseString, extensionName, goPackageName, extensionToMessage[extensionName])
}
extMainCode := fmt.Sprintf(additionalCompilerCodeWithMain, cases)
main := GenerateMainFile("main", LICENSE, extMainCode, imports)
mainFileName := path.Join(outDir, "main.go")
err = ioutil.WriteFile(mainFileName, []byte(main), 0644)
if err != nil {
return err
}
// format the compiler
return exec.Command(runtime.GOROOT()+"/bin/gofmt", "-w", mainFileName).Run()
}
func ProcessExtensionGenCommandline(usage string) error {
outDir := ""
schameFile := ""
extParamRegex, _ := regexp.Compile("--(.+)=(.+)")
for i, arg := range os.Args {
if i == 0 {
continue // skip the tool name
}
var m [][]byte
if m = extParamRegex.FindSubmatch([]byte(arg)); m != nil {
flagName := string(m[1])
flagValue := string(m[2])
switch flagName {
case "out_dir":
outDir = flagValue
default:
fmt.Printf("Unknown option: %s.\n%s\n", arg, usage)
os.Exit(-1)
}
} else if arg == "--extension" {
continue
} else if arg[0] == '-' {
fmt.Printf("Unknown option: %s.\n%s\n", arg, usage)
os.Exit(-1)
} else {
schameFile = arg
}
}
if schameFile == "" {
fmt.Printf("No input json schema specified.\n%s\n", usage)
os.Exit(-1)
}
if outDir == "" {
fmt.Printf("Missing output directive.\n%s\n", usage)
os.Exit(-1)
}
if !strings.HasPrefix(getBaseFileNameWithoutExt(schameFile), "x-") {
fmt.Printf("Schema file name has to start with 'x-'.\n%s\n", usage)
os.Exit(-1)
}
return GenerateExtension(schameFile, outDir)
}

View file

@ -0,0 +1,118 @@
// Copyright 2017 Google Inc. 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 main
import (
"fmt"
"strings"
"github.com/googleapis/gnostic/printer"
)
type ProtoOption struct {
Name string
Value string
Comment string
}
func (domain *Domain) GenerateProto(packageName string, license string, options []ProtoOption, imports []string) string {
code := &printer.Code{}
code.Print(license)
code.Print("// THIS FILE IS AUTOMATICALLY GENERATED.")
code.Print()
code.Print("syntax = \"proto3\";")
code.Print()
code.Print("package " + packageName + ";")
for _, importString := range imports {
code.Print()
code.Print("import \"" + importString + "\";")
}
code.Print()
// generate option declarations
for _, option := range options {
commentLines := strings.Split(option.Comment, "\n")
for _, commentLine := range commentLines {
code.Print(commentLine)
}
line := "option " + option.Name + " = "
if option.Value == "true" || option.Value == "false" {
line += option.Value
} else {
line += "\"" + option.Value + "\""
}
line += ";\n"
code.Print(line)
}
// generate message definitions
typeNames := domain.sortedTypeNames()
for _, typeName := range typeNames {
typeModel := domain.TypeModels[typeName]
if typeModel.Description != "" {
code.Print("// %s", typeModel.Description)
}
code.Print("message %s {", typeName)
code.Indent()
if typeModel.OneOfWrapper {
code.Print("oneof oneof {")
code.Indent()
}
var fieldNumber = 0
for _, propertyModel := range typeModel.Properties {
if propertyModel.Description != "" {
code.Print("// %s", propertyModel.Description)
}
propertyName := propertyModel.Name
fieldNumber += 1
propertyType := propertyModel.Type
if propertyType == "int" {
propertyType = "int64"
}
if propertyType == "float" {
propertyType = "double"
}
// TODO may be remove this.
if propertyType == "blob" {
propertyType = "string"
}
var displayName = propertyName
if displayName == "$ref" {
displayName = "_ref"
}
if displayName == "$schema" {
displayName = "_schema"
}
displayName = camelCaseToSnakeCase(displayName)
var line = fmt.Sprintf("%s %s = %d;", propertyType, displayName, fieldNumber)
if propertyModel.Repeated {
line = "repeated " + line
}
code.Print(line)
}
if typeModel.OneOfWrapper {
code.Outdent()
code.Print("}")
}
code.Outdent()
code.Print("}")
code.Print()
}
return code.String()
}

View file

@ -0,0 +1,34 @@
// Copyright 2017 Google Inc. 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 main
// Returns a "snake case" form of a camel-cased string.
func camelCaseToSnakeCase(input string) string {
var out = ""
for index, runeValue := range input {
//fmt.Printf("%#U starts at byte position %d\n", runeValue, index)
if runeValue >= 'A' && runeValue <= 'Z' {
if index > 0 {
out += "_"
}
out += string(runeValue - 'A' + 'a')
} else {
out += string(runeValue)
}
}
return out
}

235
vendor/github.com/googleapis/gnostic/generator/main.go generated vendored Normal file
View file

@ -0,0 +1,235 @@
// Copyright 2017 Google Inc. 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.
// generator generates Protocol Buffer models and support code from
// JSON Schemas. It is used to generate representations of the
// OpenAPI Specification and vendor and specification extensions
// that are added by third-party OpenAPI authors.
package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"runtime"
"strings"
"github.com/googleapis/gnostic/jsonschema"
)
const LICENSE = "" +
"// Copyright 2017 Google Inc. All Rights Reserved.\n" +
"//\n" +
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
"// you may not use this file except in compliance with the License.\n" +
"// You may obtain a copy of the License at\n" +
"//\n" +
"// http://www.apache.org/licenses/LICENSE-2.0\n" +
"//\n" +
"// Unless required by applicable law or agreed to in writing, software\n" +
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
"// See the License for the specific language governing permissions and\n" +
"// limitations under the License.\n"
func proto_options(packageName string) []ProtoOption {
return []ProtoOption{
ProtoOption{
Name: "java_multiple_files",
Value: "true",
Comment: "// This option lets the proto compiler generate Java code inside the package\n" +
"// name (see below) instead of inside an outer class. It creates a simpler\n" +
"// developer experience by reducing one-level of name nesting and be\n" +
"// consistent with most programming languages that don't support outer classes.",
},
ProtoOption{
Name: "java_outer_classname",
Value: "OpenAPIProto",
Comment: "// The Java outer classname should be the filename in UpperCamelCase. This\n" +
"// class is only used to hold proto descriptor, so developers don't need to\n" +
"// work with it directly.",
},
ProtoOption{
Name: "java_package",
Value: "org." + packageName,
Comment: "// The Java package name must be proto package name with proper prefix.",
},
ProtoOption{
Name: "objc_class_prefix",
Value: "OAS",
Comment: "// A reasonable prefix for the Objective-C symbols generated from the package.\n" +
"// It should at a minimum be 3 characters long, all uppercase, and convention\n" +
"// is to use an abbreviation of the package name. Something short, but\n" +
"// hopefully unique enough to not conflict with things that may come along in\n" +
"// the future. 'GPB' is reserved for the protocol buffer implementation itself.",
},
}
}
func GenerateOpenAPIModel(version string) error {
var input string
var filename string
var proto_packagename string
var extension_name string
switch version {
case "v2":
input = "openapi-2.0.json"
filename = "OpenAPIv2"
proto_packagename = "openapi.v2"
extension_name = "vendorExtension"
case "v3":
input = "openapi-3.0.json"
filename = "OpenAPIv3"
proto_packagename = "openapi.v3"
extension_name = "specificationExtension"
default:
return errors.New(fmt.Sprintf("Unknown OpenAPI version %s", version))
}
go_packagename := strings.Replace(proto_packagename, ".", "_", -1)
project_root := os.Getenv("GOPATH") + "/src/github.com/googleapis/gnostic/"
base_schema, err := jsonschema.NewSchemaFromFile(project_root + "jsonschema/schema.json")
if err != nil {
return err
}
base_schema.ResolveRefs()
base_schema.ResolveAllOfs()
openapi_schema, err := jsonschema.NewSchemaFromFile(project_root + filename + "/" + input)
if err != nil {
return err
}
openapi_schema.ResolveRefs()
openapi_schema.ResolveAllOfs()
// build a simplified model of the types described by the schema
cc := NewDomain(openapi_schema, version)
// generators will map these patterns to the associated property names
// these pattern names are a bit of a hack until we find a more automated way to obtain them
cc.PatternNames = map[string]string{
"^x-": extension_name,
// v2
"^/": "path",
"^([0-9]{3})$|^(default)$": "responseCode",
// v3
"^([0-9]{3})$": "responseCode",
"{property}": "property",
"{name}": "name",
"{expression}": "expression",
"/{path}": "path",
"{media-type}": "mediaType",
}
err = cc.Build()
if err != nil {
return err
}
if true {
log.Printf("Type Model:\n%s", cc.Description())
}
// ensure that the target directory exists
err = os.MkdirAll(filename, 0755)
if err != nil {
return err
}
// generate the protocol buffer description
proto := cc.GenerateProto(proto_packagename, LICENSE,
proto_options(go_packagename), []string{"google/protobuf/any.proto"})
proto_filename := project_root + filename + "/" + filename + ".proto"
err = ioutil.WriteFile(proto_filename, []byte(proto), 0644)
if err != nil {
return err
}
// generate the compiler
compiler := cc.GenerateCompiler(go_packagename, LICENSE, []string{
"fmt",
"gopkg.in/yaml.v2",
"strings",
"github.com/googleapis/gnostic/compiler",
})
go_filename := project_root + filename + "/" + filename + ".go"
err = ioutil.WriteFile(go_filename, []byte(compiler), 0644)
if err != nil {
return err
}
// format the compiler
return exec.Command(runtime.GOROOT()+"/bin/gofmt", "-w", go_filename).Run()
}
func usage() string {
return fmt.Sprintf(`
Usage: %s [OPTIONS]
Options:
--v2
Generate Protocol Buffer representation and support code for OpenAPI v2.
Files are read from and written to appropriate locations in the gnostic project directory.
--v3
Generate Protocol Buffer representation and support code for OpenAPI v3
Files are read from and written to appropriate locations in the gnostic project directory.
--extension EXTENSION_SCHEMA [EXTENSIONOPTIONS]
Generate a gnostic extension that reads a set of OpenAPI extensions.
EXTENSION_SCHEMA is the json schema for the OpenAPI extensions to be supported.
EXTENSION_OPTIONS
--out_dir=PATH: Location for writing extension models and support code.
`, path.Base(os.Args[0]))
}
func main() {
var openapi_version = ""
var generate_extensions = false
for i, arg := range os.Args {
if i == 0 {
continue // skip the tool name
}
if arg == "--v2" {
openapi_version = "v2"
} else if arg == "--v3" {
openapi_version = "v3"
} else if arg == "--extension" {
generate_extensions = true
break
} else {
fmt.Printf("Unknown option: %s.\n%s\n", arg, usage())
os.Exit(-1)
}
}
if openapi_version != "" {
err := GenerateOpenAPIModel(openapi_version)
if err != nil {
fmt.Printf("%+v\n", err)
}
} else if generate_extensions {
err := ProcessExtensionGenCommandline(usage())
if err != nil {
fmt.Printf("%+v\n", err)
}
} else {
fmt.Printf("%s\n", usage())
}
}

117
vendor/github.com/googleapis/gnostic/generator/types.go generated vendored Normal file
View file

@ -0,0 +1,117 @@
// Copyright 2017 Google Inc. 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 main
import (
"fmt"
"github.com/googleapis/gnostic/jsonschema"
)
/// Type Modeling
// models types that we encounter during model-building that have no named schema
type TypeRequest struct {
Name string // name of type to be created
PropertyName string // name of a property that refers to this type
Schema *jsonschema.Schema // schema for type
OneOfWrapper bool // true if the type wraps "oneOfs"
}
func NewTypeRequest(name string, propertyName string, schema *jsonschema.Schema) *TypeRequest {
return &TypeRequest{Name: name, PropertyName: propertyName, Schema: schema}
}
// models type properties, eg. fields
type TypeProperty struct {
Name string // name of property
Type string // type for property (scalar or message type)
StringEnumValues []string // possible values if this is an enumerated string type
MapType string // if this property is for a map, the name of the mapped type
Repeated bool // true if this property is repeated (an array)
Pattern string // if the property is a pattern property, names must match this pattern.
Implicit bool // true if this property is implied by a pattern or "additional properties" property
Description string // if present, the "description" field in the schema
}
func (typeProperty *TypeProperty) description() string {
result := ""
if typeProperty.Description != "" {
result += fmt.Sprintf("\t// %+s\n", typeProperty.Description)
}
if typeProperty.Repeated {
result += fmt.Sprintf("\t%s %s repeated %s\n", typeProperty.Name, typeProperty.Type, typeProperty.Pattern)
} else {
result += fmt.Sprintf("\t%s %s %s \n", typeProperty.Name, typeProperty.Type, typeProperty.Pattern)
}
return result
}
func NewTypeProperty() *TypeProperty {
return &TypeProperty{}
}
func NewTypePropertyWithNameAndType(name string, typeName string) *TypeProperty {
return &TypeProperty{Name: name, Type: typeName}
}
func NewTypePropertyWithNameTypeAndPattern(name string, typeName string, pattern string) *TypeProperty {
return &TypeProperty{Name: name, Type: typeName, Pattern: pattern}
}
// models types
type TypeModel struct {
Name string // type name
Properties []*TypeProperty // slice of properties
Required []string // required property names
OneOfWrapper bool // true if this type wraps "oneof" properties
Open bool // open types can have keys outside the specified set
OpenPatterns []string // patterns for properties that we allow
IsStringArray bool // ugly override
IsItemArray bool // ugly override
IsBlob bool // ugly override
IsPair bool // type is a name-value pair used to support ordered maps
PairValueType string // type for pair values (valid if IsPair == true)
Description string // if present, the "description" field in the schema
}
func (typeModel *TypeModel) addProperty(property *TypeProperty) {
if typeModel.Properties == nil {
typeModel.Properties = make([]*TypeProperty, 0)
}
typeModel.Properties = append(typeModel.Properties, property)
}
func (typeModel *TypeModel) description() string {
result := ""
if typeModel.Description != "" {
result += fmt.Sprintf("// %+s\n", typeModel.Description)
}
var wrapperinfo string
if typeModel.OneOfWrapper {
wrapperinfo = " oneof wrapper"
}
result += fmt.Sprintf("%+s%s\n", typeModel.Name, wrapperinfo)
for _, property := range typeModel.Properties {
result += property.description()
}
return result
}
func NewTypeModel() *TypeModel {
typeModel := &TypeModel{}
typeModel.Properties = make([]*TypeProperty, 0)
return typeModel
}