From 59c388b419b2d714f8134ba03607414dde8e3128 Mon Sep 17 00:00:00 2001 From: Awbrey Hughlett Date: Mon, 25 Nov 2024 16:40:03 -0500 Subject: [PATCH] add QueryKey helper functions (#613) * add QueryKey helper functions --- pkg/types/query/key_filter.go | 164 +++++++++++++++++++++++ pkg/types/query/key_filter_test.go | 200 +++++++++++++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 pkg/types/query/key_filter.go create mode 100644 pkg/types/query/key_filter_test.go diff --git a/pkg/types/query/key_filter.go b/pkg/types/query/key_filter.go new file mode 100644 index 000000000..d5b5b78c3 --- /dev/null +++ b/pkg/types/query/key_filter.go @@ -0,0 +1,164 @@ +package query + +import ( + "time" + + "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" +) + +// IndexedSequencesKeyFilter creates a KeyFilter that filters logs for the provided sequence property values at the +// specified property name. Sequence value filters are 'OR'ed together. A sequence read name is the value that +// identifies the sequence type. The signature value name is the sequence property to apply the filter to and the +// sequence values are the individual values to search for in the provided property. +func IndexedSequencesKeyFilter( + readName string, + comparatorName string, + values []string, + confidence primitives.ConfidenceLevel, +) KeyFilter { + return KeyFilter{ + Key: readName, + Expressions: []Expression{ + filtersForValues(comparatorName, values), + Confidence(confidence), + }, + } +} + +// IndexedSequencesByBlockRangeKeyFilter creates a KeyFilter that filters sequences for the provided property values at +// the specified property name. Value filters are 'OR'ed together and results are limited by provided cursor range. A +// read name is the value that identifies the sequence type. The signature property name is the sequence property to +// apply the filter to and the sequence values are the individual values to search for in the provided property. +func IndexedSequencesByBlockRangeKeyFilter( + readName string, + start, end string, + comparatorName string, + values []string, +) KeyFilter { + return KeyFilter{ + Key: readName, + Expressions: []Expression{ + filtersForValues(comparatorName, values), + Block(start, primitives.Gte), + Block(end, primitives.Lte), + }, + } +} + +// IndexedSequencesValueGreaterThanKeyFilter creates a KeyFilter that filters sequences for the provided property value +// and name at or above the specified confidence level. A sequence read name is the value that identifies the sequence +// type. The property name is the sequence property to apply the filter to and the value is the individual value to +// search for in the provided property. +func IndexedSequencesValueGreaterThanKeyFilter( + readName string, + comparatorName, value string, + confidence primitives.ConfidenceLevel, +) KeyFilter { + return KeyFilter{ + Key: readName, + Expressions: []Expression{ + valueComparator(comparatorName, value, primitives.Gte), + Confidence(confidence), + }, + } +} + +// IndexedSequencesValueRangeKeyFilter creates a KeyFilter that filters logs on the provided sequence property between +// the provided min and max, endpoints inclusive. A sequence read name is the value that identifies the sequence type. +func IndexedSequencesValueRangeKeyFilter( + readName string, + comparatorName string, + min, max string, + confidence primitives.ConfidenceLevel, +) KeyFilter { + return KeyFilter{ + Key: readName, + Expressions: []Expression{ + valueComparator(comparatorName, min, primitives.Gte), + valueComparator(comparatorName, max, primitives.Lte), + Confidence(confidence), + }, + } +} + +// IndexedSequencesByTxHashKeyFilter creates a KeyFilter that filters logs for the provided transaction hash. A sequence +// read name is the value that identifies the sequence type. +func IndexedSequencesByTxHashKeyFilter( + readName, txHash string, +) KeyFilter { + return KeyFilter{ + Key: readName, + Expressions: []Expression{ + TxHash(txHash), + }, + } +} + +// SequencesByBlockRangeKeyFilter creates a KeyFilter that filters sequences for the provided block range, endpoints inclusive. +func SequencesByBlockRangeKeyFilter( + readName string, + start, end string, +) KeyFilter { + return KeyFilter{ + Key: readName, + Expressions: []Expression{ + Block(start, primitives.Gte), + Block(end, primitives.Lte), + }, + } +} + +// SequencesCreatedAfterKeyFilter creates a KeyFilter that filters sequences for after but not equal to the provided time value. +func SequencesCreatedAfterKeyFilter( + readName string, + timestamp time.Time, + confidence primitives.ConfidenceLevel, +) KeyFilter { + return KeyFilter{ + Key: readName, + Expressions: []Expression{ + Timestamp(uint64(timestamp.Unix()), primitives.Gt), + Confidence(confidence), + }, + } +} + +// IndexedSequencesCreatedAfterKeyFilter creates a KeyFilter that filters sequences for the provided property and values +// created after the provided time value. Sequence property values filters are 'OR'ed. A sequence read name is the value +// that identifies the sequence type. +func IndexedSequencesCreatedAfterKeyFilter( + readName string, + comparatorName string, + values []string, + timestamp time.Time, + confidence primitives.ConfidenceLevel, +) KeyFilter { + return KeyFilter{ + Key: readName, + Expressions: []Expression{ + filtersForValues(comparatorName, values), + Timestamp(uint64(timestamp.Unix()), primitives.Gt), + Confidence(confidence), + }, + } +} + +func valueComparator(comparatorName, value string, op primitives.ComparisonOperator) Expression { + return Comparator(comparatorName, primitives.ValueComparator{ + Value: value, + Operator: op, + }) +} + +func filtersForValues(comparatorName string, values []string) Expression { + valueFilters := BoolExpression{ + Expressions: make([]Expression, len(values)), + BoolOperator: OR, + } + + for idx, value := range values { + valueFilters.Expressions[idx] = valueComparator(comparatorName, value, primitives.Eq) + } + + return Expression{BoolExpression: valueFilters} +} diff --git a/pkg/types/query/key_filter_test.go b/pkg/types/query/key_filter_test.go new file mode 100644 index 000000000..5f7cf0d9a --- /dev/null +++ b/pkg/types/query/key_filter_test.go @@ -0,0 +1,200 @@ +package query_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/types/query" + "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" +) + +func TestIndexedSequencesKeyFilter(t *testing.T) { + t.Parallel() + + filter := query.IndexedSequencesKeyFilter("readName", "property", []string{"value1", "value2"}, primitives.Finalized) + expected := query.KeyFilter{ + Key: "readName", + Expressions: []query.Expression{ + {BoolExpression: query.BoolExpression{ + Expressions: []query.Expression{ + { + Primitive: &primitives.Comparator{ + Name: "property", + ValueComparators: []primitives.ValueComparator{{Value: "value1", Operator: primitives.Eq}}, + }, + }, + { + Primitive: &primitives.Comparator{ + Name: "property", + ValueComparators: []primitives.ValueComparator{{Value: "value2", Operator: primitives.Eq}}, + }, + }, + }, + BoolOperator: query.OR, + }}, + {Primitive: &primitives.Confidence{ConfidenceLevel: primitives.Finalized}}, + }, + } + + require.Equal(t, expected, filter) +} + +func TestIndexedSequencesByBlockRangeKeyFilter(t *testing.T) { + t.Parallel() + + filter := query.IndexedSequencesByBlockRangeKeyFilter("readName", "start", "end", "property", []string{"value1", "value2"}) + expected := query.KeyFilter{ + Key: "readName", + Expressions: []query.Expression{ + {BoolExpression: query.BoolExpression{ + Expressions: []query.Expression{ + { + Primitive: &primitives.Comparator{ + Name: "property", + ValueComparators: []primitives.ValueComparator{{Value: "value1", Operator: primitives.Eq}}, + }, + }, + { + Primitive: &primitives.Comparator{ + Name: "property", + ValueComparators: []primitives.ValueComparator{{Value: "value2", Operator: primitives.Eq}}, + }, + }, + }, + BoolOperator: query.OR, + }}, + {Primitive: &primitives.Block{Block: "start", Operator: primitives.Gte}}, + {Primitive: &primitives.Block{Block: "end", Operator: primitives.Lte}}, + }, + } + + require.Equal(t, expected, filter) +} + +func TestIndexedSequencesValueGreaterThanKeyFilter(t *testing.T) { + t.Parallel() + + filter := query.IndexedSequencesValueGreaterThanKeyFilter("readName", "property", "value1", primitives.Finalized) + expected := query.KeyFilter{ + Key: "readName", + Expressions: []query.Expression{ + { + Primitive: &primitives.Comparator{ + Name: "property", + ValueComparators: []primitives.ValueComparator{{Value: "value1", Operator: primitives.Gte}}, + }, + }, + {Primitive: &primitives.Confidence{ConfidenceLevel: primitives.Finalized}}, + }, + } + + require.Equal(t, expected, filter) +} + +func TestIndexedSequencesValueRangeKeyFilter(t *testing.T) { + t.Parallel() + + filter := query.IndexedSequencesValueRangeKeyFilter("readName", "property", "min", "max", primitives.Finalized) + expected := query.KeyFilter{ + Key: "readName", + Expressions: []query.Expression{ + { + Primitive: &primitives.Comparator{ + Name: "property", + ValueComparators: []primitives.ValueComparator{{Value: "min", Operator: primitives.Gte}}, + }, + }, + { + Primitive: &primitives.Comparator{ + Name: "property", + ValueComparators: []primitives.ValueComparator{{Value: "max", Operator: primitives.Lte}}, + }, + }, + {Primitive: &primitives.Confidence{ConfidenceLevel: primitives.Finalized}}, + }, + } + + require.Equal(t, expected, filter) +} + +func TestIndexedSequencesByTxHashKeyFilter(t *testing.T) { + t.Parallel() + + filter := query.IndexedSequencesByTxHashKeyFilter("readName", "hash") + expected := query.KeyFilter{ + Key: "readName", + Expressions: []query.Expression{ + {Primitive: &primitives.TxHash{TxHash: "hash"}}, + }, + } + + require.Equal(t, expected, filter) +} + +func TestSequencesByBlockRangeKeyFilter(t *testing.T) { + t.Parallel() + + filter := query.SequencesByBlockRangeKeyFilter("readName", "start", "end") + expected := query.KeyFilter{ + Key: "readName", + Expressions: []query.Expression{ + {Primitive: &primitives.Block{Block: "start", Operator: primitives.Gte}}, + {Primitive: &primitives.Block{Block: "end", Operator: primitives.Lte}}, + }, + } + + require.Equal(t, expected, filter) +} + +func TestSequencesCreatedAfterKeyFilter(t *testing.T) { + t.Parallel() + + now := time.Now() + + filter := query.SequencesCreatedAfterKeyFilter("readName", now, primitives.Finalized) + expected := query.KeyFilter{ + Key: "readName", + Expressions: []query.Expression{ + {Primitive: &primitives.Timestamp{Timestamp: uint64(now.Unix()), Operator: primitives.Gt}}, + {Primitive: &primitives.Confidence{ConfidenceLevel: primitives.Finalized}}, + }, + } + + require.Equal(t, expected, filter) +} + +func TestIndexedSequencesCreatedAfterKeyFilter(t *testing.T) { + t.Parallel() + + now := time.Now() + + filter := query.IndexedSequencesCreatedAfterKeyFilter("readName", "property", []string{"value1", "value2"}, now, primitives.Finalized) + expected := query.KeyFilter{ + Key: "readName", + Expressions: []query.Expression{ + {BoolExpression: query.BoolExpression{ + Expressions: []query.Expression{ + { + Primitive: &primitives.Comparator{ + Name: "property", + ValueComparators: []primitives.ValueComparator{{Value: "value1", Operator: primitives.Eq}}, + }, + }, + { + Primitive: &primitives.Comparator{ + Name: "property", + ValueComparators: []primitives.ValueComparator{{Value: "value2", Operator: primitives.Eq}}, + }, + }, + }, + BoolOperator: query.OR, + }}, + {Primitive: &primitives.Timestamp{Timestamp: uint64(now.Unix()), Operator: primitives.Gt}}, + {Primitive: &primitives.Confidence{ConfidenceLevel: primitives.Finalized}}, + }, + } + + require.Equal(t, expected, filter) +}