From 160115385e155e30a09634e9851b3c4335bbf5c5 Mon Sep 17 00:00:00 2001 From: Shubham Nazare Date: Sun, 24 Sep 2023 17:02:17 +0530 Subject: [PATCH 1/2] feat: generate plugins for client Signed-off-by: Shubham Nazare --- .../commands/client/generate/driver/driver.go | 9 ++- .../client/generate/driver/golang/plugins.go | 52 +++++++++++++++ .../client/generate/driver/rtk/rtk.go | 9 ++- .../generate/driver/typescript/plugins.go | 7 ++ .../commands/client/generate/generate.go | 65 +++++++++++++++---- sc-poc/cmd/spacectl/commands/login/login.go | 2 +- sc-poc/cmd/spacectl/commands/pkg/apply.go | 2 +- sc-poc/cmd/spacectl/commands/pkg/get.go | 2 +- sc-poc/utils/client/client.go | 21 +++--- sc-poc/utils/client/http.go | 25 +++++++ sc-poc/utils/string.go | 7 ++ 11 files changed, 175 insertions(+), 26 deletions(-) create mode 100644 sc-poc/cmd/spacectl/commands/client/generate/driver/golang/plugins.go create mode 100644 sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/plugins.go diff --git a/sc-poc/cmd/spacectl/commands/client/generate/driver/driver.go b/sc-poc/cmd/spacectl/commands/client/generate/driver/driver.go index 8dde6b041..e3652fb3b 100644 --- a/sc-poc/cmd/spacectl/commands/client/generate/driver/driver.go +++ b/sc-poc/cmd/spacectl/commands/client/generate/driver/driver.go @@ -1,6 +1,9 @@ package driver -import "github.com/getkin/kin-openapi/openapi3" +import ( + "github.com/getkin/kin-openapi/openapi3" + "github.com/spacecloud-io/space-cloud/pkg/apis/core/v1alpha1" +) type Driver interface { // GenerateTypes takes openapi spec and generates types in the respective languages @@ -8,4 +11,8 @@ type Driver interface { // GenerateAPI takes openapi spec and generates apis in the respective languages GenerateAPIs(*openapi3.T) (string, string, error) + + // GeneratePlugins takes list of plugins provided by SpaceCloud and generates plugins + // in the respective languages + GeneratePlugins([]v1alpha1.HTTPPlugin) (string, string, error) } diff --git a/sc-poc/cmd/spacectl/commands/client/generate/driver/golang/plugins.go b/sc-poc/cmd/spacectl/commands/client/generate/driver/golang/plugins.go new file mode 100644 index 000000000..454bc5b1a --- /dev/null +++ b/sc-poc/cmd/spacectl/commands/client/generate/driver/golang/plugins.go @@ -0,0 +1,52 @@ +package golang + +import ( + "fmt" + "strings" + + "github.com/spacecloud-io/space-cloud/pkg/apis/core/v1alpha1" + "golang.org/x/tools/imports" +) + +func (goDriver *Golang) GeneratePlugins(plugins []v1alpha1.HTTPPlugin) (string, string, error) { + fileName := "plugins.gen.go" + var b strings.Builder + + // package name and imports + pkgOut := fmt.Sprintf("package %s\n\n", goDriver.pkgName) + _, _ = b.WriteString(pkgOut) + + s := ` +type Plugins struct {} + +type PluginDetails struct { + name string + driver string +} + +func (plugin PluginDetails) Name() string { + return plugin.name +} + +func (plugin PluginDetails) Driver() string { + return plugin.driver +} + +` + + for _, plugin := range plugins { + driverName := getTypeName(plugin.Driver, false) + getTypeName(plugin.Name, false) + s += fmt.Sprintf("func (plugin Plugins) %s() PluginDetails {\n", driverName) + s += fmt.Sprintf("%s := PluginDetails{\n", driverName) + s += fmt.Sprintf("name: %q,\n", plugin.Name) + s += fmt.Sprintf("driver: %q,\n}\n", plugin.Driver) + s += fmt.Sprintf("return %s\n}\n\n", driverName) + } + _, _ = b.WriteString(s) + + outBytes, err := imports.Process(goDriver.pkgName+".go", []byte(b.String()), nil) + if err != nil { + return "", "", fmt.Errorf("error formatting Go code: %w", err) + } + return string(outBytes), fileName, nil +} diff --git a/sc-poc/cmd/spacectl/commands/client/generate/driver/rtk/rtk.go b/sc-poc/cmd/spacectl/commands/client/generate/driver/rtk/rtk.go index 53512f65b..a04880d5b 100644 --- a/sc-poc/cmd/spacectl/commands/client/generate/driver/rtk/rtk.go +++ b/sc-poc/cmd/spacectl/commands/client/generate/driver/rtk/rtk.go @@ -1,6 +1,9 @@ package rtk -import "github.com/spacecloud-io/space-cloud/cmd/spacectl/commands/client/generate/driver" +import ( + "github.com/spacecloud-io/space-cloud/cmd/spacectl/commands/client/generate/driver" + "github.com/spacecloud-io/space-cloud/pkg/apis/core/v1alpha1" +) type RTK struct { name string @@ -13,4 +16,8 @@ func MakeRTKDriver(name string) driver.Driver { return rtkDriver } +func (r *RTK) GeneratePlugins([]v1alpha1.HTTPPlugin) (string, string, error) { + return "", "", nil +} + var _ driver.Driver = (*RTK)(nil) diff --git a/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/plugins.go b/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/plugins.go new file mode 100644 index 000000000..e41b7764d --- /dev/null +++ b/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/plugins.go @@ -0,0 +1,7 @@ +package typescript + +import "github.com/spacecloud-io/space-cloud/pkg/apis/core/v1alpha1" + +func (t *Typescript) GeneratePlugins(plugins []v1alpha1.HTTPPlugin) (string, string, error) { + return "", "", nil +} diff --git a/sc-poc/cmd/spacectl/commands/client/generate/generate.go b/sc-poc/cmd/spacectl/commands/client/generate/generate.go index d687cc45e..30e883679 100644 --- a/sc-poc/cmd/spacectl/commands/client/generate/generate.go +++ b/sc-poc/cmd/spacectl/commands/client/generate/generate.go @@ -1,7 +1,9 @@ package generate import ( - "fmt" + "log" + "net/http" + "net/url" "os" "path/filepath" "strings" @@ -11,6 +13,8 @@ import ( "github.com/spacecloud-io/space-cloud/cmd/spacectl/commands/client/generate/driver/golang" "github.com/spacecloud-io/space-cloud/cmd/spacectl/commands/client/generate/driver/rtk" "github.com/spacecloud-io/space-cloud/cmd/spacectl/commands/client/generate/driver/typescript" + "github.com/spacecloud-io/space-cloud/utils" + clientutils "github.com/spacecloud-io/space-cloud/utils/client" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -29,6 +33,7 @@ func NewCommand() *cobra.Command { _ = viper.BindPFlag("name", cmd.Flags().Lookup("name")) _ = viper.BindPFlag("lang", cmd.Flags().Lookup("lang")) _ = viper.BindPFlag("package", cmd.Flags().Lookup("package")) + _ = viper.BindPFlag("sc-url", cmd.Flags().Lookup("sc-url")) }, RunE: func(cmd *cobra.Command, args []string) error { config := viper.GetString("config") @@ -36,11 +41,25 @@ func NewCommand() *cobra.Command { name := viper.GetString("name") lang := viper.GetString("lang") pkgName := viper.GetString("package") + scUrl := viper.GetString("sc-url") - doc, err := openapi3.NewLoader().LoadFromFile(config) - if err != nil { - fmt.Println("Unable to openapi doc:", err) - os.Exit(1) + var doc *openapi3.T + if scUrl != "" { + urlStr := utils.EnsureTrailingSlash(scUrl) + "v1/api/openapi.json" + configUrl, err := url.Parse(urlStr) + if err != nil { + log.Fatal("Error parsing URL:", err) + } + doc, err = openapi3.NewLoader().LoadFromURI(configUrl) + if err != nil { + log.Fatal("Unable to openapi doc:", err) + } + } else { + fileDoc, err := openapi3.NewLoader().LoadFromFile(config) + if err != nil { + log.Fatal("Unable to openapi doc:", err) + } + doc = fileDoc } var driver driver.Driver @@ -52,7 +71,7 @@ func NewCommand() *cobra.Command { _ = os.MkdirAll(output, 0777) _ = os.WriteFile(filepath.Join(output, "helpers.ts"), []byte(rtk.HelperTS), 0777) _ = os.WriteFile(filepath.Join(output, "index.ts"), []byte(rtk.IndexTS), 0777) - if _, err = os.Stat(filepath.Join(output, "http.config.ts")); os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(output, "http.config.ts")); os.IsNotExist(err) { _ = os.WriteFile(filepath.Join(output, "http.config.ts"), []byte(rtk.ConfigTS), 0777) } case "go": @@ -60,25 +79,46 @@ func NewCommand() *cobra.Command { case "typescript": driver = typescript.MakeTSDriver() default: - fmt.Printf("Invalid language name or language %s not supported.\n", lang) - return nil + log.Fatalf("Invalid language name or language %s not supported.\n", lang) } _ = os.MkdirAll(output, 0777) api, fileName, err := driver.GenerateAPIs(doc) if err != nil { - fmt.Printf("error generating api: %s\n", err) - os.Exit(1) + log.Fatalf("error generating api: %s\n", err) } _ = os.WriteFile(filepath.Join(output, fileName), []byte(api), 0777) types, fileName, err := driver.GenerateTypes(doc) if err != nil { - fmt.Printf("error generating types: %s\n", err) - os.Exit(1) + log.Fatalf("error generating types: %s\n", err) } _ = os.WriteFile(filepath.Join(output, fileName), []byte(types), 0777) + if scUrl != "" && lang != "rtk" { + creds, err := clientutils.GetCredentials() + if err != nil { + log.Fatal("Failed to get SpaceCloud credentials: ", err) + } + + httpClient := &http.Client{} + token, err := clientutils.Login(httpClient, creds) + if err != nil { + log.Fatal("Failed to authenticate with SpaceCloud: ", err) + } + + allPlugins, err := clientutils.ListAllPlugins(httpClient, creds.BaseUrl, token) + if err != nil { + log.Fatal("Failed to list all plugins: ", err) + } + + plugins, fileName, err := driver.GeneratePlugins(allPlugins) + if err != nil { + log.Fatalf("error generating plugins: %s\n", err) + } + _ = os.WriteFile(filepath.Join(output, fileName), []byte(plugins), 0777) + } + return nil }, } @@ -88,6 +128,7 @@ func NewCommand() *cobra.Command { cmd.Flags().StringP("name", "n", "my-api", "Name for the API.") cmd.Flags().StringP("lang", "l", "go", "Language in which to generate code. Supported languages are 'rtk', 'Go'") cmd.Flags().StringP("package", "p", "openapi", "The name of the package to generate.") + cmd.Flags().StringP("sc-url", "", "http://localhost:4122", "URL where SpaceCloud is running.") return cmd } diff --git a/sc-poc/cmd/spacectl/commands/login/login.go b/sc-poc/cmd/spacectl/commands/login/login.go index 012e35331..52d8c5a8a 100644 --- a/sc-poc/cmd/spacectl/commands/login/login.go +++ b/sc-poc/cmd/spacectl/commands/login/login.go @@ -60,7 +60,7 @@ func NewCommand() *cobra.Command { Password: base64.StdEncoding.EncodeToString([]byte(password)), BaseUrl: baseUrl, } - err := clientutils.Login(httpClient, creds) + _, err := clientutils.Login(httpClient, creds) if err != nil { log.Fatal("Failed to authenticate with SpaceCloud", err) } diff --git a/sc-poc/cmd/spacectl/commands/pkg/apply.go b/sc-poc/cmd/spacectl/commands/pkg/apply.go index 50563d07c..402cf365c 100644 --- a/sc-poc/cmd/spacectl/commands/pkg/apply.go +++ b/sc-poc/cmd/spacectl/commands/pkg/apply.go @@ -25,7 +25,7 @@ func newCommandApply() *cobra.Command { } // Login to SpaceCloud - if err := clientutils.Login(httpClient, creds); err != nil { + if _, err := clientutils.Login(httpClient, creds); err != nil { log.Fatal("Failed to authenticate with SpaceCloud: ", err) } diff --git a/sc-poc/cmd/spacectl/commands/pkg/get.go b/sc-poc/cmd/spacectl/commands/pkg/get.go index 82d158e7c..644476811 100644 --- a/sc-poc/cmd/spacectl/commands/pkg/get.go +++ b/sc-poc/cmd/spacectl/commands/pkg/get.go @@ -27,7 +27,7 @@ func newCommandGet() *cobra.Command { } // Login to SpaceCloud - if err := clientutils.Login(httpClient, creds); err != nil { + if _, err := clientutils.Login(httpClient, creds); err != nil { log.Fatal("Failed to authenticate with SpaceCloud: ", err) } diff --git a/sc-poc/utils/client/client.go b/sc-poc/utils/client/client.go index 0482f2ebc..9373c9780 100644 --- a/sc-poc/utils/client/client.go +++ b/sc-poc/utils/client/client.go @@ -31,7 +31,7 @@ func GetCredentials() (Credentials, error) { return creds, nil } -func Login(client *http.Client, creds Credentials) error { +func Login(client *http.Client, creds Credentials) (string, error) { b, _ := base64.StdEncoding.DecodeString(creds.Username) creds.Username = string(b) b, _ = base64.StdEncoding.DecodeString(creds.Password) @@ -42,24 +42,27 @@ func Login(client *http.Client, creds Credentials) error { req, err := http.NewRequest(http.MethodPost, path, bytes.NewBuffer(payload)) if err != nil { - return err + return "", err } req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { - return err + return "", err } defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + if resp.StatusCode != http.StatusOK { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - return fmt.Errorf(string(body)) + return "", fmt.Errorf(string(body)) } - return nil + var m map[string]string + json.Unmarshal(body, &m) + return m["token"], nil } func UpdateSpaceCloudCredsFile(creds Credentials) (string, error) { diff --git a/sc-poc/utils/client/http.go b/sc-poc/utils/client/http.go index f1b68f8c8..22ab763af 100644 --- a/sc-poc/utils/client/http.go +++ b/sc-poc/utils/client/http.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" + "github.com/spacecloud-io/space-cloud/pkg/apis/core/v1alpha1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -97,3 +98,27 @@ func DeleteResources(client *http.Client, gvr schema.GroupVersionResource, baseU return nil } + +func ListAllPlugins(client *http.Client, baseUrl string, token string) ([]v1alpha1.HTTPPlugin, error) { + var allPlugins []v1alpha1.HTTPPlugin + + path := baseUrl + "/sc/v1/plugins" + req, err := http.NewRequest("GET", path, nil) + if err != nil { + return nil, fmt.Errorf("failed to fetch plugins") + } + req.Header.Set("Authorization", "Bearer "+token) + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to fetch plugins") + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to fetch plugins") + } + + json.Unmarshal(body, &allPlugins) + return allPlugins, nil +} diff --git a/sc-poc/utils/string.go b/sc-poc/utils/string.go index 3facd6133..7a12eb168 100644 --- a/sc-poc/utils/string.go +++ b/sc-poc/utils/string.go @@ -21,3 +21,10 @@ func Pluralize(word string) string { plural := pluralize.Plural(word) return strings.ToLower(plural) } + +func EnsureTrailingSlash(url string) string { + if !strings.HasSuffix(url, "/") { + return url + "/" + } + return url +} From cc8cdda889715a6e280f5f45b4165851824ada99 Mon Sep 17 00:00:00 2001 From: Shubham Nazare Date: Sun, 24 Sep 2023 17:29:15 +0530 Subject: [PATCH 2/2] add plugins for typescript Signed-off-by: Shubham Nazare --- .../generate/driver/typescript/common.go | 11 ++++++ .../generate/driver/typescript/plugins.go | 34 +++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/common.go b/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/common.go index a5ab2c53d..1ee8cd69d 100644 --- a/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/common.go +++ b/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/common.go @@ -14,6 +14,17 @@ func getTypeName(name string, skipFirst bool) string { arr[i] = strings.Title(item) } + s1 := strings.Join(arr, "") + + arr = strings.Split(s1, "_") + for i, item := range arr { + if i == 0 && skipFirst { + arr[i] = item + continue + } + + arr[i] = strings.Title(item) + } return strings.Join(arr, "") } diff --git a/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/plugins.go b/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/plugins.go index e41b7764d..10cd7644e 100644 --- a/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/plugins.go +++ b/sc-poc/cmd/spacectl/commands/client/generate/driver/typescript/plugins.go @@ -1,7 +1,37 @@ package typescript -import "github.com/spacecloud-io/space-cloud/pkg/apis/core/v1alpha1" +import ( + "fmt" + "strings" + + "github.com/spacecloud-io/space-cloud/pkg/apis/core/v1alpha1" +) func (t *Typescript) GeneratePlugins(plugins []v1alpha1.HTTPPlugin) (string, string, error) { - return "", "", nil + fileName := "plugins.ts" + var b strings.Builder + + s := + `interface PluginDetails { + name: string, + driver: string +} + +export class Plugins { +` + + for _, plugin := range plugins { + driverName := getTypeName(plugin.Driver, false) + getTypeName(plugin.Name, false) + s += fmt.Sprintf(" %s = (): PluginDetails => {\n", driverName) + s += fmt.Sprintf(" const %s: PluginDetails = {\n", driverName) + s += fmt.Sprintf(" name: %q,\n", plugin.Name) + s += fmt.Sprintf(" driver: %q,\n", plugin.Driver) + s += " }\n" + s += fmt.Sprintf(" return %s\n", driverName) + s += " }\n\n" + } + + s += "}\n" + _, _ = b.WriteString(s) + return b.String(), fileName, nil }