diff --git a/internal/datasourcev2/polling_data_source.go b/internal/datasourcev2/polling_data_source.go index 30f79239..e90fae3d 100644 --- a/internal/datasourcev2/polling_data_source.go +++ b/internal/datasourcev2/polling_data_source.go @@ -148,7 +148,7 @@ func (pp *PollingProcessor) poll() error { return err } - payload := changeSet.Intent().Payloads[0] + payload := changeSet.IntentCode().Payload switch payload.Code { case fdv2proto.IntentTransferFull: pp.dataDestination.SetBasis(changeSet.Changes(), changeSet.Selector(), true) diff --git a/internal/datasourcev2/streaming_data_source.go b/internal/datasourcev2/streaming_data_source.go index b3d64926..e5fc21bb 100644 --- a/internal/datasourcev2/streaming_data_source.go +++ b/internal/datasourcev2/streaming_data_source.go @@ -258,8 +258,7 @@ func (sp *StreamProcessor) consumeStream(stream *es.Stream, closeWhenReady chan< break } - // The Finish method guarantees that at least one payload is present. - payload := changeSet.Intent().Payloads[0] + payload := changeSet.IntentCode().Payload switch payload.Code { case fdv2proto.IntentTransferFull: sp.dataDestination.SetBasis(changeSet.Changes(), changeSet.Selector(), true) diff --git a/internal/fdv2proto/changeset.go b/internal/fdv2proto/changeset.go index eb5c72cf..9f519255 100644 --- a/internal/fdv2proto/changeset.go +++ b/internal/fdv2proto/changeset.go @@ -21,13 +21,13 @@ type Change struct { } type ChangeSet struct { - intent ServerIntent + intentCode IntentCode changes []Change selector Selector } -func (c *ChangeSet) Intent() ServerIntent { - return c.intent +func (c *ChangeSet) IntentCode() IntentCode{ + return c.intentCode } func (c *ChangeSet) Changes() []Change { @@ -49,16 +49,13 @@ func NewChangeSetBuilder() *ChangeSetBuilder { func (c *ChangeSetBuilder) NoChanges() *ChangeSet { return &ChangeSet{ - intent: ServerIntent{Payloads: []Payload{{Code: IntentNone}}}, + intentCode: IntentNone, selector: NoSelector(), changes: nil, } } func (c *ChangeSetBuilder) Start(intent ServerIntent) error { - if len(intent.Payloads) == 0 { - return errors.New("changeset: server-intent event has no payloads") - } c.intent = &intent c.changes = nil return nil @@ -71,11 +68,12 @@ func (c *ChangeSetBuilder) Finish(selector Selector) (*ChangeSet, error) { return nil, errors.New("changeset: cannot complete without a server-intent") } changes := &ChangeSet{ - intent: *c.intent, + intentCode: c.intent.Payload.Code, selector: selector, changes: c.changes, } c.changes = nil + c.intent.Payload.Code = return changes, nil } diff --git a/internal/fdv2proto/changeset_test.go b/internal/fdv2proto/changeset_test.go index 0426c07f..794198bd 100644 --- a/internal/fdv2proto/changeset_test.go +++ b/internal/fdv2proto/changeset_test.go @@ -16,27 +16,15 @@ func TestChangeSetBuilder_MustStartToFinish(t *testing.T) { _, err := builder.Finish(selector) assert.Error(t, err) - assert.NoError(t, builder.Start(ServerIntent{Payloads: []Payload{{Code: IntentNone}}})) + assert.NoError(t, builder.Start(ServerIntent{Payload: Payload{Code: IntentNone}})) _, err = builder.Finish(selector) assert.NoError(t, err) } -func TestChangeSetBuilder_MustHaveAtLeastOnePayload(t *testing.T) { - builder := NewChangeSetBuilder() - err := builder.Start(ServerIntent{}) - assert.Error(t, err) - - err = builder.Start(ServerIntent{Payloads: []Payload{{Code: IntentNone}}}) - assert.NoError(t, err) - - err = builder.Start(ServerIntent{Payloads: []Payload{{Code: IntentNone}, {Code: IntentNone}, {Code: IntentNone}}}) - assert.NoError(t, err) -} - func TestChangeSetBuilder_Changes(t *testing.T) { builder := NewChangeSetBuilder() - err := builder.Start(ServerIntent{Payloads: []Payload{{Code: IntentTransferChanges}}}) + err := builder.Start(ServerIntent{Payload: Payload{Code: IntentTransferChanges}}) assert.NoError(t, err) builder.AddPut("foo", "bar", 1, []byte("baz")) @@ -52,12 +40,17 @@ func TestChangeSetBuilder_Changes(t *testing.T) { assert.Equal(t, Change{Action: ChangeTypePut, Kind: "foo", Key: "bar", Version: 1, Object: []byte("baz")}, changes[0]) assert.Equal(t, Change{Action: ChangeTypeDelete, Kind: "foo", Key: "bar", Version: 1}, changes[1]) - assert.Equal(t, IntentTransferChanges, changeSet.Intent().Payloads[0].Code) + assert.Equal(t, IntentTransferChanges, changeSet.IntentCode().Payload.Code) assert.Equal(t, selector, changeSet.Selector()) } -func TestChangeSetBuilder_ImplicitXferChanges(t *testing.T) { +// After receiving an intent, the SDK may receive 1 or more objects before receiving a payload-transferred. +// At that point, LaunchDarkly may send more objects followed by another payload-transferred. These objects +// should be regarded as part of an implicit "xfer-changes" intent, even though the server doesn't actually send one. +// If the server intends to use an xfer-full instead (for efficiency or other reasons), it will need to explicitly +// send one. +func TestChangeSetBuilder_ImplicitIntentXferChanges(t *testing.T) { } @@ -66,11 +59,10 @@ func TestChangeSetBuilder_NoChanges(t *testing.T) { changeSet := builder.NoChanges() assert.NotNil(t, changeSet) - intent := changeSet.Intent() + intent := changeSet.IntentCode() assert.NotNil(t, intent) - assert.NotEmpty(t, intent.Payloads) - assert.Equal(t, IntentNone, intent.Payloads[0].Code) + assert.Equal(t, IntentNone, intent.Payload.Code) assert.False(t, changeSet.Selector().IsDefined()) assert.Equal(t, NoSelector(), changeSet.Selector())