Alexey Kim 3 mesi fa
parent
commit
f300c376ca

+ 2 - 0
go.mod

@@ -9,6 +9,7 @@ require (
 	github.com/kelindar/bitmap v1.5.2
 	github.com/prometheus/client_golang v1.18.0
 	github.com/rs/zerolog v1.32.0
+	github.com/samber/lo v1.39.0
 	github.com/urfave/cli/v2 v2.27.1
 	gopkg.in/yaml.v3 v3.0.1
 )
@@ -39,6 +40,7 @@ require (
 	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 	go.opentelemetry.io/otel v1.22.0 // indirect
 	go.opentelemetry.io/otel/trace v1.22.0 // indirect
+	golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect
 	golang.org/x/sys v0.16.0 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect
 	google.golang.org/grpc v1.60.1 // indirect

+ 2 - 0
go.sum

@@ -134,6 +134,8 @@ github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
 github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
+github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
 github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
 github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
 github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=

+ 79 - 0
graphql/connection/connection.go

@@ -0,0 +1,79 @@
+package connection
+
+import (
+	"errors"
+	"fmt"
+	"github.com/samber/lo"
+	"math"
+)
+
+type Connection[T Cursor] struct {
+	Edges    []T       `json:"edges"`
+	PageInfo *PageInfo `json:"page_info"`
+}
+
+func (c *Connection[T]) Reverse() {
+	c.Edges = lo.Reverse[T](c.Edges)
+}
+
+func (c *Connection[T]) Grain(after, before *string) error {
+	if after != nil && before != nil {
+		return errors.New("illegal arguments: only one of `after` or `before` can be used at a time")
+	}
+
+	if len(c.Edges) == 0 {
+		c.PageInfo = new(PageInfo)
+		return nil
+	}
+
+	edges := lo.FlatMap[T, string](c.Edges, func(item T, _ int) []string {
+		return []string{item.Cursor()}
+	})
+
+	c.PageInfo = &PageInfo{
+		StartCursor: lo.ToPtr(edges[0]),
+		EndCursor:   lo.ToPtr(edges[(len(edges) - 1)]),
+	}
+
+	if after != nil {
+		if idx := lo.IndexOf(edges, *after); idx != -1 {
+			c.Edges = lo.Slice[T](c.Edges, idx+1, math.MaxUint32)
+		}
+	}
+
+	if before != nil {
+		if idx := lo.IndexOf(edges, *before); idx != -1 {
+			c.Edges = lo.Subset[T](c.Edges, -idx, math.MaxUint32)
+		}
+	}
+
+	return nil
+}
+
+func (c *Connection[T]) Slice(first, last *int) error {
+	if first != nil && last != nil {
+		return fmt.Errorf("illegal agruments: only one of `first` or `last` can be used at a time")
+	}
+
+	if first != nil {
+		c.Edges = lo.Slice[T](c.Edges, 0, *first)
+	}
+
+	if last != nil {
+		c.Edges = lo.Subset[T](c.Edges, -*last, math.MaxUint32)
+	}
+
+	if len(c.Edges) < 1 {
+		c.PageInfo.HasPreviousPage = c.PageInfo.StartCursor != nil
+		c.PageInfo.HasNextPage = c.PageInfo.EndCursor != nil
+		c.PageInfo.StartCursor = nil
+		c.PageInfo.EndCursor = nil
+	} else {
+		c.PageInfo.HasPreviousPage = !(*c.PageInfo.StartCursor == c.Edges[0].Cursor())
+		c.PageInfo.HasNextPage = !(*c.PageInfo.EndCursor == c.Edges[len(c.Edges)-1].Cursor())
+		c.PageInfo.StartCursor = lo.ToPtr(c.Edges[0].Cursor())
+		c.PageInfo.EndCursor = lo.ToPtr(c.Edges[len(c.Edges)-1].Cursor())
+	}
+
+	return nil
+}

+ 8 - 0
graphql/connection/cursor.go

@@ -0,0 +1,8 @@
+package connection
+
+type Cursor interface {
+	NS() Namespace
+	Id() string
+
+	Cursor() string
+}

+ 3 - 0
graphql/connection/namespace.go

@@ -0,0 +1,3 @@
+package connection
+
+type Namespace string

+ 8 - 0
graphql/connection/page_info.go

@@ -0,0 +1,8 @@
+package connection
+
+type PageInfo struct {
+	StartCursor     *string `json:"start_cursor,omitempty"`
+	EndCursor       *string `json:"end_cursor,omitempty"`
+	HasNextPage     bool    `json:"has_next_page"`
+	HasPreviousPage bool    `json:"has_previous_page"`
+}