| // 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 |
| } |