From 5bc2b5d91b878dc00372ed99a139a18cfddcb0f2 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Tue, 24 Jan 2023 16:22:46 +0100 Subject: [PATCH] make cli more deck like, add options, rebase on latest lib version --- .gitignore | 18 +++++ cmd/openapi2kong.go | 165 ++++++++++++++++++++++++++++++++++++-------- cmd/root.go | 17 ++--- go.mod | 9 ++- go.sum | 15 ++-- 5 files changed, 178 insertions(+), 46 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8756c15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# the binary +kced diff --git a/cmd/openapi2kong.go b/cmd/openapi2kong.go index e162f6c..d62aa89 100644 --- a/cmd/openapi2kong.go +++ b/cmd/openapi2kong.go @@ -4,50 +4,161 @@ Copyright © 2023 NAME HERE package cmd import ( + "encoding/json" "fmt" + "log" "os" - "github.com/Kong/fw/convert" + "github.com/Kong/fw/convertoas3" "github.com/spf13/cobra" "gopkg.in/yaml.v3" ) -var openapi2kongCmd = &cobra.Command{ - Use: "openapi2kong", - Short: "Convert OpenAPI files to Kong's decK format", - Long: `Convert OpenAPI files to Kong's decK format`, - Run: func(cmd *cobra.Command, args []string) { - inputFilename, _ := cmd.Flags().GetString("input") +const ( + defaultJsonIndent = " " +) + +// mustReadFile reads file contents. Will panic if reading fails. +//  Reads from stdin if filename == "-" +func mustReadFile(filename string) *[]byte { + if filename == "-" { + filename = "/dev/stdin" + } + + body, err := os.ReadFile(filename) + if err != nil { + log.Fatalf("unable to read file: %v", err) + } + return &body +} + +// mustSerialize will serialize the result as a JSON/YAML. Will panic +// if serializing fails. +func mustSerialize(content map[string]interface{}, asYaml bool) *[]byte { + var ( + str []byte + err error + ) + + if asYaml { + str, err = yaml.Marshal(content) + if err != nil { + log.Fatal("failed to yaml-serialize the resulting file; %w", err) + } + } else { + str, err = json.MarshalIndent(content, "", defaultJsonIndent) + if err != nil { + log.Fatal("failed to json-serialize the resulting file; %w", err) + } + } + + return &str +} + +// mustWriteFile writes the output to a file. Will panic if writing fails. +// Writes to stdout if filename == "-" +func mustWriteFile(filename string, content *[]byte) { + + var f *os.File + var err error - input, err := os.ReadFile(inputFilename) + if filename != "-" { + // write to file + f, err = os.Create(filename) if err != nil { - fmt.Printf("%v", err) - return + log.Fatalf("failed to create output file '%s'", filename) } + defer f.Close() + } else { + // writing to stdout + f = os.Stdout + } + _, err = f.Write(*content) + if err != nil { + log.Fatalf(fmt.Sprintf("failed to write to output file '%s'; %%w", filename), err) + } +} + +// Executes the CLI command "openapi2kong" +func execute(cmd *cobra.Command, args []string) { + inputFilename, err := cmd.Flags().GetString("state") + if err != nil { + log.Fatalf(fmt.Sprintf("failed getting cli argument 'state'; %%w"), err) + } + + outputFilename, err := cmd.Flags().GetString("output-file") + if err != nil { + log.Fatalf(fmt.Sprintf("failed getting cli argument 'output-file'; %%w"), err) + } - deckContent, err := convert.ConvertOas3(&input, convert.O2kOptions{ - Tags: &[]string{"OAS3_import"}, - }) + docName, err := cmd.Flags().GetString("uuid-base") + if err != nil { + log.Fatalf(fmt.Sprintf("failed getting cli argument 'uuid-base'; %%w"), err) + } + var entityTags *[]string + { + tags, err := cmd.Flags().GetStringSlice("select-tag") if err != nil { - fmt.Printf("%v", err) + log.Fatalf(fmt.Sprintf("failed getting cli argument 'select-tag'; %%w"), err) + } + entityTags = &tags + if len(*entityTags) == 0 { + entityTags = nil + } + } + + var asYaml bool + { + outputFormat, err := cmd.Flags().GetString("format") + if err != nil { + log.Fatalf(fmt.Sprintf("failed getting cli argument 'format'; %%w"), err) + } + if outputFormat == "yaml" { + asYaml = true + } else if outputFormat == "json" { + asYaml = false } else { - outputFilename, _ := cmd.Flags().GetString("output") - YAMLOut, _ := yaml.Marshal(deckContent) - err = os.WriteFile(outputFilename, YAMLOut, 0666) - if err != nil { - fmt.Printf("%v", err) - return - } - fmt.Printf("Wrote %s\n", outputFilename) + log.Fatalf("expected '--format' to be either 'yaml' or 'json', got: '%s'", outputFormat) } - }, + } + + options := convertoas3.O2kOptions{ + Tags: entityTags, + DocName: docName, + } + + // do the work: read/convert/write + input := mustReadFile(inputFilename) + result := convertoas3.MustConvert(input, options) + output := mustSerialize(result, asYaml) + mustWriteFile(outputFilename, output) +} + +// +// +// Define the CLI data for the openapi2kong command +// +// + +var openapi2kongCmd = &cobra.Command{ + Use: "openapi2kong", + Short: "Convert OpenAPI files to Kong's decK format", + Long: `Convert OpenAPI files to Kong's decK format. + +The example file has extensive annotations explaining the conversion +process, as well as all supported custom annotations (x-kong-... directives). +See: https://github.com/Kong/fw/blob/main/learnservice_oas.yaml`, + Run: execute, } func init() { rootCmd.AddCommand(openapi2kongCmd) - openapi2kongCmd.Flags().StringP("input", "i", "", "The input file to process") - openapi2kongCmd.Flags().StringP("output", "o", "", "The output file to write") - openapi2kongCmd.MarkFlagRequired("input") - openapi2kongCmd.MarkFlagRequired("output") + openapi2kongCmd.Flags().StringP("state", "s", "-", "state file (OAS3, json/yaml) to process. Use - to read from stdin") + openapi2kongCmd.Flags().StringP("output-file", "o", "-", "output file to write. Use - to write to stdout") + openapi2kongCmd.Flags().StringP("format", "", "yaml", "output format: json or yaml") + openapi2kongCmd.Flags().StringP("uuid-base", "", "", `the unique base-string for uuid-v5 generation of enity id's (if omitted +will use the root-level "x-kong-name" directive, or fall back to 'info.title')`) + openapi2kongCmd.Flags().StringSlice("select-tag", nil, `select tags to apply to all entities (if omitted will use the "x-kong-tags" +directive from the file)`) } diff --git a/cmd/root.go b/cmd/root.go index 44a9a22..45b8e4f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,6 +1,5 @@ /* Copyright © 2023 NAME HERE - */ package cmd @@ -10,18 +9,14 @@ import ( "github.com/spf13/cobra" ) - - // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "kced", - Short: "A brief description of your application", - Long: `A longer description that spans multiple lines and likely contains -examples and usage of using your application. For example: + Short: "A temporary CLI to test a new Openapi-2-Kong implementation", + Long: `A temporary CLI to test a new Openapi-2-Kong implementation. -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, +The ApiOps tooling will be enhanced, part of this effort is +this tool, which is a rewrite of the Inso Openapi-2-Kong npm library.`, // Uncomment the following line if your bare application // has an action associated with it: // Run: func(cmd *cobra.Command, args []string) { }, @@ -45,7 +40,5 @@ func init() { // Cobra also supports local flags, which will only run // when this action is called directly. - rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } - - diff --git a/go.mod b/go.mod index 78b515a..8d00369 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,12 @@ module github.com/Kong/kced go 1.19 require ( - github.com/Kong/fw v0.0.0-20230106131953-c395476580e9 // indirect + github.com/Kong/fw v0.0.0-20230124131059-6f9916c7700a + github.com/spf13/cobra v1.6.1 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( github.com/getkin/kin-openapi v0.108.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.19.5 // indirect @@ -14,8 +19,6 @@ require ( github.com/mozillazg/go-slugify v0.2.0 // indirect github.com/mozillazg/go-unidecode v0.2.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect - github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/pflag v1.0.5 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index cb40fdc..7812d70 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,10 @@ -github.com/Kong/fw v0.0.0-20230105135007-cb8aa17c2b03 h1:O25XhzIdCfXzpMlNeZHcz/TpzGRJ0DQeq00fXAeKoV4= -github.com/Kong/fw v0.0.0-20230105135007-cb8aa17c2b03/go.mod h1:7QKrUpxphkl4z2MM296aXZQIPTjedkKR/o+LLwoGZZ4= -github.com/Kong/fw v0.0.0-20230106131953-c395476580e9 h1:Vl5wSpuz307vcoFf8ScdaxgN2hsmoeUOgeV1vUZLRCI= -github.com/Kong/fw v0.0.0-20230106131953-c395476580e9/go.mod h1:7QKrUpxphkl4z2MM296aXZQIPTjedkKR/o+LLwoGZZ4= +github.com/Kong/fw v0.0.0-20230124113753-fceb5efadc42 h1:0KPTUAkmiAmblMBveUc0ip++gnLYCF1mx96HFB89Th8= +github.com/Kong/fw v0.0.0-20230124113753-fceb5efadc42/go.mod h1:7QKrUpxphkl4z2MM296aXZQIPTjedkKR/o+LLwoGZZ4= +github.com/Kong/fw v0.0.0-20230124131059-6f9916c7700a h1:kAbs/259Mmez/yOKC43offH5A7g5zPYJgcKvX2culO4= +github.com/Kong/fw v0.0.0-20230124131059-6f9916c7700a/go.mod h1:7QKrUpxphkl4z2MM296aXZQIPTjedkKR/o+LLwoGZZ4= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/getkin/kin-openapi v0.108.0 h1:EYf0GtsKa4hQNIlplGS+Au7NEfGQ1F7MoHD2kcVevPQ= github.com/getkin/kin-openapi v0.108.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= @@ -11,13 +12,16 @@ github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUe github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= @@ -28,6 +32,7 @@ github.com/mozillazg/go-slugify v0.2.0 h1:SIhqDlnJWZH8OdiTmQgeXR28AOnypmAXPeOTcG github.com/mozillazg/go-slugify v0.2.0/go.mod h1:z7dPH74PZf2ZPFkyxx+zjPD8CNzRJNa1CGacv0gg8Ns= github.com/mozillazg/go-unidecode v0.2.0 h1:vFGEzAH9KSwyWmXCOblazEWDh7fOkpmy/Z4ArmamSUc= github.com/mozillazg/go-unidecode v0.2.0/go.mod h1:zB48+/Z5toiRolOZy9ksLryJ976VIwmDmpQ2quyt1aA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= @@ -42,8 +47,10 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=