mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-22 13:00:19 +00:00
304 lines
8.4 KiB
Go
304 lines
8.4 KiB
Go
|
// 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"
|
||
|
"strings"
|
||
|
|
||
|
openapi3 "github.com/googleapis/gnostic/OpenAPIv3"
|
||
|
discovery "github.com/googleapis/gnostic/discovery"
|
||
|
)
|
||
|
|
||
|
func pathForMethod(path string) string {
|
||
|
return "/" + strings.Replace(path, "{+", "{", -1)
|
||
|
}
|
||
|
|
||
|
func addOpenAPI3SchemaForSchema(d *openapi3.Document, name string, schema *discovery.Schema) {
|
||
|
d.Components.Schemas.AdditionalProperties = append(d.Components.Schemas.AdditionalProperties,
|
||
|
&openapi3.NamedSchemaOrReference{
|
||
|
Name: name,
|
||
|
Value: buildOpenAPI3SchemaOrReferenceForSchema(schema),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func buildOpenAPI3SchemaOrReferenceForSchema(schema *discovery.Schema) *openapi3.SchemaOrReference {
|
||
|
if ref := schema.XRef; ref != "" {
|
||
|
return &openapi3.SchemaOrReference{
|
||
|
Oneof: &openapi3.SchemaOrReference_Reference{
|
||
|
Reference: &openapi3.Reference{
|
||
|
XRef: "#/definitions/" + ref,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
s := &openapi3.Schema{}
|
||
|
|
||
|
if description := schema.Description; description != "" {
|
||
|
s.Description = description
|
||
|
}
|
||
|
if typeName := schema.Type; typeName != "" {
|
||
|
s.Type = typeName
|
||
|
}
|
||
|
if len(schema.Enum) > 0 {
|
||
|
for _, e := range schema.Enum {
|
||
|
s.Enum = append(s.Enum, &openapi3.Any{Yaml: e})
|
||
|
}
|
||
|
}
|
||
|
if schema.Items != nil {
|
||
|
s.Items = &openapi3.ItemsItem{
|
||
|
SchemaOrReference: []*openapi3.SchemaOrReference{buildOpenAPI3SchemaOrReferenceForSchema(schema.Items)},
|
||
|
}
|
||
|
}
|
||
|
if (schema.Properties != nil) && (len(schema.Properties.AdditionalProperties) > 0) {
|
||
|
s.Properties = &openapi3.Properties{}
|
||
|
for _, pair := range schema.Properties.AdditionalProperties {
|
||
|
s.Properties.AdditionalProperties = append(s.Properties.AdditionalProperties,
|
||
|
&openapi3.NamedSchemaOrReference{
|
||
|
Name: pair.Name,
|
||
|
Value: buildOpenAPI3SchemaOrReferenceForSchema(pair.Value),
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
return &openapi3.SchemaOrReference{
|
||
|
Oneof: &openapi3.SchemaOrReference_Schema{
|
||
|
Schema: s,
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func buildOpenAPI3ParameterForParameter(name string, p *discovery.Parameter) *openapi3.Parameter {
|
||
|
typeName := p.Type
|
||
|
format := p.Format
|
||
|
location := p.Location
|
||
|
switch location {
|
||
|
case "query", "path":
|
||
|
return &openapi3.Parameter{
|
||
|
Name: name,
|
||
|
In: location,
|
||
|
Description: p.Description,
|
||
|
Required: p.Required,
|
||
|
Schema: &openapi3.SchemaOrReference{
|
||
|
Oneof: &openapi3.SchemaOrReference_Schema{
|
||
|
Schema: &openapi3.Schema{
|
||
|
Type: typeName,
|
||
|
Format: format,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
default:
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func buildOpenAPI3RequestBodyForRequest(request *discovery.Request) *openapi3.RequestBody {
|
||
|
ref := request.XRef
|
||
|
if ref == "" {
|
||
|
log.Printf("WARNING: Unhandled request schema %+v", request)
|
||
|
}
|
||
|
return &openapi3.RequestBody{
|
||
|
Content: &openapi3.MediaTypes{
|
||
|
AdditionalProperties: []*openapi3.NamedMediaType{
|
||
|
&openapi3.NamedMediaType{
|
||
|
Name: "application/json",
|
||
|
Value: &openapi3.MediaType{
|
||
|
Schema: &openapi3.SchemaOrReference{
|
||
|
Oneof: &openapi3.SchemaOrReference_Reference{
|
||
|
Reference: &openapi3.Reference{
|
||
|
XRef: "#/definitions/" + ref,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func buildOpenAPI3ResponseForResponse(response *discovery.Response, hasDataWrapper bool) *openapi3.Response {
|
||
|
if response == nil {
|
||
|
return &openapi3.Response{
|
||
|
Description: "Successful operation",
|
||
|
}
|
||
|
} else {
|
||
|
ref := response.XRef
|
||
|
if ref == "" {
|
||
|
log.Printf("WARNING: Unhandled response %+v", response)
|
||
|
}
|
||
|
return &openapi3.Response{
|
||
|
Description: "Successful operation",
|
||
|
Content: &openapi3.MediaTypes{
|
||
|
AdditionalProperties: []*openapi3.NamedMediaType{
|
||
|
&openapi3.NamedMediaType{
|
||
|
Name: "application/json",
|
||
|
Value: &openapi3.MediaType{
|
||
|
Schema: &openapi3.SchemaOrReference{
|
||
|
Oneof: &openapi3.SchemaOrReference_Reference{
|
||
|
Reference: &openapi3.Reference{
|
||
|
XRef: "#/definitions/" + ref,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func buildOpenAPI3OperationForMethod(method *discovery.Method, hasDataWrapper bool) *openapi3.Operation {
|
||
|
if method == nil {
|
||
|
return nil
|
||
|
}
|
||
|
parameters := make([]*openapi3.ParameterOrReference, 0)
|
||
|
if method.Parameters != nil {
|
||
|
for _, pair := range method.Parameters.AdditionalProperties {
|
||
|
parameters = append(parameters, &openapi3.ParameterOrReference{
|
||
|
Oneof: &openapi3.ParameterOrReference_Parameter{
|
||
|
Parameter: buildOpenAPI3ParameterForParameter(pair.Name, pair.Value),
|
||
|
},
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
responses := &openapi3.Responses{
|
||
|
ResponseOrReference: []*openapi3.NamedResponseOrReference{
|
||
|
&openapi3.NamedResponseOrReference{
|
||
|
Name: "default",
|
||
|
Value: &openapi3.ResponseOrReference{
|
||
|
Oneof: &openapi3.ResponseOrReference_Response{
|
||
|
Response: buildOpenAPI3ResponseForResponse(method.Response, hasDataWrapper),
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
var requestBodyOrReference *openapi3.RequestBodyOrReference
|
||
|
if method.Request != nil {
|
||
|
requestBody := buildOpenAPI3RequestBodyForRequest(method.Request)
|
||
|
requestBodyOrReference = &openapi3.RequestBodyOrReference{
|
||
|
Oneof: &openapi3.RequestBodyOrReference_RequestBody{
|
||
|
RequestBody: requestBody,
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
return &openapi3.Operation{
|
||
|
Description: method.Description,
|
||
|
OperationId: method.Id,
|
||
|
Parameters: parameters,
|
||
|
Responses: responses,
|
||
|
RequestBody: requestBodyOrReference,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func getOpenAPI3PathItemForPath(d *openapi3.Document, path string) *openapi3.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 := &openapi3.PathItem{}
|
||
|
d.Paths.Path = append(d.Paths.Path,
|
||
|
&openapi3.NamedPathItem{
|
||
|
Name: path,
|
||
|
Value: pathItem,
|
||
|
},
|
||
|
)
|
||
|
return pathItem
|
||
|
}
|
||
|
|
||
|
func addOpenAPI3PathsForMethod(d *openapi3.Document, name string, method *discovery.Method, hasDataWrapper bool) {
|
||
|
operation := buildOpenAPI3OperationForMethod(method, hasDataWrapper)
|
||
|
pathItem := getOpenAPI3PathItemForPath(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 addOpenAPI3PathsForResource(d *openapi3.Document, resource *discovery.Resource, hasDataWrapper bool) {
|
||
|
if resource.Methods != nil {
|
||
|
for _, pair := range resource.Methods.AdditionalProperties {
|
||
|
addOpenAPI3PathsForMethod(d, pair.Name, pair.Value, hasDataWrapper)
|
||
|
}
|
||
|
}
|
||
|
if resource.Resources != nil {
|
||
|
for _, pair := range resource.Resources.AdditionalProperties {
|
||
|
addOpenAPI3PathsForResource(d, pair.Value, hasDataWrapper)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// OpenAPIv3 returns an OpenAPI v3 representation of a Discovery document
|
||
|
func OpenAPIv3(api *discovery.Document) (*openapi3.Document, error) {
|
||
|
d := &openapi3.Document{}
|
||
|
d.Openapi = "3.0"
|
||
|
d.Info = &openapi3.Info{
|
||
|
Title: api.Title,
|
||
|
Version: api.Version,
|
||
|
Description: api.Description,
|
||
|
}
|
||
|
d.Servers = make([]*openapi3.Server, 0)
|
||
|
|
||
|
url, _ := url.Parse(api.RootUrl)
|
||
|
host := url.Host
|
||
|
basePath := api.BasePath
|
||
|
if basePath == "" {
|
||
|
basePath = "/"
|
||
|
}
|
||
|
d.Servers = append(d.Servers, &openapi3.Server{Url: "https://" + host + basePath})
|
||
|
|
||
|
hasDataWrapper := false
|
||
|
for _, feature := range api.Features {
|
||
|
if feature == "dataWrapper" {
|
||
|
hasDataWrapper = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
d.Components = &openapi3.Components{}
|
||
|
d.Components.Schemas = &openapi3.SchemasOrReferences{}
|
||
|
for _, pair := range api.Schemas.AdditionalProperties {
|
||
|
addOpenAPI3SchemaForSchema(d, pair.Name, pair.Value)
|
||
|
}
|
||
|
|
||
|
d.Paths = &openapi3.Paths{}
|
||
|
if api.Methods != nil {
|
||
|
for _, pair := range api.Methods.AdditionalProperties {
|
||
|
addOpenAPI3PathsForMethod(d, pair.Name, pair.Value, hasDataWrapper)
|
||
|
}
|
||
|
}
|
||
|
for _, pair := range api.Resources.AdditionalProperties {
|
||
|
addOpenAPI3PathsForResource(d, pair.Value, hasDataWrapper)
|
||
|
}
|
||
|
|
||
|
return d, nil
|
||
|
}
|