Skip to content

Commit 0a8989f

Browse files
Improvements (#33)
* fix: preserving type on marshalling/unmarshalling - test improvements - improved logging on diff * feat: add pending state management
1 parent 253952f commit 0a8989f

File tree

9 files changed

+504
-127
lines changed

9 files changed

+504
-127
lines changed

internal/client/restclient.go

Lines changed: 81 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,21 @@ import (
1111
"os"
1212
"strings"
1313

14+
rawyaml "gopkg.in/yaml.v3"
15+
1416
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1517
)
1618

17-
func (u *UnstructuredClient) Call(ctx context.Context, cli *http.Client, path string, opts *RequestConfiguration) (any, error) {
19+
type Response struct {
20+
ResponseBody any
21+
statusCode int
22+
}
23+
24+
func (r *Response) IsPending() bool {
25+
return r.statusCode == http.StatusProcessing || r.statusCode == http.StatusContinue || r.statusCode == http.StatusAccepted
26+
}
27+
28+
func (u *UnstructuredClient) Call(ctx context.Context, cli *http.Client, path string, opts *RequestConfiguration) (*Response, error) {
1829
uri := buildPath(u.Server, path, opts.Parameters, opts.Query)
1930
pathItem, ok := u.DocScheme.Model.Paths.PathItems.Get(path)
2031
if !ok {
@@ -129,58 +140,67 @@ func (u *UnstructuredClient) Call(ctx context.Context, cli *http.Client, path st
129140
return nil, fmt.Errorf("error handling response: %w", err)
130141
}
131142

132-
val, ok := response.(map[string]interface{})
133-
if !ok {
134-
return nil, fmt.Errorf("unexpected response type: %T", response)
135-
}
136-
return &val, nil
143+
return &Response{
144+
ResponseBody: response,
145+
statusCode: resp.StatusCode,
146+
}, nil
137147
}
138148

139-
func (u *UnstructuredClient) FindBy(ctx context.Context, cli *http.Client, path string, opts *RequestConfiguration) (any, error) {
140-
list, err := u.Call(ctx, cli, path, opts)
149+
// It support both list and single item responses
150+
func (u *UnstructuredClient) FindBy(ctx context.Context, cli *http.Client, path string, opts *RequestConfiguration) (*Response, error) {
151+
response, err := u.Call(ctx, cli, path, opts)
141152
if err != nil {
142153
return nil, err
143154
}
144-
if list == nil {
155+
if response == nil {
145156
return nil, nil
146157
}
147158

148-
var li map[string]interface{}
159+
list := response.ResponseBody
149160

150-
if _, ok := list.([]interface{}); !ok {
161+
var li map[string]interface{}
162+
if _, ok := list.([]interface{}); ok {
151163
li = map[string]interface{}{
152164
"items": list,
153165
}
154-
}
155-
156-
if _, ok := list.(map[string]interface{}); !ok {
157-
return nil, fmt.Errorf("unexpected response type: %T", list)
166+
} else {
167+
li, ok = list.(map[string]interface{})
168+
if !ok {
169+
return nil, fmt.Errorf("unexpected response type: %T", list)
170+
}
158171
}
159172

160173
for _, v := range li {
161-
if v, ok := v.([]interface{}); ok {
162-
if len(v) > 0 {
163-
for _, item := range v {
164-
if item, ok := item.(map[string]interface{}); ok {
165-
for _, ide := range u.IdentifierFields {
166-
idepath := strings.Split(ide, ".") // split the identifier field by '.'
167-
responseValue, _, err := unstructured.NestedString(item, idepath...)
168-
if err != nil {
169-
val, _, err := unstructured.NestedFieldCopy(item, idepath...)
170-
if err != nil {
171-
return nil, fmt.Errorf("error getting nested field: %w", err)
172-
}
173-
responseValue = fmt.Sprintf("%v", val)
174-
}
175-
ok, err = u.isInSpecFields(ide, responseValue)
174+
if vli, ok := v.([]interface{}); ok {
175+
if len(vli) > 0 {
176+
for _, item := range vli {
177+
itMap, ok := item.(map[string]interface{})
178+
if !ok {
179+
continue // skip this item if it's not a map
180+
}
181+
182+
for _, ide := range u.IdentifierFields {
183+
idepath := strings.Split(ide, ".") // split the identifier field by '.'
184+
responseValue, _, err := unstructured.NestedString(itMap, idepath...)
185+
if err != nil {
186+
val, _, err := unstructured.NestedFieldNoCopy(itMap, idepath...)
176187
if err != nil {
177-
return nil, err
178-
}
179-
if ok {
180-
return &item, nil
188+
return nil, fmt.Errorf("error getting nested field: %w", err)
181189
}
190+
responseValue = fmt.Sprintf("%v", val)
191+
}
192+
ok, err = u.isInSpecFields(ide, responseValue)
193+
if err != nil {
194+
return nil, err
195+
}
196+
if ok {
197+
return &Response{
198+
ResponseBody: itMap,
199+
statusCode: response.statusCode,
200+
}, nil
182201
}
183202
}
203+
184204
}
185205
}
186206
break
@@ -192,7 +212,22 @@ func (u *UnstructuredClient) FindBy(ctx context.Context, cli *http.Client, path
192212
}
193213
}
194214

195-
// buildPath constructs the URL path with the given parameters and query.
215+
func jsonToYAML(jsonData []byte) ([]byte, error) {
216+
// First unmarshal JSON into a generic interface
217+
var obj interface{}
218+
if err := json.Unmarshal(jsonData, &obj); err != nil {
219+
return nil, err
220+
}
221+
222+
// Then marshal to YAML
223+
yamlData, err := rawyaml.Marshal(obj)
224+
if err != nil {
225+
return nil, err
226+
}
227+
228+
return yamlData, nil
229+
}
230+
196231
// response should be a pointer to the object where the response will be unmarshalled.
197232
func handleResponse(rc io.ReadCloser, response any) error {
198233
if rc == nil {
@@ -206,7 +241,17 @@ func handleResponse(rc io.ReadCloser, response any) error {
206241
if len(data) == 0 {
207242
return nil
208243
}
209-
return json.Unmarshal(data, &response)
244+
245+
yamlData, err := jsonToYAML(data)
246+
if err != nil {
247+
return fmt.Errorf("error converting JSON to YAML: %w", err)
248+
}
249+
250+
err = rawyaml.Unmarshal(yamlData, response)
251+
if err != nil {
252+
return fmt.Errorf("error unmarshalling YAML response: %w", err)
253+
}
254+
return nil
210255
}
211256

212257
type debuggingRoundTripper struct {

0 commit comments

Comments
 (0)