diff --git a/proxmox.go b/proxmox.go index b38fffb..e7a5770 100644 --- a/proxmox.go +++ b/proxmox.go @@ -280,6 +280,24 @@ func (c *Client) authHeaders(header *http.Header) { } } +func extractJSONNumber[T int64 | float64](raw map[string]interface{}, key string, assign func(T)) { + if num, ok := raw[key].(json.Number); ok { + var val any + var err error + + switch any(*new(T)).(type) { + case int64: + val, err = num.Int64() + case float64: + val, err = num.Float64() + } + + if err == nil { + assign(val.(T)) + } + } +} + func (c *Client) handleResponse(res *http.Response, v interface{}) error { if res.StatusCode == http.StatusInternalServerError || res.StatusCode == http.StatusNotImplemented { @@ -324,7 +342,102 @@ func (c *Client) handleResponse(res *http.Response, v interface{}) error { } if body, ok := datakey["data"]; ok { - return json.Unmarshal(body, &v) + switch typedV := v.(type) { + case *Storages: + var rawList []map[string]interface{} + + decoder := json.NewDecoder(bytes.NewReader(body)) + decoder.UseNumber() + if err := decoder.Decode(&rawList); err != nil { + return err + } + var result Storages + + for _, raw := range rawList { + s := &Storage{} + + if name, ok := raw["storage"].(string); ok { + s.Name = name + } + if content, ok := raw["content"].(string); ok { + s.Content = content + } + if typ, ok := raw["type"].(string); ok { + s.Type = typ + } + extractJSONNumber(raw, "enabled", func(v int64) { + s.Enabled = int(v) + }) + extractJSONNumber(raw, "active", func(v int64) { + s.Active = int(v) + }) + extractJSONNumber(raw, "shared", func(v int64) { + s.Shared = int(v) + }) + extractJSONNumber(raw, "used_fraction", func(v float64) { + s.UsedFraction = v + }) + + for key, dest := range map[string]*uint64{ + "avail": &s.Avail, + "used": &s.Used, + "total": &s.Total, + } { + extractJSONNumber(raw, key, func(v float64) { + *dest = uint64(v) + }) + } + + result = append(result, s) + } + + *typedV = result + return nil + case *Storage: + var raw map[string]interface{} + + decoder := json.NewDecoder(bytes.NewReader(body)) + decoder.UseNumber() + if err := decoder.Decode(&raw); err != nil { + return err + } + s := typedV + + if name, ok := raw["storage"].(string); ok { + s.Name = name + } + if content, ok := raw["content"].(string); ok { + s.Content = content + } + if typ, ok := raw["type"].(string); ok { + s.Type = typ + } + extractJSONNumber(raw, "enabled", func(v int64) { + s.Enabled = int(v) + }) + extractJSONNumber(raw, "active", func(v int64) { + s.Active = int(v) + }) + extractJSONNumber(raw, "shared", func(v int64) { + s.Shared = int(v) + }) + extractJSONNumber(raw, "used_fraction", func(v float64) { + s.UsedFraction = v + }) + + for key, dest := range map[string]*uint64{ + "avail": &s.Avail, + "used": &s.Used, + "total": &s.Total, + } { + extractJSONNumber(raw, key, func(v float64) { + *dest = uint64(v) + }) + } + return nil + default: + return json.Unmarshal(body, &v) + } } return json.Unmarshal(body, &v) // assume passed in type fully supports response diff --git a/proxmox_test.go b/proxmox_test.go index a1fcbfc..517916c 100644 --- a/proxmox_test.go +++ b/proxmox_test.go @@ -7,6 +7,8 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" + "github.com/luthermonson/go-proxmox/tests/mocks" "github.com/luthermonson/go-proxmox/tests/mocks/config" "github.com/stretchr/testify/assert" @@ -159,4 +161,62 @@ func TestClient_handleResponse(t *testing.T) { err = client.handleResponse(resp, &testData) assert.NotNil(t, err) assert.Equal(t, "bad request: - {\"test\":\"data\"}", err.Error()) + + // storages test with float total + storagesData := `{ + "data": [ + { + "storage": "local", + "enabled": 1, + "active": 1, + "total": 1.12589990684262e+15 + }, + { + "storage": "local2", + "enabled": 1, + "active": 1, + "total": 1.12589990684262e+15 + } + ] + }` + resp = &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(storagesData)), + } + + var testStorages Storages + err = client.handleResponse(resp, &testStorages) + require.NoError(t, err) + + require.Len(t, testStorages, 2) + storage := testStorages[0] + + assert.Equal(t, "local", storage.Name) + assert.Equal(t, 1, storage.Enabled) + assert.Equal(t, 1, storage.Active) + + expectedTotal := uint64(1125899906842620) + assert.Equal(t, expectedTotal, storage.Total) + + // storage test with float total + storageData := `{ + "data": { + "storage": "local3", + "enabled": 4, + "active": 5, + "total": 1.12589990684262e+15 + } + }` + resp = &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(storageData)), + } + var testStorage Storage + err = client.handleResponse(resp, &testStorage) + require.NoError(t, err) + + assert.Equal(t, "local3", testStorage.Name) + assert.Equal(t, 4, testStorage.Enabled) + assert.Equal(t, 5, testStorage.Active) + assert.Equal(t, expectedTotal, testStorage.Total) }