Skip to content

Commit 6311b61

Browse files
authored
Merge pull request #1 from sendinblue/issue/merge-upstream
ReadLast & Read events from one family
2 parents 864a024 + dfb3187 commit 6311b61

File tree

3 files changed

+282
-12
lines changed

3 files changed

+282
-12
lines changed

mapping/mapper_test.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package mapping
22

33
import (
4-
"fmt"
54
"log"
65
"testing"
76
"time"
@@ -32,10 +31,10 @@ func TestSeekRaw(t *testing.T) {
3231
t.Fatal("should NOT have found the column")
3332
}
3433
if col != "" {
35-
t.Fatal(fmt.Sprintf("column must be empty, got %s \n", col))
34+
t.Fatalf("column must be empty, got %s \n", col)
3635
}
3736
if val != "" {
38-
t.Fatal(fmt.Sprintf("value must be empty, got %s \n", val))
37+
t.Fatalf("value must be empty, got %s \n", val)
3938
}
4039
}
4140

@@ -69,10 +68,10 @@ func TestSeekMapped(t *testing.T) {
6968
t.Fatal("should NOT have found the column")
7069
}
7170
if col != "" {
72-
t.Fatal(fmt.Sprintf("column must be empty, got %s \n", col))
71+
t.Fatalf("column must be empty, got %s \n", col)
7372
}
7473
if val != "" {
75-
t.Fatal(fmt.Sprintf("value must be empty, got %s \n", val))
74+
t.Fatalf("value must be empty, got %s \n", val)
7675
}
7776
}
7877

@@ -128,10 +127,10 @@ func TestMapper(t *testing.T) {
128127
func compare(t *testing.T, m *Mapper, col string, val string, wantedCol string, wantedVal string) {
129128
fCol, fVal := getMappedData(m.Mapping, m.rules.toEvent, col, val)
130129
if fCol != wantedCol {
131-
t.Fatal(fmt.Sprintf("wrong column: wanted %s, got %s", wantedCol, fCol))
130+
t.Fatalf("wrong column: wanted %s, got %s", wantedCol, fCol)
132131
}
133132
if fVal != wantedVal {
134-
t.Fatal(fmt.Sprintf("wrong value: wanted %s, got %s", wantedVal, fVal))
133+
t.Fatalf("wrong value: wanted %s, got %s", wantedVal, fVal)
135134
}
136135
}
137136

@@ -228,10 +227,10 @@ func TestTurnToShortColumn(t *testing.T) {
228227
t.Fatal("should NOT have found the column")
229228
}
230229
if col != "" {
231-
t.Fatal(fmt.Sprintf("column must be empty, got %s \n", col))
230+
t.Fatalf("column must be empty, got %s \n", col)
232231
}
233232
if val != "" {
234-
t.Fatal(fmt.Sprintf("value must be empty, got %s \n", val))
233+
t.Fatalf("value must be empty, got %s \n", val)
235234
}
236235
}
237236

@@ -265,10 +264,10 @@ func TestTurnToMappedColumnValue(t *testing.T) {
265264
t.Fatal("should NOT have found the column")
266265
}
267266
if col != "" {
268-
t.Fatal(fmt.Sprintf("column must be empty, got %s \n", col))
267+
t.Fatalf("column must be empty, got %s \n", col)
269268
}
270269
if val != "" {
271-
t.Fatal(fmt.Sprintf("value must be empty, got %s \n", val))
270+
t.Fatalf("value must be empty, got %s \n", val)
272271
}
273272
}
274273

repository/repository.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,32 @@ This method takes a row key as an argument, uses its internal adapter to read th
5252
parses all cells contained in the row to turn it into a map of data.Event and finally returns the data.Set that contains all the events.
5353
*/
5454
func (r *Repository) Read(ctx context.Context, key string) (*data.Set, error) {
55-
row, err := r.adapter.ReadRow(ctx, key)
55+
return r.read(ctx, key)
56+
}
57+
58+
/*
59+
ReadFamily reads a row from the repository keeping only the desired column family and map it to a data.Set
60+
61+
This method takes a row key and the column family as an argument, uses its internal adapter to read the row from Big Table,
62+
parses all cells contained in the row to turn it into a map of data.Event and finally returns the data.Set that contains all the events.
63+
64+
Be careful, this method will perform an exact match on the column family name.
65+
*/
66+
func (r *Repository) ReadFamily(ctx context.Context, key string, family string) (*data.Set, error) {
67+
familyFilter := bigtable.RowFilter(bigtable.FamilyFilter(family))
68+
return r.read(ctx, key, familyFilter)
69+
}
70+
71+
// ReadLast reads a row from the repository while returning only the latest cell values after
72+
// mapping it to a data.Set. This method takes a row key as an argument, uses its internal adapter
73+
// to read the row from Big Table, parses only the latest cells contained in the row to turn it into
74+
// a map of data.Event and finally returns the data.Set that contains all the events.
75+
func (r *Repository) ReadLast(ctx context.Context, key string) (*data.Set, error) {
76+
return r.read(ctx, key, bigtable.RowFilter(bigtable.LatestNFilter(1)))
77+
}
78+
79+
func (r *Repository) read(ctx context.Context, key string, opts ...bigtable.ReadOption) (*data.Set, error) {
80+
row, err := r.adapter.ReadRow(ctx, key, opts...)
5681
if err != nil {
5782
return nil, err
5883
}

repository/repository_test.go

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,143 @@ func ExampleRepository_Search() {
257257
// Computer
258258
}
259259

260+
func ExampleRepository_ReadLast() {
261+
ctx := context.Background()
262+
client := getBigTableClient(ctx)
263+
c, err := fs.ReadFile("testdata/mapping.json")
264+
if err != nil {
265+
log.Fatalln(err)
266+
}
267+
jsonMapping, err := mapping.LoadMapping(c)
268+
if err != nil {
269+
log.Fatalln(err)
270+
}
271+
mapper := mapping.NewMapper(jsonMapping)
272+
tbl := client.Open(table)
273+
274+
repo := NewRepository(tbl, mapper)
275+
eventSet := &data.Set{Events: map[string][]*data.Event{
276+
"front": {
277+
{
278+
RowKey: "contactz-102",
279+
Date: time.Date(2018, time.January, 1, 0, 2, 0, 0, time.UTC),
280+
Cells: map[string]string{
281+
"event_type": "add_to_cart",
282+
"device_type": "Computer",
283+
"url": "https://example.org/some/product",
284+
},
285+
},
286+
},
287+
}}
288+
289+
// insert
290+
errs, err := repo.Write(ctx, eventSet)
291+
if err != nil {
292+
log.Fatalln(err)
293+
}
294+
if len(errs) > 0 {
295+
log.Fatalln(errs)
296+
}
297+
298+
// update
299+
eventSet = &data.Set{Events: map[string][]*data.Event{
300+
"front": {
301+
{
302+
RowKey: "contactz-102",
303+
Date: time.Now(),
304+
Cells: map[string]string{
305+
"event_type": "purchase",
306+
"device_type": "Smartphone",
307+
"url": "https://example.org/some/cart",
308+
},
309+
},
310+
},
311+
}}
312+
errs, err = repo.Write(ctx, eventSet)
313+
if err != nil {
314+
log.Fatalln(err)
315+
}
316+
if len(errs) > 0 {
317+
log.Fatalln(errs)
318+
}
319+
320+
readSet, err := repo.ReadLast(ctx, "contactz-102")
321+
if err != nil {
322+
log.Fatalln(err)
323+
}
324+
for _, event := range readSet.Events["front"] {
325+
fmt.Println(event.Cells["event_type"])
326+
fmt.Println(event.Cells["device_type"])
327+
}
328+
// Output:
329+
// add_to_cart
330+
//
331+
// purchase
332+
// Smartphone
333+
}
334+
335+
func ExampleRepository_ReadFamily() {
336+
ctx := context.Background()
337+
client := getBigTableClient(ctx)
338+
c, err := fs.ReadFile("testdata/mapping.json")
339+
if err != nil {
340+
log.Fatalln(err)
341+
}
342+
jsonMapping, err := mapping.LoadMapping(c)
343+
if err != nil {
344+
log.Fatalln(err)
345+
}
346+
mapper := mapping.NewMapper(jsonMapping)
347+
tbl := client.Open(table)
348+
349+
repo := NewRepository(tbl, mapper)
350+
eventSet := &data.Set{Events: map[string][]*data.Event{
351+
"front": {
352+
{
353+
RowKey: "contactz-102",
354+
Date: time.Date(2018, time.January, 1, 0, 2, 0, 0, time.UTC),
355+
Cells: map[string]string{
356+
"event_type": "add_to_cart",
357+
"device_type": "Computer",
358+
"url": "https://example.org/some/product",
359+
},
360+
},
361+
},
362+
"blog": {
363+
{
364+
RowKey: "contactz-102",
365+
Date: time.Date(2018, time.January, 1, 0, 2, 0, 0, time.UTC),
366+
Cells: map[string]string{
367+
"event_type": "page_view",
368+
"device_type": "Computer",
369+
"url": "https://example.org/blog/article/1",
370+
},
371+
},
372+
},
373+
}}
374+
375+
// insert
376+
errs, err := repo.Write(ctx, eventSet)
377+
if err != nil {
378+
log.Fatalln(err)
379+
}
380+
if len(errs) > 0 {
381+
log.Fatalln(errs)
382+
}
383+
384+
readSet, err := repo.ReadFamily(ctx, "contactz-102", "blog")
385+
if err != nil {
386+
log.Fatalln(err)
387+
}
388+
for _, event := range readSet.Events["blog"] {
389+
fmt.Println(event.Cells["event_type"])
390+
fmt.Println(event.Cells["device_type"])
391+
}
392+
// Output:
393+
// page_view
394+
// Computer
395+
}
396+
260397
var t1 = bigtable.Time(time.Date(2020, time.January, 1, 0, 1, 0, 0, time.UTC))
261398
var t2 = bigtable.Time(time.Date(2020, time.January, 1, 0, 2, 0, 0, time.UTC))
262399
var t3 = bigtable.Time(time.Date(2020, time.January, 1, 0, 3, 0, 0, time.UTC))
@@ -360,6 +497,111 @@ func TestRepository_Search(t *testing.T) {
360497

361498
}
362499

500+
func TestRepository_ReadLast(t *testing.T) {
501+
ctx := context.Background()
502+
repository := &Repository{
503+
adapter: mockAdapter{},
504+
mapper: getMockMapper(t),
505+
}
506+
eventSet, err := repository.ReadLast(ctx, "contact-3")
507+
if err != nil {
508+
t.Fatal(err)
509+
}
510+
if err != nil {
511+
t.Fatalf("failed to read: %v", err)
512+
}
513+
if len(eventSet.Events) != 1 {
514+
t.Fatalf("expected 1 event family, got %d", len(eventSet.Events))
515+
}
516+
if v, ok := eventSet.Events["front"]; !ok {
517+
t.Fatalf("expected front family, got %v", v)
518+
} else {
519+
if len(v) != 3 {
520+
t.Fatalf("expected 3 events, got %d", len(v))
521+
}
522+
523+
if v[0].RowKey != "contact-3" {
524+
t.Fatalf("expected contact-3, got %s", v[0].RowKey)
525+
}
526+
if v[0].Cells["url"] != "http://someexample.url/query/string/1" {
527+
t.Fatalf("expected http://someexample.url/query/string/1, got %s", v[0].Cells["url"])
528+
}
529+
if v[0].Cells["device_type"] != "Smartphone" {
530+
t.Fatalf("expected Smartphone, got %s", v[0].Cells["device_type"])
531+
}
532+
// here we're testing each event_type depending on the timestamp.
533+
// It's because Go doesn't guarantee the order of the map iteration
534+
for _, event := range v {
535+
if event.Date.Unix() == t1.Time().Unix() {
536+
if event.Cells["event_type"] != "page_view" {
537+
t.Fatalf("expected page_view, got %s", event.Cells["event_type"])
538+
}
539+
}
540+
if event.Date.Unix() == t2.Time().Unix() {
541+
if event.Cells["event_type"] != "add_to_cart" {
542+
t.Fatalf("expected add_to_cart, got %s", event.Cells["event_type"])
543+
}
544+
}
545+
if event.Date.Unix() == t3.Time().Unix() {
546+
if event.Cells["event_type"] != "purchase" {
547+
t.Fatalf("expected purchase, got %s", event.Cells["event_type"])
548+
}
549+
}
550+
}
551+
}
552+
}
553+
554+
func TestRepository_ReadFamily(t *testing.T) {
555+
ctx := context.Background()
556+
repository := &Repository{
557+
adapter: mockAdapter{},
558+
mapper: getMockMapper(t),
559+
}
560+
eventSet, err := repository.ReadFamily(ctx, "contact-3", "front")
561+
if err != nil {
562+
t.Fatalf("failed to read: %v", err)
563+
}
564+
if len(eventSet.Events) != 1 {
565+
t.Fatalf("expected 1 event family, got %d", len(eventSet.Events))
566+
}
567+
if v, ok := eventSet.Events["front"]; !ok {
568+
t.Fatalf("expected front family, got %v", v)
569+
} else {
570+
if len(v) != 3 {
571+
t.Fatalf("expected 3 events, got %d", len(v))
572+
}
573+
574+
if v[0].RowKey != "contact-3" {
575+
t.Fatalf("expected contact-3, got %s", v[0].RowKey)
576+
}
577+
if v[0].Cells["url"] != "http://someexample.url/query/string/1" {
578+
t.Fatalf("expected http://someexample.url/query/string/1, got %s", v[0].Cells["url"])
579+
}
580+
if v[0].Cells["device_type"] != "Smartphone" {
581+
t.Fatalf("expected Smartphone, got %s", v[0].Cells["device_type"])
582+
}
583+
// here we're testing each event_type depending on the timestamp.
584+
// It's because Go doesn't guarantee the order of the map iteration
585+
for _, event := range v {
586+
if event.Date.Unix() == t1.Time().Unix() {
587+
if event.Cells["event_type"] != "page_view" {
588+
t.Fatalf("expected page_view, got %s", event.Cells["event_type"])
589+
}
590+
}
591+
if event.Date.Unix() == t2.Time().Unix() {
592+
if event.Cells["event_type"] != "add_to_cart" {
593+
t.Fatalf("expected add_to_cart, got %s", event.Cells["event_type"])
594+
}
595+
}
596+
if event.Date.Unix() == t3.Time().Unix() {
597+
if event.Cells["event_type"] != "purchase" {
598+
t.Fatalf("expected purchase, got %s", event.Cells["event_type"])
599+
}
600+
}
601+
}
602+
}
603+
}
604+
363605
//go:embed testdata/mapping.json
364606
var fs embed.FS
365607

@@ -497,6 +739,10 @@ func getBigTableClient(ctx context.Context) *bigtable.Client {
497739
log.Fatalln(err)
498740
}
499741

742+
if err = adminClient.CreateColumnFamily(ctx, table, "blog"); err != nil {
743+
log.Fatalln(err)
744+
}
745+
500746
client, err := bigtable.NewClient(ctx, projectID, instance, option.WithGRPCConn(conn))
501747
if err != nil {
502748
log.Fatalln(err)

0 commit comments

Comments
 (0)