github.com/argoproj/argo-events@v1.9.1/hack/gen-openapi-spec/main.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"log"
     6  	"os"
     7  	"strings"
     8  
     9  	"k8s.io/kube-openapi/pkg/common"
    10  	"k8s.io/kube-openapi/pkg/validation/spec"
    11  
    12  	cv1 "github.com/argoproj/argo-events/pkg/apis/common"
    13  	ebv1 "github.com/argoproj/argo-events/pkg/apis/eventbus/v1alpha1"
    14  	esv1 "github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1"
    15  	sv1 "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
    16  )
    17  
    18  type (
    19  	obj = map[string]interface{}
    20  )
    21  
    22  // Generate OpenAPI spec definitions for Workflow Resource
    23  func main() {
    24  	if len(os.Args) <= 3 {
    25  		log.Fatal("Supply a version")
    26  	}
    27  	log.Println(os.Args)
    28  	version := os.Args[1]
    29  	kubeSwaggerPath := os.Args[2]
    30  	output := os.Args[3]
    31  	if version != "latest" && !strings.HasPrefix(version, "v") {
    32  		version = "v" + version
    33  	}
    34  	referenceCallback := func(name string) spec.Ref {
    35  		return spec.MustCreateRef("#/definitions/" + common.EscapeJsonPointer(swaggify(name)))
    36  	}
    37  	defs := spec.Definitions{}
    38  	dependencies := []string{}
    39  	for defName, val := range cv1.GetOpenAPIDefinitions(referenceCallback) {
    40  		defs[swaggify(defName)] = val.Schema
    41  		dependencies = append(dependencies, val.Dependencies...)
    42  	}
    43  	for defName, val := range ebv1.GetOpenAPIDefinitions(referenceCallback) {
    44  		defs[swaggify(defName)] = val.Schema
    45  		dependencies = append(dependencies, val.Dependencies...)
    46  	}
    47  	for defName, val := range esv1.GetOpenAPIDefinitions(referenceCallback) {
    48  		defs[swaggify(defName)] = val.Schema
    49  		dependencies = append(dependencies, val.Dependencies...)
    50  	}
    51  	for defName, val := range sv1.GetOpenAPIDefinitions(referenceCallback) {
    52  		defs[swaggify(defName)] = val.Schema
    53  		dependencies = append(dependencies, val.Dependencies...)
    54  	}
    55  
    56  	k8sDefinitions := getKubernetesSwagger(kubeSwaggerPath)
    57  	for _, dep := range dependencies {
    58  		if !strings.Contains(dep, "k8s.io") {
    59  			continue
    60  		}
    61  		d := swaggify(dep)
    62  		if kd, ok := k8sDefinitions[d]; ok {
    63  			defs[d] = kd
    64  		}
    65  	}
    66  	for d, s := range k8sDefinitions {
    67  		defs[d] = s
    68  	}
    69  
    70  	swagger := &spec.Swagger{
    71  		SwaggerProps: spec.SwaggerProps{
    72  			Swagger:     "2.0",
    73  			Definitions: defs,
    74  			Paths:       &spec.Paths{Paths: map[string]spec.PathItem{}},
    75  			Info: &spec.Info{
    76  				InfoProps: spec.InfoProps{
    77  					Title:   "Argo Events",
    78  					Version: version,
    79  				},
    80  			},
    81  		},
    82  	}
    83  
    84  	jsonBytes, err := json.MarshalIndent(swagger, "", "  ")
    85  	if err != nil {
    86  		log.Fatal(err.Error())
    87  	}
    88  	err = os.WriteFile(output, jsonBytes, 0644)
    89  	if err != nil {
    90  		panic(err)
    91  	}
    92  	f, err := os.Open(output)
    93  	if err != nil {
    94  		panic(err)
    95  	}
    96  	// filter out "default" fields from swagger definitions properties because they are being set to empty strings and it makes the swagger validation fail.
    97  	swaggerObj := obj{}
    98  	err = json.NewDecoder(f).Decode(&swaggerObj)
    99  	if err != nil {
   100  		panic(err)
   101  	}
   102  	definitions := swaggerObj["definitions"].(obj)
   103  
   104  	for _, d := range definitions {
   105  		props, ok := d.(obj)["properties"].(obj)
   106  		if ok {
   107  			for _, prop := range props {
   108  				prop := prop.(obj)
   109  				delete(prop, "default")
   110  				items, ok := prop["items"].(obj)
   111  				if ok {
   112  					delete(items, "default")
   113  				}
   114  				additionalProperties, ok := prop["additionalProperties"].(obj)
   115  				if ok {
   116  					delete(additionalProperties, "default")
   117  				}
   118  			}
   119  		}
   120  		props, ok = d.(obj)["additionalProperties"].(obj)
   121  		if ok {
   122  			delete(props, "default")
   123  		}
   124  	}
   125  
   126  	f, err = os.Create(output)
   127  	if err != nil {
   128  		panic(err)
   129  	}
   130  	e := json.NewEncoder(f)
   131  	e.SetIndent("", "  ")
   132  	err = e.Encode(swaggerObj)
   133  	if err != nil {
   134  		panic(err)
   135  	}
   136  	err = f.Close()
   137  	if err != nil {
   138  		panic(err)
   139  	}
   140  }
   141  
   142  // swaggify converts the github package
   143  // e.g.:
   144  // github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1.Sensor
   145  // to:
   146  // io.argoproj.v1alpha1.Sensor
   147  func swaggify(name string) string {
   148  	name = strings.ReplaceAll(name, "github.com/argoproj/argo-events/pkg/apis", "argoproj.io")
   149  	parts := strings.Split(name, "/")
   150  	hostParts := strings.Split(parts[0], ".")
   151  	// reverses something like k8s.io to io.k8s
   152  	for i, j := 0, len(hostParts)-1; i < j; i, j = i+1, j-1 {
   153  		hostParts[i], hostParts[j] = hostParts[j], hostParts[i]
   154  	}
   155  	parts[0] = strings.Join(hostParts, ".")
   156  	return strings.Join(parts, ".")
   157  }
   158  
   159  func getKubernetesSwagger(kubeSwaggerPath string) spec.Definitions {
   160  	data, err := os.ReadFile(kubeSwaggerPath)
   161  	if err != nil {
   162  		panic(err)
   163  	}
   164  	swagger := &spec.Swagger{}
   165  	err = json.Unmarshal(data, swagger)
   166  	if err != nil {
   167  		panic(err)
   168  	}
   169  	return swagger.Definitions
   170  }