Skip to content

Commit 794e07b

Browse files
authored
Merge pull request #394 from IBM-Cloud/dev
Release 1.2.0
2 parents f807cfb + f2f5c79 commit 794e07b

File tree

11 files changed

+311
-110
lines changed

11 files changed

+311
-110
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
language: go
2-
dist: bionic
2+
dist: focal
33
go:
4-
- '1.17.x'
4+
- '1.21.x'
55
addons:
66
apt:
77
packages:

bluemix/configuration/config_helpers/helpers.go

+44-17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
package config_helpers
33

44
import (
5+
"encoding/base64"
6+
gourl "net/url"
57
"os"
68
"path/filepath"
79
"runtime"
@@ -21,23 +23,6 @@ func ConfigDir() string {
2123
return defaultConfigDirOld()
2224
}
2325

24-
// func MigrateFromOldConfig() error {
25-
// new := defaultConfigDirNew()
26-
// if file_helpers.FileExists(new) {
27-
// return nil
28-
// }
29-
30-
// old := defaultConfigDirOld()
31-
// if !file_helpers.FileExists(old) {
32-
// return nil
33-
// }
34-
35-
// if err := file_helpers.CopyDir(old, new); err != nil {
36-
// return err
37-
// }
38-
// return os.RemoveAll(old)
39-
// }
40-
4126
func defaultConfigDirNew() string {
4227
return filepath.Join(homeDir(), ".ibmcloud")
4328
}
@@ -108,3 +93,45 @@ func UserHomeDir() string {
10893

10994
return os.Getenv("HOME")
11095
}
96+
97+
// IsValidPaginationNextURL will return true if the provided nextURL has the expected queries provided
98+
func IsValidPaginationNextURL(nextURL string, cursorQueryParamName string, expectedQueries gourl.Values) bool {
99+
parsedURL, parseErr := gourl.Parse(nextURL)
100+
// NOTE: ignore handling error(s) since if there error(s)
101+
// we can assume the url is invalid
102+
if parseErr != nil {
103+
return false
104+
}
105+
106+
// retrive encoded cursor
107+
// eg. /api?cursor=<encode_string>
108+
queries := parsedURL.Query()
109+
encodedQuery := queries.Get(cursorQueryParamName)
110+
if encodedQuery == "" {
111+
return false
112+
113+
}
114+
// decode string and parse encoded queries
115+
decodedQuery, decodedErr := base64.RawURLEncoding.DecodeString(encodedQuery)
116+
if decodedErr != nil {
117+
return false
118+
}
119+
queries, parsedErr := gourl.ParseQuery(string(decodedQuery))
120+
if parsedErr != nil {
121+
return false
122+
}
123+
124+
// compare expected queries that should match
125+
// NOTE: assume queries are single value queries.
126+
// if multi-value queries will check the first query
127+
for expectedQuery := range expectedQueries {
128+
paginationQueryValue := queries.Get(expectedQuery)
129+
expectedQueryValue := expectedQueries.Get(expectedQuery)
130+
if paginationQueryValue != expectedQueryValue {
131+
return false
132+
}
133+
134+
}
135+
136+
return true
137+
}

bluemix/configuration/config_helpers/helpers_test.go

+60-78
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package config_helpers
22

33
import (
4+
"encoding/base64"
45
"io/ioutil"
6+
gourl "net/url"
57
"os"
68
"path/filepath"
79
"strings"
@@ -104,81 +106,61 @@ func TestConfigDir_IbmCloudConfigHomeSet_Exists(t *testing.T) {
104106
assert.Equal(userHome, ConfigDir())
105107
}
106108

107-
// func TestMigrateFromOldConfig(t *testing.T) {
108-
// assert := assert.New(t)
109-
110-
// err := prepareBluemixHome()
111-
// assert.NoError(err)
112-
// defer clearBluemixHome()
113-
114-
// err = os.MkdirAll(oldConfigDir(), 0700)
115-
// assert.NoError(err)
116-
// oldConfigPath := filepath.Join(oldConfigDir(), "config.json")
117-
// err = ioutil.WriteFile(oldConfigPath, []byte("old"), 0600)
118-
// assert.NoError(err)
119-
120-
// err = MigrateFromOldConfig()
121-
// assert.NoError(err)
122-
123-
// newConfigPath := filepath.Join(newConfigDir(), "config.json")
124-
// assert.True(file_helpers.FileExists(newConfigPath))
125-
// content, err := ioutil.ReadFile(newConfigPath)
126-
// assert.NoError(err)
127-
// assert.Equal([]byte("old"), content, "should copy old config file")
128-
129-
// assert.False(file_helpers.FileExists(oldConfigDir()), "old config dir should be deleted")
130-
// }
131-
132-
// func TestMigrateFromOldConfig_NewConfigExist(t *testing.T) {
133-
// assert := assert.New(t)
134-
135-
// err := prepareBluemixHome()
136-
// assert.NoError(err)
137-
// defer clearBluemixHome()
138-
139-
// err = os.MkdirAll(oldConfigDir(), 0700)
140-
// assert.NoError(err)
141-
// oldConfigPath := filepath.Join(oldConfigDir(), "config.json")
142-
// err = ioutil.WriteFile(oldConfigPath, []byte("old"), 0600)
143-
// assert.NoError(err)
144-
145-
// err = os.MkdirAll(newConfigDir(), 0700)
146-
// assert.NoError(err)
147-
// newConfigPath := filepath.Join(newConfigDir(), "config.json")
148-
// err = ioutil.WriteFile(newConfigPath, []byte("new"), 0600)
149-
// assert.NoError(err)
150-
151-
// err = MigrateFromOldConfig()
152-
// assert.NoError(err)
153-
154-
// content, err := ioutil.ReadFile(newConfigPath)
155-
// assert.NoError(err)
156-
// assert.Equal([]byte("new"), content, "should not copy old config file")
157-
// }
158-
159-
// func TestMigrateFromOldConfig_OldConfigNotExist(t *testing.T) {
160-
// assert := assert.New(t)
161-
162-
// err := prepareBluemixHome()
163-
// assert.NoError(err)
164-
// defer clearBluemixHome()
165-
166-
// err = MigrateFromOldConfig()
167-
// assert.NoError(err)
168-
// }
169-
170-
// func prepareBluemixHome() error {
171-
// temp, err := ioutil.TempDir("", "IBMCloudSDKConfigTest")
172-
// if err != nil {
173-
// return err
174-
// }
175-
// os.Setenv("BLUEMIX_HOME", temp)
176-
// return nil
177-
// }
178-
179-
// func clearBluemixHome() {
180-
// if homeDir := os.Getenv("BLUEMIX_HOME"); homeDir != "" {
181-
// os.RemoveAll(homeDir)
182-
// os.Unsetenv("BLUEMIX_HOME")
183-
// }
184-
// }
109+
func TestIsValidPaginationNextURL(t *testing.T) {
110+
assert := assert.New(t)
111+
112+
testCases := []struct {
113+
name string
114+
nextURL string
115+
encodedQueryParam string
116+
expectedQueries gourl.Values
117+
isValid bool
118+
}{
119+
{
120+
name: "return true for matching expected queries in pagination url",
121+
nextURL: "/api/example?cursor=" + base64.RawURLEncoding.EncodeToString([]byte("limit=100&active=true")),
122+
encodedQueryParam: "cursor",
123+
expectedQueries: gourl.Values{
124+
"limit": []string{"100"},
125+
"active": []string{"true"},
126+
},
127+
isValid: true,
128+
},
129+
{
130+
name: "return true for matching expected queries with extraneous queries in pagination url",
131+
nextURL: "/api/example?cursor=" + base64.RawURLEncoding.EncodeToString([]byte("limit=100&active=true&extra=foo")),
132+
encodedQueryParam: "cursor",
133+
expectedQueries: gourl.Values{
134+
"limit": []string{"100"},
135+
"active": []string{"true"},
136+
},
137+
isValid: true,
138+
},
139+
{
140+
name: "return false for different limit in pagination url",
141+
nextURL: "/api/example?cursor=" + base64.RawURLEncoding.EncodeToString([]byte("limit=200")),
142+
encodedQueryParam: "cursor",
143+
expectedQueries: gourl.Values{
144+
"limit": []string{"100"},
145+
},
146+
isValid: false,
147+
},
148+
{
149+
name: "return false for different query among multiple parameters in the pagination url",
150+
nextURL: "/api/example?cursor=" + base64.RawURLEncoding.EncodeToString([]byte("limit=100&active=true")),
151+
encodedQueryParam: "cursor",
152+
expectedQueries: gourl.Values{
153+
"limit": []string{"100"},
154+
"active": []string{"false"},
155+
},
156+
isValid: false,
157+
},
158+
}
159+
160+
for _, tc := range testCases {
161+
t.Run(tc.name, func(_ *testing.T) {
162+
isValid := IsValidPaginationNextURL(tc.nextURL, tc.encodedQueryParam, tc.expectedQueries)
163+
assert.Equal(tc.isValid, isValid)
164+
})
165+
}
166+
}

bluemix/configuration/core_config/bx_config.go

+36
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package core_config
22

33
import (
44
"encoding/json"
5+
"sort"
56
"strings"
67
"sync"
78
"time"
@@ -63,6 +64,7 @@ type BXConfigData struct {
6364
UpdateCheckInterval time.Duration
6465
UpdateRetryCheckInterval time.Duration
6566
UpdateNotificationInterval time.Duration
67+
PaginationURLs []models.PaginationURL
6668
raw raw
6769
}
6870

@@ -752,9 +754,43 @@ func (c *bxConfig) ClearSession() {
752754
c.data.IsLoggedInAsCRI = false
753755
c.data.ResourceGroup = models.ResourceGroup{}
754756
c.data.LoginAt = time.Time{}
757+
c.data.PaginationURLs = []models.PaginationURL{}
755758
})
756759
}
757760

761+
func (c *bxConfig) SetPaginationURLs(paginationURLs []models.PaginationURL) {
762+
c.write(func() {
763+
c.data.PaginationURLs = paginationURLs
764+
})
765+
}
766+
767+
func (c *bxConfig) ClearPaginationURLs() {
768+
c.write(func() {
769+
c.data.PaginationURLs = []models.PaginationURL{}
770+
})
771+
}
772+
773+
func (c *bxConfig) AddPaginationURL(index int, url string) {
774+
urls := c.PaginationURLs()
775+
776+
urls = append(urls, models.PaginationURL{
777+
LastIndex: index,
778+
NextURL: url,
779+
})
780+
781+
// sort by last index for easier retrieval
782+
sort.Sort(models.ByLastIndex(urls))
783+
c.SetPaginationURLs(urls)
784+
}
785+
786+
func (c *bxConfig) PaginationURLs() (paginationURLs []models.PaginationURL) {
787+
c.read(func() {
788+
paginationURLs = c.data.PaginationURLs
789+
})
790+
791+
return
792+
}
793+
758794
func (c *bxConfig) UnsetAPI() {
759795
c.write(func() {
760796
c.data.APIEndpoint = ""

bluemix/configuration/core_config/bx_config_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,64 @@ func TestLastUpdateSessionTime(t *testing.T) {
469469

470470
}
471471

472+
func TestPaginationURLs(t *testing.T) {
473+
config := prepareConfigForCLI(`{}`, t)
474+
475+
// check initial state
476+
paginationURLs := config.PaginationURLs()
477+
assert.Empty(t, paginationURLs)
478+
479+
expected := []models.PaginationURL{
480+
{
481+
NextURL: "https://api.example.com?token=dd3784000d9744acb2a23ad121a7bb4b",
482+
LastIndex: 50,
483+
},
484+
}
485+
config.SetPaginationURLs(expected)
486+
487+
paginationURLs = config.PaginationURLs()
488+
assert.Equal(t, 1, len(paginationURLs))
489+
assert.Equal(t, expected[0].LastIndex, paginationURLs[0].LastIndex)
490+
assert.Equal(t, expected[0].NextURL, paginationURLs[0].NextURL)
491+
492+
t.Cleanup(cleanupConfigFiles)
493+
494+
}
495+
496+
func TestAddPaginationURL(t *testing.T) {
497+
config := prepareConfigForCLI(`{}`, t)
498+
assert := assert.New(t)
499+
unsortedUrls := []models.PaginationURL{
500+
{
501+
NextURL: "/v2/example.com/stuff?limit=200",
502+
LastIndex: 200,
503+
},
504+
{
505+
NextURL: "/v2/example.com/stuff?limit=100",
506+
LastIndex: 50,
507+
},
508+
{
509+
NextURL: "/v2/example.com/stuff?limit=100",
510+
LastIndex: 100,
511+
},
512+
}
513+
514+
for _, p := range unsortedUrls {
515+
config.AddPaginationURL(p.LastIndex, p.NextURL)
516+
}
517+
518+
// expect url to be sorted in ascending order by LastIndex
519+
sortedUrls := config.PaginationURLs()
520+
521+
assert.Equal(3, len(sortedUrls))
522+
assert.Equal(sortedUrls[0].LastIndex, unsortedUrls[1].LastIndex)
523+
assert.Equal(sortedUrls[0].NextURL, unsortedUrls[1].NextURL)
524+
assert.Equal(sortedUrls[1].LastIndex, unsortedUrls[2].LastIndex)
525+
assert.Equal(sortedUrls[1].NextURL, unsortedUrls[2].NextURL)
526+
assert.Equal(sortedUrls[2].LastIndex, unsortedUrls[0].LastIndex)
527+
assert.Equal(sortedUrls[2].NextURL, unsortedUrls[0].NextURL)
528+
}
529+
472530
func checkUsageStats(enabled bool, timeStampExist bool, config core_config.Repository, t *testing.T) {
473531
assert.Equal(t, config.UsageStatsEnabled(), enabled)
474532
assert.Equal(t, config.UsageStatsEnabledLastUpdate().IsZero(), !timeStampExist)

bluemix/configuration/core_config/repository.go

+21
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ type Repository interface {
125125

126126
SetLastSessionUpdateTime()
127127
LastSessionUpdateTime() (session int64)
128+
129+
SetPaginationURLs(paginationURLs []models.PaginationURL)
130+
ClearPaginationURLs()
131+
AddPaginationURL(lastIndex int, nextURL string)
132+
PaginationURLs() []models.PaginationURL
128133
}
129134

130135
// Deprecated
@@ -368,6 +373,22 @@ func (c repository) SetLastSessionUpdateTime() {
368373
c.bxConfig.SetLastSessionUpdateTime()
369374
}
370375

376+
func (c repository) PaginationURLs() []models.PaginationURL {
377+
return c.bxConfig.PaginationURLs()
378+
}
379+
380+
func (c repository) AddPaginationURL(index int, url string) {
381+
c.bxConfig.AddPaginationURL(index, url)
382+
}
383+
384+
func (c repository) SetPaginationURLs(paginationURLs []models.PaginationURL) {
385+
c.bxConfig.SetPaginationURLs(paginationURLs)
386+
}
387+
388+
func (c repository) ClearPaginationURLs() {
389+
c.bxConfig.ClearPaginationURLs()
390+
}
391+
371392
func NewCoreConfig(errHandler func(error)) ReadWriter {
372393
// config_helpers.MigrateFromOldConfig() // error ignored
373394
return NewCoreConfigFromPath(config_helpers.CFConfigFilePath(), config_helpers.ConfigFilePath(), errHandler)

0 commit comments

Comments
 (0)