@@ -3,7 +3,10 @@ package utils
3
3
4
4
import (
5
5
"encoding/json"
6
+ "fmt"
7
+ "net/url"
6
8
"reflect"
9
+ "strings"
7
10
"time"
8
11
9
12
"github.com/algolia/algoliasearch-client-go/v4/algolia/errs"
@@ -65,49 +68,87 @@ func IsNilOrEmpty(i any) bool {
65
68
}
66
69
}
67
70
68
- type IterableError [ T any ] struct {
69
- Validate func (* T , error ) bool
70
- Message func (* T , error ) string
71
+ type IterableError struct {
72
+ Validate func (any , error ) bool
73
+ Message func (any , error ) string
71
74
}
72
75
73
- func CreateIterable [T any ](
74
- execute func (* T , error ) (* T , error ),
75
- validate func (* T , error ) bool ,
76
- aggregator func (* T , error ),
77
- timeout func () time.Duration ,
78
- iterableErr * IterableError [T ],
79
- ) (* T , error ) {
76
+ func CreateIterable [T any ](execute func (* T , error ) (* T , error ), validate func (* T , error ) bool , opts ... IterableOption ) (* T , error ) {
77
+ options := Options {
78
+ MaxRetries : 50 ,
79
+ Timeout : func (_ int ) time.Duration {
80
+ return 1 * time .Second
81
+ },
82
+ }
83
+
84
+ for _ , opt := range opts {
85
+ opt .Apply (& options )
86
+ }
87
+
80
88
var executor func (* T , error ) (* T , error )
81
89
90
+ retryCount := 0
91
+
82
92
executor = func (previousResponse * T , previousError error ) (* T , error ) {
83
93
response , responseErr := execute (previousResponse , previousError )
84
94
85
- if aggregator != nil {
86
- aggregator (response , responseErr )
95
+ retryCount ++
96
+
97
+ if options .Aggregator != nil {
98
+ options .Aggregator (response , responseErr )
87
99
}
88
100
89
101
if validate (response , responseErr ) {
90
102
return response , responseErr
91
103
}
92
104
93
- if iterableErr != nil && iterableErr .Validate (response , responseErr ) {
94
- if iterableErr .Message != nil {
95
- return nil , errs .NewWaitError (iterableErr .Message (response , responseErr ))
96
- }
97
-
98
- return nil , errs .NewWaitError ("an error occurred" )
105
+ if retryCount >= options .MaxRetries {
106
+ return nil , errs .NewWaitError (fmt .Sprintf ("The maximum number of retries exceeded. (%d/%d)" , retryCount , options .MaxRetries ))
99
107
}
100
108
101
- if timeout == nil {
102
- timeout = func () time. Duration {
103
- return 1 * time . Second
109
+ if options . IterableError != nil && options . IterableError . Validate ( response , responseErr ) {
110
+ if options . IterableError . Message != nil {
111
+ return nil , errs . NewWaitError ( options . IterableError . Message ( response , responseErr ))
104
112
}
113
+
114
+ return nil , errs .NewWaitError ("an error occurred" )
105
115
}
106
116
107
- time .Sleep (timeout ( ))
117
+ time .Sleep (options . Timeout ( retryCount ))
108
118
109
119
return executor (response , responseErr )
110
120
}
111
121
112
122
return executor (nil , nil )
113
123
}
124
+
125
+ // QueryParameterToString convert any query parameters to string.
126
+ func QueryParameterToString (obj any ) string {
127
+ return strings .ReplaceAll (url .QueryEscape (ParameterToString (obj )), "+" , "%20" )
128
+ }
129
+
130
+ // ParameterToString convert any parameters to string.
131
+ func ParameterToString (obj any ) string {
132
+ objKind := reflect .TypeOf (obj ).Kind ()
133
+ if objKind == reflect .Slice {
134
+ var result []string
135
+ sliceValue := reflect .ValueOf (obj )
136
+ for i := 0 ; i < sliceValue .Len (); i ++ {
137
+ element := sliceValue .Index (i ).Interface ()
138
+ result = append (result , ParameterToString (element ))
139
+ }
140
+ return strings .Join (result , "," )
141
+ }
142
+
143
+ if t , ok := obj .(time.Time ); ok {
144
+ return t .Format (time .RFC3339 )
145
+ }
146
+
147
+ if objKind == reflect .Struct {
148
+ if actualObj , ok := obj .(interface { GetActualInstance () any }); ok {
149
+ return ParameterToString (actualObj .GetActualInstance ())
150
+ }
151
+ }
152
+
153
+ return fmt .Sprintf ("%v" , obj )
154
+ }
0 commit comments