Skip to content

Commit a3f3f5d

Browse files
authored
Merge pull request #391 from IBM-Cloud/feat/pagination-cache
feat: added methods for caching pagination urls into config
2 parents b469c05 + 175d539 commit a3f3f5d

File tree

10 files changed

+206
-110
lines changed

10 files changed

+206
-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

-17
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,6 @@ func ConfigDir() string {
2121
return defaultConfigDirOld()
2222
}
2323

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-
4124
func defaultConfigDirNew() string {
4225
return filepath.Join(homeDir(), ".ibmcloud")
4326
}

bluemix/configuration/config_helpers/helpers_test.go

-79
Original file line numberDiff line numberDiff line change
@@ -103,82 +103,3 @@ func TestConfigDir_IbmCloudConfigHomeSet_Exists(t *testing.T) {
103103
os.Setenv("IBMCLOUD_CONFIG_HOME", userHome)
104104
assert.Equal(userHome, ConfigDir())
105105
}
106-
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-
// }

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)

bluemix/models/pagination.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package models
2+
3+
// ByLastIndex sorts PaginationURLs by LastIndex
4+
type ByLastIndex []PaginationURL
5+
6+
func (a ByLastIndex) Len() int { return len(a) }
7+
8+
func (a ByLastIndex) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
9+
10+
func (a ByLastIndex) Less(i, j int) bool { return a[i].LastIndex < a[j].LastIndex }
11+
12+
type PaginationURL struct {
13+
LastIndex int
14+
NextURL string
15+
}

common/rest/request.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ import (
5151
"net/textproto"
5252
"net/url"
5353
"strings"
54+
55+
"github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/models"
5456
)
5557

5658
const (
57-
contentType = "Content-Type"
58-
jsonContentType = "application/json"
59-
formUrlEncodedContentType = "application/x-www-form-urlencoded"
59+
ContentType = "Content-Type"
60+
JSONContentType = "application/json"
61+
FormUrlEncodedContentType = "application/x-www-form-urlencoded"
6062
)
6163

6264
// File represents a file upload in HTTP request
@@ -138,6 +140,17 @@ func OptionsRequest(rawUrl string) *Request {
138140
return NewRequest(rawUrl).Method("OPTIONS")
139141
}
140142

143+
// CachedPaginationNextURL will attempt to return a cached URL with last index
144+
// if there exists a URL with a last index smaller than the offset provided
145+
func CachedPaginationNextURL(paginationURLs []models.PaginationURL, offset int) models.PaginationURL {
146+
for _, p := range paginationURLs {
147+
if p.LastIndex < offset {
148+
return p
149+
}
150+
}
151+
return models.PaginationURL{}
152+
}
153+
141154
// Add adds the key, value pair to the request header. It appends to any
142155
// existing values associated with key.
143156
func (r *Request) Add(key string, value string) *Request {
@@ -272,7 +285,7 @@ func (r *Request) buildFormMultipart() (io.Reader, error) {
272285
}
273286
}
274287

275-
r.header.Set(contentType, w.FormDataContentType())
288+
r.header.Set(ContentType, w.FormDataContentType())
276289
return b, nil
277290
}
278291

@@ -296,7 +309,7 @@ func escapeQuotes(s string) string {
296309
}
297310

298311
func (r *Request) buildFormFields() (io.Reader, error) {
299-
r.header.Set(contentType, formUrlEncodedContentType)
312+
r.header.Set(ContentType, FormUrlEncodedContentType)
300313
return strings.NewReader(r.formParams.Encode()), nil
301314
}
302315

0 commit comments

Comments
 (0)