From df1790e7a10f92bab216e2cb5d299fdf6c1ea715 Mon Sep 17 00:00:00 2001 From: Srinandan Sridhar <13950006+srinandan@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:55:46 -0800 Subject: [PATCH] feat: export and import apicategories (#353) * feat: export apicategories #352 * chore: fix vscode settings * feat: aads support to import apicategories #352 * feat: include import cmd for categories #352 --- .vscode/settings.json | 2 +- cmd/apicategories/apicategories.go | 2 + cmd/apicategories/export.go | 52 +++++++++++ cmd/apicategories/import.go | 50 +++++++++++ cmd/apidocs/import.go | 2 +- cmd/org/export.go | 6 ++ .../client/apicategories/apicategories.go | 90 +++++++++++++++++++ 7 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 cmd/apicategories/export.go create mode 100644 cmd/apicategories/import.go diff --git a/.vscode/settings.json b/.vscode/settings.json index 9680592a9..943f8602b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,7 +24,7 @@ "editor.insertSpaces": true, "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.organizeImports": true + "source.organizeImports": "explicit" }, "editor.defaultFormatter": "golang.go" }, diff --git a/cmd/apicategories/apicategories.go b/cmd/apicategories/apicategories.go index 35ea60101..858116a2b 100644 --- a/cmd/apicategories/apicategories.go +++ b/cmd/apicategories/apicategories.go @@ -37,6 +37,8 @@ func init() { Cmd.AddCommand(GetCmd) Cmd.AddCommand(DelCmd) Cmd.AddCommand(CreateCmd) + Cmd.AddCommand(ExpCmd) + Cmd.AddCommand(ImpCmd) _ = Cmd.MarkFlagRequired("org") _ = Cmd.MarkFlagRequired("siteid") diff --git a/cmd/apicategories/export.go b/cmd/apicategories/export.go new file mode 100644 index 000000000..eef24b64c --- /dev/null +++ b/cmd/apicategories/export.go @@ -0,0 +1,52 @@ +// Copyright 2023 Google LLC +// +// 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 apicategories + +import ( + "os" + + "internal/apiclient" + + "internal/client/apicategories" + + "github.com/spf13/cobra" +) + +// ExpCmd to export apidocs +var ExpCmd = &cobra.Command{ + Use: "export", + Short: "Export API Categories across all sites", + Long: "Export API Categories across all sites", + Args: func(cmd *cobra.Command, args []string) (err error) { + return apiclient.SetApigeeOrg(org) + }, + RunE: func(cmd *cobra.Command, args []string) (err error) { + if folder == "" { + folder, _ = os.Getwd() + } + if err = apiclient.FolderExists(folder); err != nil { + return err + } + apiclient.DisableCmdPrintHttpResponse() + return apicategories.Export(folder) + }, +} + +var folder string + +func init() { + ExpCmd.Flags().StringVarP(&folder, "folder", "f", + "", "folder to export API Docs") +} diff --git a/cmd/apicategories/import.go b/cmd/apicategories/import.go new file mode 100644 index 000000000..907850728 --- /dev/null +++ b/cmd/apicategories/import.go @@ -0,0 +1,50 @@ +// Copyright 2023 Google LLC +// +// 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 apicategories + +import ( + "fmt" + + "internal/apiclient" + + "internal/client/apicategories" + + "github.com/spf13/cobra" +) + +// ImpCmd to import products +var ImpCmd = &cobra.Command{ + Use: "import", + Short: "Import from a folder containing apicategories", + Long: "Import from a folder containing apicategories", + Args: func(cmd *cobra.Command, args []string) (err error) { + return apiclient.SetApigeeOrg(org) + }, + RunE: func(cmd *cobra.Command, args []string) error { + if siteid == "" { + return fmt.Errorf("siteid is a mandatory parameter") + } + return apicategories.Import(siteid, apicategoryFile) + }, +} + +var apicategoryFile string + +func init() { + ImpCmd.Flags().StringVarP(&apicategoryFile, "file", "f", + "", "A file containing apicategories") + + _ = ImpCmd.MarkFlagRequired("file") +} diff --git a/cmd/apidocs/import.go b/cmd/apidocs/import.go index 737e52627..572c4cb6f 100644 --- a/cmd/apidocs/import.go +++ b/cmd/apidocs/import.go @@ -44,5 +44,5 @@ func init() { ImpCmd.Flags().StringVarP(&folder, "folder", "f", "", "Folder containing site_.json and apidocs__.json files") - _ = ImpCmd.MarkFlagRequired("file") + _ = ImpCmd.MarkFlagRequired("folder") } diff --git a/cmd/org/export.go b/cmd/org/export.go index 037c563b6..2aa5912dd 100644 --- a/cmd/org/export.go +++ b/cmd/org/export.go @@ -26,6 +26,7 @@ import ( "internal/clilog" + "internal/client/apicategories" "internal/client/apidocs" "internal/client/apis" "internal/client/appgroups" @@ -186,6 +187,11 @@ var ExportCmd = &cobra.Command{ return err } + clilog.Info.Println("Exporting API Portal apicategories Configuration...") + if err = apicategories.Export(portalsFolderName); proceedOnError(err) != nil { + return err + } + if runtimeType == "HYBRID" { clilog.Info.Println("Exporting Sync Authorization Identities...") if respBody, err = sync.Get(); err != nil { diff --git a/internal/client/apicategories/apicategories.go b/internal/client/apicategories/apicategories.go index 22d14a411..cc01a0137 100644 --- a/internal/client/apicategories/apicategories.go +++ b/internal/client/apicategories/apicategories.go @@ -15,13 +15,36 @@ package apicategories import ( + "encoding/json" + "errors" + "fmt" + "io" "net/url" + "os" "path" "strings" "internal/apiclient" + "internal/client/sites" + "internal/clilog" ) +type listapicategories struct { + Status string `json:"status,omitempty"` + Message string `json:"message,omitempty"` + RequestID string `json:"requestId,omitempty"` + ErrorCode string `json:"errorCode,omitempty"` + Data []data `json:"data,omitempty"` + NextPageToken string `json:"nextPageToken,omitempty"` +} + +type data struct { + SiteID string `json:"siteId,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + UpdateTime string `json:"updateTime,omitempty"` +} + // Create func Create(siteid string, name string) (respBody []byte, err error) { apicategories := []string{} @@ -69,3 +92,70 @@ func Update(siteid string, name string) (respBody []byte, err error) { respBody, err = apiclient.HttpClient(u.String(), payload, "PATCH") return respBody, err } + +// Export +func Export(folder string) (err error) { + apiclient.ClientPrintHttpResponse.Set(false) + defer apiclient.ClientPrintHttpResponse.Set(apiclient.GetCmdPrintHttpResponseSetting()) + + siteids, err := sites.GetSiteIDs() + if err != nil { + return err + } + + for _, siteid := range siteids { + listRespBytes, err := List(siteid) + if err != nil { + return fmt.Errorf("failed to fetch apicategories: %w", err) + } + + docFileName := fmt.Sprintf("apicategory_%s.json", siteid) + if err = apiclient.WriteByteArrayToFile(path.Join(folder, docFileName), false, listRespBytes); err != nil { + return err + } + } + return nil +} + +// Import +func Import(siteid string, apicateogyFile string) (err error) { + errs := []string{} + l, err := readAPICategoriesFile(apicateogyFile) + if err != nil { + return err + } + if len(l.Data) < 1 { + clilog.Warning.Println("No categories found for the siteid") + return nil + } + + for _, category := range l.Data { + _, err = Create(siteid, category.Name) + if err != nil { + errs = append(errs, err.Error()) + } + } + if len(errs) > 0 { + return errors.New(strings.Join(errs, "\n")) + } + return nil +} + +func readAPICategoriesFile(fileName string) (l listapicategories, err error) { + jsonFile, err := os.Open(fileName) + if err != nil { + return l, err + } + + defer jsonFile.Close() + + content, err := io.ReadAll(jsonFile) + if err != nil { + return l, err + } + err = json.Unmarshal(content, &l) + if err != nil { + return l, err + } + return l, nil +}