Skip to content

Commit

Permalink
Merge pull request #430 from Ramky-Infoblox/pages-cherrypick
Browse files Browse the repository at this point in the history
Cherry pick fix for Revised Data Paging/Totals #426 & #427
  • Loading branch information
davidhankins authored Jan 7, 2025
2 parents 140df20 + 78aa8ab commit 4c0b55b
Show file tree
Hide file tree
Showing 21 changed files with 232 additions and 158 deletions.
2 changes: 1 addition & 1 deletion gateway/field_presence.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ type presenceInterceptorOptionsDecorator struct {

type presenceInterceptorOption func(*presenceInterceptorOptionsDecorator)

//WithOverrideFieldMask represent an option to override field mask generated by grpc-gateway
// WithOverrideFieldMask represent an option to override field mask generated by grpc-gateway
func WithOverrideFieldMask(d *presenceInterceptorOptionsDecorator) {
d.overrideFieldMask = true
}
Expand Down
8 changes: 4 additions & 4 deletions gateway/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
"github.com/infobloxopen/atlas-app-toolkit/query"
)

//retainFields function extracts the configuration for fields that
//need to be ratained either from gRPC response or from original testRequest
//(in case when gRPC side didn't set any preferences) and retains only
//this fields on outgoing response (dynmap).
// retainFields function extracts the configuration for fields that
// need to be ratained either from gRPC response or from original testRequest
// (in case when gRPC side didn't set any preferences) and retains only
// this fields on outgoing response (dynmap).
func retainFields(ctx context.Context, req *http.Request, dynmap map[string]interface{}) {
fieldsStr := ""
if req != nil {
Expand Down
3 changes: 2 additions & 1 deletion gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ func ClientUnaryInterceptor(parentCtx context.Context, method string, req, reply
l := vals.Get(limitQueryKey)
o := vals.Get(offsetQueryKey)
pt := vals.Get(pageTokenQueryKey)
t := vals.Get(isTotalSizeNeededQueryKey)

p, err = query.ParsePagination(l, o, pt)
p, err = query.ParsePagination(l, o, pt, t)
if err != nil {
return status.Error(codes.InvalidArgument, err.Error())
}
Expand Down
26 changes: 16 additions & 10 deletions gateway/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ import (
)

const (
filterQueryKey = "_filter"
sortQueryKey = "_order_by"
fieldsQueryKey = "_fields"
limitQueryKey = "_limit"
offsetQueryKey = "_offset"
pageTokenQueryKey = "_page_token"
searchQueryKey = "_fts"
pageInfoSizeMetaKey = "status-page-info-size"
pageInfoOffsetMetaKey = "status-page-info-offset"
pageInfoPageTokenMetaKey = "status-page-info-page_token"
filterQueryKey = "_filter"
sortQueryKey = "_order_by"
fieldsQueryKey = "_fields"
limitQueryKey = "_limit"
offsetQueryKey = "_offset"
pageTokenQueryKey = "_page_token"
searchQueryKey = "_fts"
isTotalSizeNeededQueryKey = "_is_total_size_needed"
pageInfoSizeMetaKey = "status-page-info-size"
pageInfoOffsetMetaKey = "status-page-info-offset"
pageInfoPageTokenMetaKey = "status-page-info-page_token"
pageInfoPageTotalSizeMetaKey = "status-page-info-total_size"

query_url = "query_url"
)
Expand Down Expand Up @@ -55,5 +57,9 @@ func SetPageInfo(ctx context.Context, p *query.PageInfo) error {
m[pageInfoSizeMetaKey] = strconv.FormatUint(uint64(s), 10)
}

if t := p.GetTotalSize(); t != 0 {
m[pageInfoPageTotalSizeMetaKey] = strconv.FormatUint(uint64(t), 10)
}

return grpc.SetHeader(ctx, metadata.New(m))
}
2 changes: 1 addition & 1 deletion gorm/filtering.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func FilterStringToGorm(ctx context.Context, filter string, obj interface{}, pb
return FilteringToGormEx(ctx, f, obj, c)
}

//Deprecated: Use FilteringToGormEx instead
// Deprecated: Use FilteringToGormEx instead
// FilteringToGorm returns GORM Plain SQL representation of the filtering expression.
func FilteringToGorm(ctx context.Context, m *query.Filtering, obj interface{}, pb proto.Message) (string, []interface{}, map[string]struct{}, error) {
c := &DefaultFilteringConditionConverter{&DefaultFilteringConditionProcessor{pb}}
Expand Down
4 changes: 2 additions & 2 deletions gorm/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func HandleFieldPath(ctx context.Context, fieldPath []string, obj interface{}) (
return dbPath, "", nil
}

//HandleJSONFiledPath translate field path to JSONB path for postgres jsonb
// HandleJSONFiledPath translate field path to JSONB path for postgres jsonb
func HandleJSONFieldPath(ctx context.Context, fieldPath []string, obj interface{}, values ...string) (string, string, error) {
operator := "#>>"
if isRawJSON(values...) {
Expand Down Expand Up @@ -82,7 +82,7 @@ func isRawJSON(values ...string) bool {
return true
}

//TODO: add supprt for embeded objects
// TODO: add supprt for embeded objects
func IsJSONCondition(ctx context.Context, fieldPath []string, obj interface{}) bool {
fieldName := generator.CamelCase(fieldPath[0])
objType := indirectType(reflect.TypeOf(obj))
Expand Down
170 changes: 96 additions & 74 deletions query/collection_operators.pb.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions query/collection_operators.proto
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ message Pagination {
// The service may impose maximum value.
// If omitted the service may impose a default value.
int32 limit = 3;
// The bool value enables/disables the record count.
// The default setting disables the record count.
bool is_total_size_needed = 4;
}

// PageInfo represents both server-driven and client-driven pagination response.
Expand All @@ -220,6 +223,8 @@ message PageInfo {
// The service may optionally include the offset of the next page of resources.
// A null value indicates no more pages.
int32 offset = 3;
// total_size indicates the total records present.
int64 total_size = 4;
}

// Searching represents search by.
Expand Down
20 changes: 10 additions & 10 deletions query/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const (
opCommonInnerDelimiter = "."
)

//FieldSelectionMap is a convenience type that represents map[string]*Field
//used in FieldSelection and Field structs
// FieldSelectionMap is a convenience type that represents map[string]*Field
// used in FieldSelection and Field structs
type FieldSelectionMap map[string]*Field

func innerDelimiter(delimiter ...string) string {
Expand All @@ -26,9 +26,9 @@ func toParts(input string, delimiter ...string) []string {
return strings.Split(input, split)
}

//ParseFieldSelection transforms a string with comma-separated fields that comes
//from client to FieldSelection struct. For complex fields dot is used as a delimeter by
//default, but it is also possible to specify a different delimiter.
// ParseFieldSelection transforms a string with comma-separated fields that comes
// from client to FieldSelection struct. For complex fields dot is used as a delimeter by
// default, but it is also possible to specify a different delimiter.
func ParseFieldSelection(input string, delimiter ...string) *FieldSelection {
if len(input) == 0 {
return nil
Expand All @@ -44,8 +44,8 @@ func ParseFieldSelection(input string, delimiter ...string) *FieldSelection {
return result
}

//GoString converts FieldSelection to a string representation
//It implements fmt.GoStringer interface and returns dot-notated fields separated by commas
// GoString converts FieldSelection to a string representation
// It implements fmt.GoStringer interface and returns dot-notated fields separated by commas
func (f *FieldSelection) GoString() string {
return strings.Join(f.AllFieldStrings(), opCommonDelimiter)
}
Expand All @@ -70,7 +70,7 @@ func addChildFieldString(result *[]string, parent string, field *Field) {
}
}

//Add allows to add new fields to FieldSelection
// Add allows to add new fields to FieldSelection
func (f *FieldSelection) Add(field string, delimiter ...string) {
if len(field) == 0 {
return
Expand All @@ -97,7 +97,7 @@ func (f *FieldSelection) Add(field string, delimiter ...string) {
}
}

//Delete allows to remove fields from FieldSelection
// Delete allows to remove fields from FieldSelection
func (f *FieldSelection) Delete(field string, delimiter ...string) bool {
if len(field) == 0 || f.Fields == nil {
return false
Expand All @@ -122,7 +122,7 @@ func (f *FieldSelection) Delete(field string, delimiter ...string) bool {
return true
}

//Get allows to get specified field from FieldSelection
// Get allows to get specified field from FieldSelection
func (f *FieldSelection) Get(field string, delimiter ...string) *Field {
if len(field) == 0 || f.Fields == nil {
return nil
Expand Down
4 changes: 2 additions & 2 deletions query/filtering_lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ func (t InToken) String() string {
return "in"
}

//NumberArrayToken represent number array e.g. [1,2,5]
// NumberArrayToken represent number array e.g. [1,2,5]
type StringArrayToken struct {
TokenBase
Values []string
Expand All @@ -231,7 +231,7 @@ func (t StringArrayToken) String() string {
return fmt.Sprintf("%v", t.Values)
}

//NumberArrayToken represent number array e.g. [1,2,5]
// NumberArrayToken represent number array e.g. [1,2,5]
type NumberArrayToken struct {
TokenBase
Values []float64
Expand Down
10 changes: 9 additions & 1 deletion query/pagination.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const (

// Pagination parses string representation of pagination limit, offset.
// Returns error if limit or offset has invalid syntax or out of range.
func ParsePagination(limit, offset, ptoken string) (*Pagination, error) {
func ParsePagination(limit, offset, ptoken, isTotalSizeNeeded string) (*Pagination, error) {
p := new(Pagination)

if limit != "" {
Expand Down Expand Up @@ -43,6 +43,14 @@ func ParsePagination(limit, offset, ptoken string) (*Pagination, error) {
p.PageToken = ptoken
}

if isTotalSizeNeeded != "" {
u, err := strconv.ParseBool(isTotalSizeNeeded)
if err != nil {
return nil, fmt.Errorf("pagination: is_total_size_needed - %s", err.(*strconv.NumError).Err)
}
p.IsTotalSizeNeeded = u
}

return p, nil
}

Expand Down
44 changes: 35 additions & 9 deletions query/pagination_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package query

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestParsePagination(t *testing.T) {
// invalid limit
_, err := ParsePagination("1s", "0", "ptoken")
_, err := ParsePagination("1s", "0", "ptoken", "")
if err == nil {
t.Error("unexpected nil error - expected: pagination: limit - invalid syntax")
}
Expand All @@ -15,7 +17,7 @@ func TestParsePagination(t *testing.T) {
}

// negative limit
_, err = ParsePagination("-1", "0", "ptoken")
_, err = ParsePagination("-1", "0", "ptoken", "")
if err == nil {
t.Error("unexpected nil error - expected: pagination: limit must be a positive value")
}
Expand All @@ -24,7 +26,7 @@ func TestParsePagination(t *testing.T) {
}

// zero limit
_, err = ParsePagination("0", "0", "ptoken")
_, err = ParsePagination("0", "0", "ptoken", "")
if err == nil {
t.Error("unexpected nil error - expected: pagination: limit must be a positive value")
}
Expand All @@ -33,7 +35,7 @@ func TestParsePagination(t *testing.T) {
}

// invalid offset
_, err = ParsePagination("", "0w", "ptoken")
_, err = ParsePagination("", "0w", "ptoken", "")
if err == nil {
t.Error("unexpected nil error - expected: pagination: offset - invalid syntax")
}
Expand All @@ -42,7 +44,7 @@ func TestParsePagination(t *testing.T) {
}

// negative offset
_, err = ParsePagination("", "-1", "ptoken")
_, err = ParsePagination("", "-1", "ptoken", "")
if err == nil {
t.Error("unexpected nil error - expected: pagination: offset - negative value")
}
Expand All @@ -51,7 +53,7 @@ func TestParsePagination(t *testing.T) {
}

// null offset
p, err := ParsePagination("", "null", "ptoken")
p, err := ParsePagination("", "null", "ptoken", "")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
Expand All @@ -60,14 +62,14 @@ func TestParsePagination(t *testing.T) {
}

// first page
p, err = ParsePagination("", "0", "ptoken")
p, err = ParsePagination("", "0", "ptoken", "")
if err != nil {
t.Errorf("unexpected error: %s", err)
}
if !p.FirstPage() {
t.Errorf("invalid value of first page: %v - expected: true", p.FirstPage())
}
p, err = ParsePagination("", "100", "null")
p, err = ParsePagination("", "100", "null", "")
if err != nil {
t.Errorf("unexpected error: %s", err)
}
Expand All @@ -81,7 +83,7 @@ func TestParsePagination(t *testing.T) {
}

// valid pagination
p, err = ParsePagination("1000", "100", "ptoken")
p, err = ParsePagination("1000", "100", "ptoken", "")
if err != nil {
t.Errorf("unexpected error: %s", err)
}
Expand All @@ -94,6 +96,30 @@ func TestParsePagination(t *testing.T) {
if p.GetPageToken() != "ptoken" {
t.Errorf("invalid page token: %q - expected: ptoken", p.GetPageToken())
}

// valid pagination with isTotalSizeNeeded=true
p, err = ParsePagination("1000", "100", "ptoken", "true")
if err != nil {
t.Errorf("unexpected error: %s", err)
}
assert.Equal(t, true, p.GetIsTotalSizeNeeded())

// valid pagination with isTotalSizeNeeded=false
p, err = ParsePagination("1000", "100", "ptoken", "false")
if err != nil {
t.Errorf("unexpected error: %s", err)
}
assert.Equal(t, false, p.GetIsTotalSizeNeeded())

// valid pagination with isTotalSizeNeeded=null
_, err = ParsePagination("1000", "100", "ptoken", "null")
if err == nil {
t.Error("unexpected nil error - expected: pagination: is_total_size_needed - invalid syntax")
}
if err.Error() != "pagination: is_total_size_needed - invalid syntax" {
t.Errorf("invalid error: %s - expected: pagination: is_total_size_needed - invalid syntax", err)
}

}

func TestPageInfo(t *testing.T) {
Expand Down
1 change: 0 additions & 1 deletion requestid/interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
// that should be used as a middleware to generate/include Request-Id in headers and context
// for tracing and tracking user's request.
//
//
// Returned middleware populates Request-Id from gRPC metadata if
// they defined in a testRequest message else creates a new one.
func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
Expand Down
8 changes: 6 additions & 2 deletions rpc/resource/jsonpb.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (

// MarshalJSONPB implements jsonpb.JSONPBMarshaler interface by marshal
// Identifier from a JSON string in accordance with Atlas Reference format
// <application_name>/<resource_type>/<resource_id>
//
// <application_name>/<resource_type>/<resource_id>
//
// Support "null" value.
func (m Identifier) MarshalJSONPB(*jsonpb.Marshaler) ([]byte, error) {
v := BuildString(m.GetApplicationName(), m.GetResourceType(), m.GetResourceId())
Expand All @@ -28,7 +30,9 @@ var _ json.Marshaler = &Identifier{}

// UnmarshalJSONPB implements jsonpb.JSONPBUnmarshaler interface by unmarshal
// Identifier to a JSON string in accordance with Atlas Reference format
// <application_name>/<resource_type>/<resource_id>
//
// <application_name>/<resource_type>/<resource_id>
//
// Support "null" value.
func (m *Identifier) UnmarshalJSONPB(_ *jsonpb.Unmarshaler, data []byte) error {
v := strings.Trim(string(data), "\"")
Expand Down
5 changes: 4 additions & 1 deletion rpc/resource/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const (
)

// BuildString builds string id according to Atlas Reference format:
// <application_name>/<resource_type>/<resource_id>
//
// <application_name>/<resource_type>/<resource_id>
func BuildString(aname, rtype, rid string) string {
var l []string

Expand All @@ -26,7 +27,9 @@ func BuildString(aname, rtype, rid string) string {
}

// ParseString parses id according to Atlas Reference format:
//
// <application_name>/<resource_type>/<resource_id>
//
// All leading and trailing Delimiter are removed.
// The resource_id is parsed first, then resource type and last application name.
// The id "/a/b/c/" will be converted to "a/b/c" and returned as (a, b, c).
Expand Down
2 changes: 1 addition & 1 deletion server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type Server struct {
isAutomaticStop bool
}

//Middleware wrapper
// Middleware wrapper
type Middleware func(handler http.Handler) http.Handler

// Option is a functional option for creating a Server
Expand Down
Loading

0 comments on commit 4c0b55b

Please sign in to comment.