Skip to content

Commit

Permalink
Merge branch 'task/flagSets' of github.com:splitio/splitd into task/f…
Browse files Browse the repository at this point in the history
…lagsetsinmanager
  • Loading branch information
mmelograno committed Jan 9, 2024
2 parents 1ad06e9 + 477cb0c commit d70b26c
Show file tree
Hide file tree
Showing 10 changed files with 802 additions and 11 deletions.
2 changes: 1 addition & 1 deletion splitio/commitsha.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package splitio

const CommitSHA = "ff4a059"
const CommitSHA = "b98165e"
28 changes: 28 additions & 0 deletions splitio/link/protocol/v1/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,34 @@ func NewTreatmentsResp(ok bool, data []sdk.EvaluationResult) *v1.ResponseWrapper
}
}

func NewTreatmentsByFlagSetResp(ok bool, data map[string]sdk.EvaluationResult) *v1.ResponseWrapper[v1.TreatmentsWithFeaturePayload] {
res := v1.ResultOk
if !ok {
res = v1.ResultInternalError
}

payload := make(map[string]v1.TreatmentPayload, len(data))
for f, r := range data {
p := v1.TreatmentPayload{
Treatment: r.Treatment,
Config: r.Config,
}
if r.Impression != nil {
p.ListenerData = &v1.ListenerExtraData{
Label: r.Impression.Label,
Timestamp: r.Impression.Time,
ChangeNumber: r.Impression.ChangeNumber,
}
}
payload[f] = p
}

return &v1.ResponseWrapper[v1.TreatmentsWithFeaturePayload]{
Status: res,
Payload: v1.TreatmentsWithFeaturePayload{Results: payload},
}
}

func NewTrackResp(ok bool) *v1.ResponseWrapper[v1.TrackPayload] {
res := v1.ResultOk
if !ok {
Expand Down
7 changes: 6 additions & 1 deletion splitio/link/protocol/v1/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ type ResponseWrapper[T validPayloadsConstraint] struct {

type RegisterPayload struct{}

type TreatmentsWithFeaturePayload struct {
Results map[string]TreatmentPayload `msgpack:"r"`
}

type TreatmentPayload struct {
Treatment string `msgpack:"t"`
Config *string `msgpack:"c,omitempty"`
Expand Down Expand Up @@ -60,5 +64,6 @@ type validPayloadsConstraint interface {
SplitNamesPayload |
SplitPayload |
SplitsPayload |
RegisterPayload
RegisterPayload |
TreatmentsWithFeaturePayload
}
144 changes: 135 additions & 9 deletions splitio/link/protocol/v1/rpcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ const (
OCRegister OpCode = 0x00

// Treatment-related ops
OCTreatment OpCode = 0x11
OCTreatments OpCode = 0x12
OCTreatmentWithConfig OpCode = 0x13
OCTreatmentsWithConfig OpCode = 0x14
OCTreatment OpCode = 0x11
OCTreatments OpCode = 0x12
OCTreatmentWithConfig OpCode = 0x13
OCTreatmentsWithConfig OpCode = 0x14
OCTreatmentsByFlagSet OpCode = 0x15
OCTreatmentsWithConfigByFlagSet OpCode = 0x16
OCTreatmentsByFlagSets OpCode = 0x17
OCTreatmentsWithConfigByFlagSets OpCode = 0x18

// Track-related ops
OCTrack OpCode = 0x80
Expand All @@ -39,6 +43,14 @@ func (o OpCode) String() string {
return "treatment-with-config"
case OCTreatmentsWithConfig:
return "treatments-with-config"
case OCTreatmentsByFlagSet:
return "treatments-by-flag-set"
case OCTreatmentsWithConfigByFlagSet:
return "treatments-with-config-by-flag-set"
case OCTreatmentsByFlagSets:
return "treatments-by-flag-sets"
case OCTreatmentsWithConfigByFlagSets:
return "treatments-with-config-by-flag-sets"
case OCTrack:
return "track"
case OCSplitNames:
Expand Down Expand Up @@ -215,7 +227,7 @@ func (t *TreatmentsArgs) PopulateFromRPC(rpc *RPC) error {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsArgFeaturesIdx)}

}
t.Features, ok = sanitizeFeatureList(rawFeatureList)
t.Features, ok = sanitizeToStringSlice(rawFeatureList)
if !ok {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsArgFeaturesIdx)}
}
Expand All @@ -229,6 +241,120 @@ func (t *TreatmentsArgs) PopulateFromRPC(rpc *RPC) error {
return nil
}

const (
TreatmentsByFlagSetArgKeyIdx int = 0
TreatmentsByFlagSetArgBucketingKeyIdx int = 1
TreatmentsByFlagSetArgFlagSetIdx int = 2
TreatmentsByFlagSetArgAttributesIdx int = 3
)

type TreatmentsByFlagSetArgs struct {
Key string `msgpack:"k"`
BucketingKey *string `msgpack:"b"`
FlagSet string `msgpack:"f"`
Attributes map[string]interface{} `msgpack:"a"`
}

func (r TreatmentsByFlagSetArgs) Encode() []interface{} {
var bk string
if r.BucketingKey != nil {
bk = *r.BucketingKey
}
return []interface{}{r.Key, bk, r.FlagSet, r.Attributes}
}

func (t *TreatmentsByFlagSetArgs) PopulateFromRPC(rpc *RPC) error {
if rpc.OpCode != OCTreatmentsByFlagSet && rpc.OpCode != OCTreatmentsWithConfigByFlagSet {
return RPCParseError{Code: PECOpCodeMismatch}
}
if len(rpc.Args) != 4 {
return RPCParseError{Code: PECWrongArgCount}
}

var ok bool
var err error

if t.Key, ok = rpc.Args[TreatmentsByFlagSetArgKeyIdx].(string); !ok {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetArgKeyIdx)}
}

if t.BucketingKey, err = getOptionalRef[string](rpc.Args[TreatmentsByFlagSetArgBucketingKeyIdx]); err != nil {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetArgBucketingKeyIdx)}
}

if t.FlagSet, ok = rpc.Args[TreatmentsByFlagSetArgFlagSetIdx].(string); !ok {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetArgFlagSetIdx)}
}

rawAttrs, err := getOptional[map[string]interface{}](rpc.Args[TreatmentsByFlagSetArgAttributesIdx])
if err != nil {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetArgAttributesIdx)}
}
t.Attributes = sanitizeAttributes(rawAttrs)

return nil
}

const (
TreatmentsByFlagSetsArgKeyIdx int = 0
TreatmentsByFlagSetsArgBucketingKeyIdx int = 1
TreatmentsByFlagSetsArgFlagSetsIdx int = 2
TreatmentsByFlagSetsArgAttributesIdx int = 3
)

type TreatmentsByFlagSetsArgs struct {
Key string `msgpack:"k"`
BucketingKey *string `msgpack:"b"`
FlagSets []string `msgpack:"f"`
Attributes map[string]interface{} `msgpack:"a"`
}

func (r TreatmentsByFlagSetsArgs) Encode() []interface{} {
var bk string
if r.BucketingKey != nil {
bk = *r.BucketingKey
}
return []interface{}{r.Key, bk, r.FlagSets, r.Attributes}
}

func (t *TreatmentsByFlagSetsArgs) PopulateFromRPC(rpc *RPC) error {
if rpc.OpCode != OCTreatmentsByFlagSets && rpc.OpCode != OCTreatmentsWithConfigByFlagSets {
return RPCParseError{Code: PECOpCodeMismatch}
}
if len(rpc.Args) != 4 {
return RPCParseError{Code: PECWrongArgCount}
}

var ok bool
var err error

if t.Key, ok = rpc.Args[TreatmentsByFlagSetsArgKeyIdx].(string); !ok {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetsArgKeyIdx)}
}

if t.BucketingKey, err = getOptionalRef[string](rpc.Args[TreatmentsByFlagSetsArgBucketingKeyIdx]); err != nil {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetsArgBucketingKeyIdx)}
}

rawFlagSetsList, ok := rpc.Args[TreatmentsByFlagSetsArgFlagSetsIdx].([]interface{})
if !ok {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetsArgFlagSetsIdx)}
}

t.FlagSets, ok = sanitizeToStringSlice(rawFlagSetsList)
if !ok {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetsArgFlagSetsIdx)}
}

rawAttrs, err := getOptional[map[string]interface{}](rpc.Args[TreatmentsByFlagSetsArgAttributesIdx])
if err != nil {
return RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetsArgAttributesIdx)}
}
t.Attributes = sanitizeAttributes(rawAttrs)

return nil
}

const (
TrackArgKeyIdx int = 0
TrackArgTrafficTypeIdx int = 1
Expand Down Expand Up @@ -416,16 +542,16 @@ func sanitizeAttributes(attrs map[string]interface{}) map[string]interface{} {
return attrs
}

func sanitizeFeatureList(raw []interface{}) ([]string, bool) {
features := make([]string, 0, len(raw))
func sanitizeToStringSlice(raw []interface{}) ([]string, bool) {
asStringSlice := make([]string, 0, len(raw))
for _, f := range raw {
asStr, ok := f.(string)
if !ok {
return nil, false
}
features = append(features, asStr)
asStringSlice = append(asStringSlice, asStr)
}
return features, true
return asStringSlice, true
}

func tryInt[T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64](x interface{}) (T, bool) {
Expand Down
130 changes: 130 additions & 0 deletions splitio/link/protocol/v1/rpcs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ func TestOpStringify(t *testing.T) {
assert.Equal(t, "treatment-with-config", OCTreatmentWithConfig.String())
assert.Equal(t, "treatments", OCTreatments.String())
assert.Equal(t, "treatments-with-config", OCTreatmentsWithConfig.String())
assert.Equal(t, "treatments-by-flag-set", OCTreatmentsByFlagSet.String())
assert.Equal(t, "treatments-with-config-by-flag-set", OCTreatmentsWithConfigByFlagSet.String())
assert.Equal(t, "treatments-by-flag-sets", OCTreatmentsByFlagSets.String())
assert.Equal(t, "treatments-with-config-by-flag-sets", OCTreatmentsWithConfigByFlagSets.String())
assert.Equal(t, "track", OCTrack.String())
assert.Equal(t, "split-names", OCSplitNames.String())
assert.Equal(t, "split", OCSplit.String())
Expand Down Expand Up @@ -177,6 +181,132 @@ func TestTreatmentsRPCParsing(t *testing.T) {
assert.Nil(t, r.Attributes)
}

func TestTreatmentsByFlagSetRPCParsing(t *testing.T) {
var r TreatmentsByFlagSetArgs
assert.Equal(t,
RPCParseError{Code: PECOpCodeMismatch},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCRegister, Args: nil}),
)
assert.Equal(t,
RPCParseError{Code: PECWrongArgCount},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCTreatmentsByFlagSet, Args: []interface{}{}}),
)
assert.Equal(t,
RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetArgKeyIdx)},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCTreatmentsByFlagSet, Args: []interface{}{nil, nil, nil, nil}}),
)
assert.Equal(t,
RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetArgBucketingKeyIdx)},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCTreatmentsByFlagSet, Args: []interface{}{"key", 123, nil, nil}}),
)
assert.Equal(t,
RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetArgFlagSetIdx)},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCTreatmentsByFlagSet, Args: []interface{}{"key", "bk", 123, nil}}),
)
assert.Equal(t,
RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetArgAttributesIdx)},
r.PopulateFromRPC(&RPC{
RPCBase: protocol.RPCBase{Version: protocol.V1},
OpCode: OCTreatmentsByFlagSet,
Args: []interface{}{"key", "bk", "set", 123}}),
)

err := r.PopulateFromRPC(&RPC{
RPCBase: protocol.RPCBase{Version: protocol.V1},
OpCode: OCTreatmentsByFlagSet,
Args: []interface{}{"key", "bk", "set", map[string]interface{}{"a": 1}}})
assert.Nil(t, err)
assert.Equal(t, "key", r.Key)
assert.Equal(t, lang.Ref("bk"), r.BucketingKey)
assert.Equal(t, "set", r.FlagSet)
assert.Equal(t, map[string]interface{}{"a": int64(1)}, r.Attributes)

// nil bucketing key
err = r.PopulateFromRPC(&RPC{
RPCBase: protocol.RPCBase{Version: protocol.V1},
OpCode: OCTreatmentsByFlagSet,
Args: []interface{}{"key", nil, "set", map[string]interface{}{"a": 1}}})
assert.Nil(t, err)
assert.Equal(t, "key", r.Key)
assert.Nil(t, r.BucketingKey)
assert.Equal(t, "set", r.FlagSet)
assert.Equal(t, map[string]interface{}{"a": int64(1)}, r.Attributes)

// nil attributes
err = r.PopulateFromRPC(&RPC{
RPCBase: protocol.RPCBase{Version: protocol.V1},
OpCode: OCTreatmentsByFlagSet,
Args: []interface{}{"key", "bk", "set", nil}})
assert.Nil(t, err)
assert.Equal(t, "key", r.Key)
assert.Equal(t, lang.Ref("bk"), r.BucketingKey)
assert.Equal(t, "set", r.FlagSet)
assert.Nil(t, r.Attributes)
}

func TestTreatmentsByFlagSetsRPCParsing(t *testing.T) {
var r TreatmentsByFlagSetsArgs
assert.Equal(t,
RPCParseError{Code: PECOpCodeMismatch},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCRegister, Args: nil}),
)
assert.Equal(t,
RPCParseError{Code: PECWrongArgCount},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCTreatmentsByFlagSets, Args: []interface{}{}}),
)
assert.Equal(t,
RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetsArgKeyIdx)},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCTreatmentsByFlagSets, Args: []interface{}{nil, nil, nil, nil}}),
)
assert.Equal(t,
RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetsArgBucketingKeyIdx)},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCTreatmentsByFlagSets, Args: []interface{}{"key", 123, nil, nil}}),
)
assert.Equal(t,
RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetsArgFlagSetsIdx)},
r.PopulateFromRPC(&RPC{RPCBase: protocol.RPCBase{Version: protocol.V1}, OpCode: OCTreatmentsByFlagSets, Args: []interface{}{"key", "bk", 123, nil}}),
)
assert.Equal(t,
RPCParseError{Code: PECInvalidArgType, Data: int64(TreatmentsByFlagSetsArgAttributesIdx)},
r.PopulateFromRPC(&RPC{
RPCBase: protocol.RPCBase{Version: protocol.V1},
OpCode: OCTreatmentsByFlagSets,
Args: []interface{}{"key", "bk", []interface{}{"set_1", "set_2"}, 123}}),
)

err := r.PopulateFromRPC(&RPC{
RPCBase: protocol.RPCBase{Version: protocol.V1},
OpCode: OCTreatmentsByFlagSets,
Args: []interface{}{"key", "bk", []interface{}{"set_1", "set_2"}, map[string]interface{}{"a": 1}}})
assert.Nil(t, err)
assert.Equal(t, "key", r.Key)
assert.Equal(t, lang.Ref("bk"), r.BucketingKey)
assert.Equal(t, []string{"set_1", "set_2"}, r.FlagSets)
assert.Equal(t, map[string]interface{}{"a": int64(1)}, r.Attributes)

// nil bucketing key
err = r.PopulateFromRPC(&RPC{
RPCBase: protocol.RPCBase{Version: protocol.V1},
OpCode: OCTreatmentsByFlagSets,
Args: []interface{}{"key", nil, []interface{}{"set_1", "set_2"}, map[string]interface{}{"a": 1}}})
assert.Nil(t, err)
assert.Equal(t, "key", r.Key)
assert.Nil(t, r.BucketingKey)
assert.Equal(t, []string{"set_1", "set_2"}, r.FlagSets)
assert.Equal(t, map[string]interface{}{"a": int64(1)}, r.Attributes)

// nil attributes
err = r.PopulateFromRPC(&RPC{
RPCBase: protocol.RPCBase{Version: protocol.V1},
OpCode: OCTreatmentsByFlagSets,
Args: []interface{}{"key", "bk", []interface{}{"set_1", "set_2"}, nil}})
assert.Nil(t, err)
assert.Equal(t, "key", r.Key)
assert.Equal(t, lang.Ref("bk"), r.BucketingKey)
assert.Equal(t, []string{"set_1", "set_2"}, r.FlagSets)
assert.Nil(t, r.Attributes)
}

func TestTrackRPCParsing(t *testing.T) {
var r TrackArgs
assert.Equal(t,
Expand Down
Loading

0 comments on commit d70b26c

Please sign in to comment.