From e5b59e3c15c24ca7a06726c482a35eb9b7844242 Mon Sep 17 00:00:00 2001 From: Sebastian Widmer Date: Thu, 7 Dec 2023 11:21:10 +0100 Subject: [PATCH 1/2] Ensure client always sends empty array instead of JSON `null` --- pkg/odoo/odoo16.go | 14 ++++++++++++-- pkg/odoo/odoo16_test.go | 7 ++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pkg/odoo/odoo16.go b/pkg/odoo/odoo16.go index 29a8b81..3fd2a7f 100644 --- a/pkg/odoo/odoo16.go +++ b/pkg/odoo/odoo16.go @@ -21,7 +21,7 @@ type OdooAPIClient struct { } type apiObject struct { - Data []OdooMeteredBillingRecord `json:"data"` + Data ensureJSONArray[OdooMeteredBillingRecord] `json:"data"` } type OdooMeteredBillingRecord struct { @@ -72,7 +72,7 @@ func NewOdooAPIWithClient(odooURL string, client *http.Client, logger logr.Logge func (c OdooAPIClient) SendData(ctx context.Context, data []OdooMeteredBillingRecord) error { apiObject := apiObject{ - Data: data, + Data: ensureJSONArray[OdooMeteredBillingRecord](data), } str, err := json.Marshal(apiObject) if err != nil { @@ -92,3 +92,13 @@ func (c OdooAPIClient) SendData(ctx context.Context, data []OdooMeteredBillingRe return nil } + +// ensureJSONArray is a wrapper around any slice that will marshal to an empty array instead of `null` if the array is nil. +type ensureJSONArray[T any] []T + +func (a ensureJSONArray[T]) MarshalJSON() ([]byte, error) { + if a == nil { + return []byte("[]"), nil + } + return json.Marshal([]T(a)) +} diff --git a/pkg/odoo/odoo16_test.go b/pkg/odoo/odoo16_test.go index cdaf0d6..70c5753 100644 --- a/pkg/odoo/odoo16_test.go +++ b/pkg/odoo/odoo16_test.go @@ -44,10 +44,11 @@ func TestOdooRecordsSent(t *testing.T) { logger := logr.New(logr.Discard().GetSink()) uut := odoo.NewOdooAPIWithClient("https://foo.bar/odoo16/", &client, logger) - err := uut.SendData(context.Background(), []odoo.OdooMeteredBillingRecord{getOdooRecord()}) - - require.NoError(t, err) + require.NoError(t, uut.SendData(context.Background(), []odoo.OdooMeteredBillingRecord{getOdooRecord()})) require.Equal(t, `{"data":[{"product_id":"my-product","instance_id":"my-instance","item_description":"my-description","item_group_description":"my-group","sales_order_id":"SO00000","unit_id":"my-unit","consumed_units":11.1,"timerange":"2022-02-22T22:22:22Z/2022-02-22T23:22:22Z"}]}`, mrt.receivedContent) + + require.NoError(t, uut.SendData(context.Background(), nil)) + require.Equal(t, `{"data":[]}`, mrt.receivedContent, "client should ensure that the data field is always represented as an array") } func TestErrorHandling(t *testing.T) { From 60e63cb2cf7db74e019923e5b484244ab55c30c2 Mon Sep 17 00:00:00 2001 From: Sebastian Widmer Date: Thu, 7 Dec 2023 11:22:16 +0100 Subject: [PATCH 2/2] Initialize record array with assumed length More or less cosmetic change with slight performance improvements possible. --- pkg/report/report.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/report/report.go b/pkg/report/report.go index d3506bc..d6a980b 100644 --- a/pkg/report/report.go +++ b/pkg/report/report.go @@ -90,7 +90,7 @@ func runQuery(ctx context.Context, odooClient OdooClient, prom PromQuerier, args } var errs error - var records []odoo.OdooMeteredBillingRecord + records := make([]odoo.OdooMeteredBillingRecord, 0, len(samples)) for _, sample := range samples { record, err := processSample(ctx, odooClient, args, from, sample) if err != nil {