Skip to content

Commit

Permalink
Merge pull request #394 from IBM-Cloud/dev
Browse files Browse the repository at this point in the history
Release 1.2.0
  • Loading branch information
Aerex authored Dec 1, 2023
2 parents f807cfb + f2f5c79 commit 794e07b
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 110 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: go
dist: bionic
dist: focal
go:
- '1.17.x'
- '1.21.x'
addons:
apt:
packages:
Expand Down
61 changes: 44 additions & 17 deletions bluemix/configuration/config_helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
package config_helpers

import (
"encoding/base64"
gourl "net/url"
"os"
"path/filepath"
"runtime"
Expand All @@ -21,23 +23,6 @@ func ConfigDir() string {
return defaultConfigDirOld()
}

// func MigrateFromOldConfig() error {
// new := defaultConfigDirNew()
// if file_helpers.FileExists(new) {
// return nil
// }

// old := defaultConfigDirOld()
// if !file_helpers.FileExists(old) {
// return nil
// }

// if err := file_helpers.CopyDir(old, new); err != nil {
// return err
// }
// return os.RemoveAll(old)
// }

func defaultConfigDirNew() string {
return filepath.Join(homeDir(), ".ibmcloud")
}
Expand Down Expand Up @@ -108,3 +93,45 @@ func UserHomeDir() string {

return os.Getenv("HOME")
}

// IsValidPaginationNextURL will return true if the provided nextURL has the expected queries provided
func IsValidPaginationNextURL(nextURL string, cursorQueryParamName string, expectedQueries gourl.Values) bool {
parsedURL, parseErr := gourl.Parse(nextURL)
// NOTE: ignore handling error(s) since if there error(s)
// we can assume the url is invalid
if parseErr != nil {
return false
}

// retrive encoded cursor
// eg. /api?cursor=<encode_string>
queries := parsedURL.Query()
encodedQuery := queries.Get(cursorQueryParamName)
if encodedQuery == "" {
return false

}
// decode string and parse encoded queries
decodedQuery, decodedErr := base64.RawURLEncoding.DecodeString(encodedQuery)
if decodedErr != nil {
return false
}
queries, parsedErr := gourl.ParseQuery(string(decodedQuery))
if parsedErr != nil {
return false
}

// compare expected queries that should match
// NOTE: assume queries are single value queries.
// if multi-value queries will check the first query
for expectedQuery := range expectedQueries {
paginationQueryValue := queries.Get(expectedQuery)
expectedQueryValue := expectedQueries.Get(expectedQuery)
if paginationQueryValue != expectedQueryValue {
return false
}

}

return true
}
138 changes: 60 additions & 78 deletions bluemix/configuration/config_helpers/helpers_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package config_helpers

import (
"encoding/base64"
"io/ioutil"
gourl "net/url"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -104,81 +106,61 @@ func TestConfigDir_IbmCloudConfigHomeSet_Exists(t *testing.T) {
assert.Equal(userHome, ConfigDir())
}

// func TestMigrateFromOldConfig(t *testing.T) {
// assert := assert.New(t)

// err := prepareBluemixHome()
// assert.NoError(err)
// defer clearBluemixHome()

// err = os.MkdirAll(oldConfigDir(), 0700)
// assert.NoError(err)
// oldConfigPath := filepath.Join(oldConfigDir(), "config.json")
// err = ioutil.WriteFile(oldConfigPath, []byte("old"), 0600)
// assert.NoError(err)

// err = MigrateFromOldConfig()
// assert.NoError(err)

// newConfigPath := filepath.Join(newConfigDir(), "config.json")
// assert.True(file_helpers.FileExists(newConfigPath))
// content, err := ioutil.ReadFile(newConfigPath)
// assert.NoError(err)
// assert.Equal([]byte("old"), content, "should copy old config file")

// assert.False(file_helpers.FileExists(oldConfigDir()), "old config dir should be deleted")
// }

// func TestMigrateFromOldConfig_NewConfigExist(t *testing.T) {
// assert := assert.New(t)

// err := prepareBluemixHome()
// assert.NoError(err)
// defer clearBluemixHome()

// err = os.MkdirAll(oldConfigDir(), 0700)
// assert.NoError(err)
// oldConfigPath := filepath.Join(oldConfigDir(), "config.json")
// err = ioutil.WriteFile(oldConfigPath, []byte("old"), 0600)
// assert.NoError(err)

// err = os.MkdirAll(newConfigDir(), 0700)
// assert.NoError(err)
// newConfigPath := filepath.Join(newConfigDir(), "config.json")
// err = ioutil.WriteFile(newConfigPath, []byte("new"), 0600)
// assert.NoError(err)

// err = MigrateFromOldConfig()
// assert.NoError(err)

// content, err := ioutil.ReadFile(newConfigPath)
// assert.NoError(err)
// assert.Equal([]byte("new"), content, "should not copy old config file")
// }

// func TestMigrateFromOldConfig_OldConfigNotExist(t *testing.T) {
// assert := assert.New(t)

// err := prepareBluemixHome()
// assert.NoError(err)
// defer clearBluemixHome()

// err = MigrateFromOldConfig()
// assert.NoError(err)
// }

// func prepareBluemixHome() error {
// temp, err := ioutil.TempDir("", "IBMCloudSDKConfigTest")
// if err != nil {
// return err
// }
// os.Setenv("BLUEMIX_HOME", temp)
// return nil
// }

// func clearBluemixHome() {
// if homeDir := os.Getenv("BLUEMIX_HOME"); homeDir != "" {
// os.RemoveAll(homeDir)
// os.Unsetenv("BLUEMIX_HOME")
// }
// }
func TestIsValidPaginationNextURL(t *testing.T) {
assert := assert.New(t)

testCases := []struct {
name string
nextURL string
encodedQueryParam string
expectedQueries gourl.Values
isValid bool
}{
{
name: "return true for matching expected queries in pagination url",
nextURL: "/api/example?cursor=" + base64.RawURLEncoding.EncodeToString([]byte("limit=100&active=true")),
encodedQueryParam: "cursor",
expectedQueries: gourl.Values{
"limit": []string{"100"},
"active": []string{"true"},
},
isValid: true,
},
{
name: "return true for matching expected queries with extraneous queries in pagination url",
nextURL: "/api/example?cursor=" + base64.RawURLEncoding.EncodeToString([]byte("limit=100&active=true&extra=foo")),
encodedQueryParam: "cursor",
expectedQueries: gourl.Values{
"limit": []string{"100"},
"active": []string{"true"},
},
isValid: true,
},
{
name: "return false for different limit in pagination url",
nextURL: "/api/example?cursor=" + base64.RawURLEncoding.EncodeToString([]byte("limit=200")),
encodedQueryParam: "cursor",
expectedQueries: gourl.Values{
"limit": []string{"100"},
},
isValid: false,
},
{
name: "return false for different query among multiple parameters in the pagination url",
nextURL: "/api/example?cursor=" + base64.RawURLEncoding.EncodeToString([]byte("limit=100&active=true")),
encodedQueryParam: "cursor",
expectedQueries: gourl.Values{
"limit": []string{"100"},
"active": []string{"false"},
},
isValid: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(_ *testing.T) {
isValid := IsValidPaginationNextURL(tc.nextURL, tc.encodedQueryParam, tc.expectedQueries)
assert.Equal(tc.isValid, isValid)
})
}
}
36 changes: 36 additions & 0 deletions bluemix/configuration/core_config/bx_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package core_config

import (
"encoding/json"
"sort"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -63,6 +64,7 @@ type BXConfigData struct {
UpdateCheckInterval time.Duration
UpdateRetryCheckInterval time.Duration
UpdateNotificationInterval time.Duration
PaginationURLs []models.PaginationURL
raw raw
}

Expand Down Expand Up @@ -752,9 +754,43 @@ func (c *bxConfig) ClearSession() {
c.data.IsLoggedInAsCRI = false
c.data.ResourceGroup = models.ResourceGroup{}
c.data.LoginAt = time.Time{}
c.data.PaginationURLs = []models.PaginationURL{}
})
}

func (c *bxConfig) SetPaginationURLs(paginationURLs []models.PaginationURL) {
c.write(func() {
c.data.PaginationURLs = paginationURLs
})
}

func (c *bxConfig) ClearPaginationURLs() {
c.write(func() {
c.data.PaginationURLs = []models.PaginationURL{}
})
}

func (c *bxConfig) AddPaginationURL(index int, url string) {
urls := c.PaginationURLs()

urls = append(urls, models.PaginationURL{
LastIndex: index,
NextURL: url,
})

// sort by last index for easier retrieval
sort.Sort(models.ByLastIndex(urls))
c.SetPaginationURLs(urls)
}

func (c *bxConfig) PaginationURLs() (paginationURLs []models.PaginationURL) {
c.read(func() {
paginationURLs = c.data.PaginationURLs
})

return
}

func (c *bxConfig) UnsetAPI() {
c.write(func() {
c.data.APIEndpoint = ""
Expand Down
58 changes: 58 additions & 0 deletions bluemix/configuration/core_config/bx_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,64 @@ func TestLastUpdateSessionTime(t *testing.T) {

}

func TestPaginationURLs(t *testing.T) {
config := prepareConfigForCLI(`{}`, t)

// check initial state
paginationURLs := config.PaginationURLs()
assert.Empty(t, paginationURLs)

expected := []models.PaginationURL{
{
NextURL: "https://api.example.com?token=dd3784000d9744acb2a23ad121a7bb4b",
LastIndex: 50,
},
}
config.SetPaginationURLs(expected)

paginationURLs = config.PaginationURLs()
assert.Equal(t, 1, len(paginationURLs))
assert.Equal(t, expected[0].LastIndex, paginationURLs[0].LastIndex)
assert.Equal(t, expected[0].NextURL, paginationURLs[0].NextURL)

t.Cleanup(cleanupConfigFiles)

}

func TestAddPaginationURL(t *testing.T) {
config := prepareConfigForCLI(`{}`, t)
assert := assert.New(t)
unsortedUrls := []models.PaginationURL{
{
NextURL: "/v2/example.com/stuff?limit=200",
LastIndex: 200,
},
{
NextURL: "/v2/example.com/stuff?limit=100",
LastIndex: 50,
},
{
NextURL: "/v2/example.com/stuff?limit=100",
LastIndex: 100,
},
}

for _, p := range unsortedUrls {
config.AddPaginationURL(p.LastIndex, p.NextURL)
}

// expect url to be sorted in ascending order by LastIndex
sortedUrls := config.PaginationURLs()

assert.Equal(3, len(sortedUrls))
assert.Equal(sortedUrls[0].LastIndex, unsortedUrls[1].LastIndex)
assert.Equal(sortedUrls[0].NextURL, unsortedUrls[1].NextURL)
assert.Equal(sortedUrls[1].LastIndex, unsortedUrls[2].LastIndex)
assert.Equal(sortedUrls[1].NextURL, unsortedUrls[2].NextURL)
assert.Equal(sortedUrls[2].LastIndex, unsortedUrls[0].LastIndex)
assert.Equal(sortedUrls[2].NextURL, unsortedUrls[0].NextURL)
}

func checkUsageStats(enabled bool, timeStampExist bool, config core_config.Repository, t *testing.T) {
assert.Equal(t, config.UsageStatsEnabled(), enabled)
assert.Equal(t, config.UsageStatsEnabledLastUpdate().IsZero(), !timeStampExist)
Expand Down
21 changes: 21 additions & 0 deletions bluemix/configuration/core_config/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ type Repository interface {

SetLastSessionUpdateTime()
LastSessionUpdateTime() (session int64)

SetPaginationURLs(paginationURLs []models.PaginationURL)
ClearPaginationURLs()
AddPaginationURL(lastIndex int, nextURL string)
PaginationURLs() []models.PaginationURL
}

// Deprecated
Expand Down Expand Up @@ -368,6 +373,22 @@ func (c repository) SetLastSessionUpdateTime() {
c.bxConfig.SetLastSessionUpdateTime()
}

func (c repository) PaginationURLs() []models.PaginationURL {
return c.bxConfig.PaginationURLs()
}

func (c repository) AddPaginationURL(index int, url string) {
c.bxConfig.AddPaginationURL(index, url)
}

func (c repository) SetPaginationURLs(paginationURLs []models.PaginationURL) {
c.bxConfig.SetPaginationURLs(paginationURLs)
}

func (c repository) ClearPaginationURLs() {
c.bxConfig.ClearPaginationURLs()
}

func NewCoreConfig(errHandler func(error)) ReadWriter {
// config_helpers.MigrateFromOldConfig() // error ignored
return NewCoreConfigFromPath(config_helpers.CFConfigFilePath(), config_helpers.ConfigFilePath(), errHandler)
Expand Down
Loading

0 comments on commit 794e07b

Please sign in to comment.