Skip to content

Commit d518bc0

Browse files
feat - extend methods on response.Data
1 parent 07ac163 commit d518bc0

4 files changed

Lines changed: 110 additions & 7 deletions

File tree

data.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package tinybird
2+
3+
import "encoding/json"
4+
5+
// Row is a generic map-based structure that can hold fields of any type.
6+
// Each key corresponds to a field name, and each value can be any Go type.
7+
type Row map[string]any
8+
9+
// Data represents a collection (slice) of rows returned from a query or dataset.
10+
type Data []Row
11+
12+
// First returns the first Row in the Data slice.
13+
// It assumes the slice is not empty.
14+
func (d Data) First() (out Row) {
15+
out = d[0]
16+
17+
return out
18+
}
19+
20+
// FetchOne retrieves the value of a specific field from the first Row
21+
// in the Data slice, using the provided field name (key).
22+
// If the key doesn't exist, it returns nil.
23+
func (d Data) FetchOne(in string) (out any) {
24+
row := d.First()
25+
out = row[in]
26+
27+
return out
28+
}
29+
30+
// ToString converts the entire Data slice into a JSON-formatted string.
31+
// If marshalling fails, it returns an empty string.
32+
func (d Data) ToString() (out string) {
33+
tmp, _ := json.Marshal(d)
34+
35+
return string(tmp)
36+
}

data_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package tinybird
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestFirst_ReturnsFirstRow(t *testing.T) {
10+
d := Data{
11+
{"id": 1, "name": "first"},
12+
{"id": 2, "name": "second"},
13+
}
14+
15+
first := d.First()
16+
assert.Equal(t, Row{"id": 1, "name": "first"}, first)
17+
}
18+
19+
func TestFirst_PanicsOnEmptyData(t *testing.T) {
20+
var d Data
21+
assert.Panics(t, func() { _ = d.First() })
22+
23+
empty := Data{}
24+
assert.Panics(t, func() { _ = empty.First() })
25+
}
26+
27+
func TestFetchOne_ReturnsValueFromFirstRow(t *testing.T) {
28+
d := Data{
29+
{"foo": "bar", "n": 42},
30+
{"foo": "baz"},
31+
}
32+
33+
val := d.FetchOne("foo")
34+
assert.Equal(t, "bar", val)
35+
36+
valNum := d.FetchOne("n")
37+
assert.Equal(t, 42, valNum)
38+
}
39+
40+
func TestFetchOne_ReturnsNilWhenKeyMissing(t *testing.T) {
41+
d := Data{
42+
{"foo": "bar"},
43+
}
44+
45+
val := d.FetchOne("missing")
46+
assert.Nil(t, val)
47+
}
48+
49+
func TestFetchOne_PanicsOnEmptyData(t *testing.T) {
50+
var d Data
51+
assert.Panics(t, func() { _ = d.FetchOne("foo") })
52+
}
53+
54+
func TestToString_JSONRoundTripEq(t *testing.T) {
55+
d := Data{
56+
{"a": 1, "b": "x"},
57+
{"a": 2, "c": true},
58+
}
59+
60+
expected := `[{"a":1,"b":"x"},{"a":2,"c":true}]`
61+
assert.JSONEq(t, expected, d.ToString())
62+
}
63+
64+
func TestToString_OnNilAndEmpty(t *testing.T) {
65+
var dNil Data
66+
dEmpty := Data{}
67+
68+
assert.Equal(t, "null", dNil.ToString())
69+
assert.Equal(t, "[]", dEmpty.ToString())
70+
}

request_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func TestRequest(t *testing.T) {
3636
assert.Nil(t, req.Error)
3737
assert.Equal(t, res.Status, http.StatusOK)
3838
assert.Equal(t, res.Rows, uint(1))
39-
assert.Equal(t, res.Data, []tinybird.Row{{"Col1": "1", "Col2": float64(2)}})
39+
assert.Equal(t, res.Data, tinybird.Data{{"Col1": "1", "Col2": float64(2)}})
4040
}
4141

4242
func TestRequestWithCustomURL(t *testing.T) {
@@ -66,7 +66,7 @@ func TestRequestWithCustomURL(t *testing.T) {
6666
assert.Nil(t, req.Error)
6767
assert.Equal(t, res.Status, http.StatusOK)
6868
assert.Equal(t, res.Rows, uint(1))
69-
assert.Equal(t, res.Data, []tinybird.Row{{"Col1": "1", "Col2": float64(2)}})
69+
assert.Equal(t, res.Data, tinybird.Data{{"Col1": "1", "Col2": float64(2)}})
7070
}
7171

7272
func TestRequestWithRequestParamInspect(t *testing.T) {
@@ -204,5 +204,5 @@ func TestRequestEvent(t *testing.T) {
204204
assert.Nil(t, req.Error)
205205
assert.Equal(t, res.Status, http.StatusOK)
206206
assert.Equal(t, res.Rows, uint(0))
207-
assert.Equal(t, res.Data, []tinybird.Row(nil))
207+
assert.Equal(t, res.Data, tinybird.Data(nil))
208208
}

response.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Response struct {
1414
Cached bool // Flag control to use to determine the origin this response.
1515
Raw io.ReadCloser // Raw is the original data from response.
1616
Body string // Body in string format, same a Data but in struct format and Raw in original format.
17-
Data []Row `json:"data,omitempty"` // Data is part a tinybird response.
17+
Data Data `json:"data,omitempty"` // Data is part a tinybird response.
1818
Documentation string `json:"documentation,omitempty"` // Documentation is part a tinybird response.
1919
Error string `json:"error,omitempty"` // Error is part a tinybird response.
2020
Meta []Meta `json:"meta,omitempty"` // Meta is part a tinybird response.
@@ -28,9 +28,6 @@ type Response struct {
2828
QuarantinedRows int `json:"quarantined_rows"`
2929
}
3030

31-
// Generic row structure to allow any field with any type.
32-
type Row map[string]any
33-
3431
// Specific field with data type.
3532
type Meta struct {
3633
Name string `json:"name,omitempty"`

0 commit comments

Comments
 (0)