blob: 042bde54d2bcf3a11e08455c8112e5229d2c4f1a [file] [log] [blame]
// 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 (
"log"
"net/url"
discovery "github.com/googleapis/gnostic/discovery"
openapi2 "github.com/googleapis/gnostic/OpenAPIv2"
)
func addOpenAPI2SchemaForSchema(d *openapi2.Document, name string, schema *discovery.Schema) {
//log.Printf("SCHEMA %s\n", name)
d.Definitions.AdditionalProperties = append(d.Definitions.AdditionalProperties,
&openapi2.NamedSchema{
Name: name,
Value: buildOpenAPI2SchemaForSchema(schema),
})
}
func buildOpenAPI2SchemaForSchema(schema *discovery.Schema) *openapi2.Schema {
s := &openapi2.Schema{}
if description := schema.Description; description != "" {
s.Description = description
}
if typeName := schema.Type; typeName != "" {
s.Type = &openapi2.TypeItem{[]string{typeName}}
}
if ref := schema.XRef; ref != "" {
s.XRef = "#/definitions/" + ref
}
if len(schema.Enum) > 0 {
for _, e := range schema.Enum {
s.Enum = append(s.Enum, &openapi2.Any{Yaml: e})
}
}
if schema.Items != nil {
s2 := buildOpenAPI2SchemaForSchema(schema.Items)
s.Items = &openapi2.ItemsItem{}
s.Items.Schema = append(s.Items.Schema, s2)
}
if schema.Properties != nil {
if len(schema.Properties.AdditionalProperties) > 0 {
s.Properties = &openapi2.Properties{}
for _, pair := range schema.Properties.AdditionalProperties {
s.Properties.AdditionalProperties = append(s.Properties.AdditionalProperties,
&openapi2.NamedSchema{
Name: pair.Name,
Value: buildOpenAPI2SchemaForSchema(pair.Value),
},
)
}
}
}
// assume that all schemas are closed
s.AdditionalProperties = &openapi2.AdditionalPropertiesItem{Oneof: &openapi2.AdditionalPropertiesItem_Boolean{Boolean: false}}
return s
}
func buildOpenAPI2ParameterForParameter(name string, p *discovery.Parameter) *openapi2.Parameter {
//log.Printf("- PARAMETER %+v\n", p.Name)
typeName := p.Type
format := p.Format
location := p.Location
switch location {
case "query":
return &openapi2.Parameter{
Oneof: &openapi2.Parameter_NonBodyParameter{
NonBodyParameter: &openapi2.NonBodyParameter{
Oneof: &openapi2.NonBodyParameter_QueryParameterSubSchema{
QueryParameterSubSchema: &openapi2.QueryParameterSubSchema{
Name: name,
In: "query",
Description: p.Description,
Required: p.Required,
Type: typeName,
Format: format,
},
},
},
},
}
case "path":
return &openapi2.Parameter{
Oneof: &openapi2.Parameter_NonBodyParameter{
NonBodyParameter: &openapi2.NonBodyParameter{
Oneof: &openapi2.NonBodyParameter_PathParameterSubSchema{
PathParameterSubSchema: &openapi2.PathParameterSubSchema{
Name: name,
In: "path",
Description: p.Description,
Required: p.Required,
Type: typeName,
Format: format,
},
},
},
},
}
default:
return nil
}
}
func buildOpenAPI2ParameterForRequest(p *discovery.Request) *openapi2.Parameter {
return &openapi2.Parameter{
Oneof: &openapi2.Parameter_BodyParameter{
BodyParameter: &openapi2.BodyParameter{
Name: "resource",
In: "body",
Description: "",
Schema: &openapi2.Schema{XRef: "#/definitions/" + p.XRef},
},
},
}
}
func buildOpenAPI2ResponseForResponse(response *discovery.Response) *openapi2.Response {
//log.Printf("- RESPONSE %+v\n", schema)
if response == nil {
return &openapi2.Response{
Description: "Successful operation",
}
}
ref := response.XRef
if ref == "" {
log.Printf("WARNING: Unhandled response %+v", response)
}
return &openapi2.Response{
Description: "Successful operation",
Schema: &openapi2.SchemaItem{
Oneof: &openapi2.SchemaItem_Schema{
Schema: &openapi2.Schema{
XRef: "#/definitions/" + ref,
},
},
},
}
}
func buildOpenAPI2OperationForMethod(method *discovery.Method) *openapi2.Operation {
//log.Printf("METHOD %s %s %s %s\n", method.Name, method.path(), method.HTTPMethod, method.ID)
//log.Printf("MAP %+v\n", method.JSONMap)
parameters := make([]*openapi2.ParametersItem, 0)
if method.Parameters != nil {
for _, pair := range method.Parameters.AdditionalProperties {
parameters = append(parameters, &openapi2.ParametersItem{
Oneof: &openapi2.ParametersItem_Parameter{
Parameter: buildOpenAPI2ParameterForParameter(pair.Name, pair.Value),
},
})
}
}
responses := &openapi2.Responses{
ResponseCode: []*openapi2.NamedResponseValue{
&openapi2.NamedResponseValue{
Name: "default",
Value: &openapi2.ResponseValue{
Oneof: &openapi2.ResponseValue_Response{
Response: buildOpenAPI2ResponseForResponse(method.Response),
},
},
},
},
}
if method.Request != nil {
parameter := buildOpenAPI2ParameterForRequest(method.Request)
parameters = append(parameters, &openapi2.ParametersItem{
Oneof: &openapi2.ParametersItem_Parameter{
Parameter: parameter,
},
})
}
return &openapi2.Operation{
Description: method.Description,
OperationId: method.Id,
Parameters: parameters,
Responses: responses,
}
}
func getOpenAPI2PathItemForPath(d *openapi2.Document, path string) *openapi2.PathItem {
// First, try to find a path item with the specified path. If it exists, return it.
for _, item := range d.Paths.Path {
if item.Name == path {
return item.Value
}
}
// Otherwise, create and return a new path item.
pathItem := &openapi2.PathItem{}
d.Paths.Path = append(d.Paths.Path,
&openapi2.NamedPathItem{
Name: path,
Value: pathItem,
},
)
return pathItem
}
func addOpenAPI2PathsForMethod(d *openapi2.Document, name string, method *discovery.Method) {
operation := buildOpenAPI2OperationForMethod(method)
pathItem := getOpenAPI2PathItemForPath(d, pathForMethod(method.Path))
switch method.HttpMethod {
case "GET":
pathItem.Get = operation
case "POST":
pathItem.Post = operation
case "PUT":
pathItem.Put = operation
case "DELETE":
pathItem.Delete = operation
case "PATCH":
pathItem.Patch = operation
default:
log.Printf("WARNING: Unknown HTTP method %s", method.HttpMethod)
}
}
func addOpenAPI2PathsForResource(d *openapi2.Document, name string, resource *discovery.Resource) {
//log.Printf("RESOURCE %s (%s)\n", resource.Name, resource.FullName)
if resource.Methods != nil {
for _, pair := range resource.Methods.AdditionalProperties {
addOpenAPI2PathsForMethod(d, pair.Name, pair.Value)
}
}
if resource.Resources != nil {
for _, pair := range resource.Resources.AdditionalProperties {
addOpenAPI2PathsForResource(d, pair.Name, pair.Value)
}
}
}
func removeTrailingSlash(path string) string {
if len(path) > 1 && path[len(path)-1] == '/' {
return path[0: len(path)-1]
}
return path
}
// OpenAPIv2 returns an OpenAPI v2 representation of this Discovery document
func OpenAPIv2(api *discovery.Document) (*openapi2.Document, error) {
d := &openapi2.Document{}
d.Swagger = "2.0"
d.Info = &openapi2.Info{
Title: api.Title,
Version: api.Version,
Description: api.Description,
}
url, _ := url.Parse(api.RootUrl)
d.Host = url.Host
d.BasePath = removeTrailingSlash(api.BasePath)
d.Schemes = []string{url.Scheme}
d.Consumes = []string{"application/json"}
d.Produces = []string{"application/json"}
d.Paths = &openapi2.Paths{}
d.Definitions = &openapi2.Definitions{}
if api.Schemas != nil {
for _, pair := range api.Schemas.AdditionalProperties {
addOpenAPI2SchemaForSchema(d, pair.Name, pair.Value)
}
}
if api.Methods != nil {
for _, pair := range api.Methods.AdditionalProperties {
addOpenAPI2PathsForMethod(d, pair.Name, pair.Value)
}
}
if api.Resources != nil {
for _, pair := range api.Resources.AdditionalProperties {
addOpenAPI2PathsForResource(d, pair.Name, pair.Value)
}
}
return d, nil
}