Skip to content

Commit

Permalink
GH-83: Generate delete event event on system.reset with notFound resp…
Browse files Browse the repository at this point in the history
…onse.
  • Loading branch information
jirenius committed Oct 28, 2019
1 parent ce74a4b commit 7932b7e
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 45 deletions.
14 changes: 12 additions & 2 deletions server/rescache/resourceSubscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"

"github.com/resgateio/resgate/server/codec"
"github.com/resgateio/resgate/server/reserr"
)

type subscriptionState byte
Expand Down Expand Up @@ -456,8 +457,17 @@ func (rs *ResourceSubscription) processResetGetResponse(payload []byte, err erro

// Get request failed
if err != nil {
// [TODO] Delete the resource
rs.e.cache.Errorf("Subscription %s: Reset get error - %s", rs.e.ResourceName, err)
// In case of a system.notFound error,
// a delete event is generated. Otherwise we
// just log the error.
if reserr.IsError(err, reserr.CodeNotFound) {
r := &ResourceEvent{
Event: "delete",
}
rs.handleEvent(r)
} else {
rs.e.cache.Errorf("Subscription %s: Reset get error - %s", rs.e.ResourceName, err)
}
return
}

Expand Down
9 changes: 9 additions & 0 deletions server/reserr/reserr.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ func InternalError(err error) *Error {
return &Error{Code: CodeInternalError, Message: "Internal error: " + err.Error()}
}

// IsError returns true if the error is an Error with the given error code.
func IsError(err error, code string) bool {
rerr, ok := err.(*Error)
if !ok {
return false
}
return rerr.Code == code
}

// Pre-defined RES error codes
const (
CodeAccessDenied = "system.accessDenied"
Expand Down
20 changes: 8 additions & 12 deletions test/07model_event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ func TestChangeEventOnCachedModel(t *testing.T) {
// Test change event with new resource reference
func TestChangeEventWithNewResourceReference(t *testing.T) {
collection := resourceData("test.collection")
customEvent := json.RawMessage(`{"foo":"bar"}`)

runTest(t, func(s *Session) {
c := s.Connect()
Expand All @@ -127,37 +126,34 @@ func TestChangeEventWithNewResourceReference(t *testing.T) {
c.GetEvent(t).Equals(t, "test.model.change", json.RawMessage(`{"values":{"ref":{"rid":"test.collection"}},"collections":{"test.collection":`+collection+`}}`))

// Send event on collection and validate client event
s.ResourceEvent("test.collection", "custom", customEvent)
c.GetEvent(t).Equals(t, "test.collection.custom", customEvent)
s.ResourceEvent("test.collection", "custom", common.CustomEvent())
c.GetEvent(t).Equals(t, "test.collection.custom", common.CustomEvent())
})
}

// Test change event with removed resource reference
func TestChangeEventWithRemovedResourceReference(t *testing.T) {
customEvent := json.RawMessage(`{"foo":"bar"}`)

runTest(t, func(s *Session) {
c := s.Connect()
subscribeToTestModelParent(t, s, c, false)

// Send event on model and validate client event
s.ResourceEvent("test.model", "custom", customEvent)
c.GetEvent(t).Equals(t, "test.model.custom", customEvent)
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent())

// Send event on model and validate client event
s.ResourceEvent("test.model.parent", "change", json.RawMessage(`{"values":{"child":null}}`))
c.GetEvent(t).Equals(t, "test.model.parent.change", json.RawMessage(`{"values":{"child":null}}`))

// Send event on collection and validate client event is not sent to client
s.ResourceEvent("test.model", "custom", customEvent)
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.AssertNoEvent(t, "test.model")
})
}

// Test change event with new resource reference
func TestChangeEventWithChangedResourceReference(t *testing.T) {
collection := resourceData("test.collection")
customEvent := json.RawMessage(`{"foo":"bar"}`)

runTest(t, func(s *Session) {
c := s.Connect()
Expand All @@ -175,11 +171,11 @@ func TestChangeEventWithChangedResourceReference(t *testing.T) {
c.GetEvent(t).Equals(t, "test.model.parent.change", json.RawMessage(`{"values":{"child":{"rid":"test.collection"}},"collections":{"test.collection":`+collection+`}}`))

// Send event on collection and validate client event
s.ResourceEvent("test.collection", "custom", customEvent)
c.GetEvent(t).Equals(t, "test.collection.custom", customEvent)
s.ResourceEvent("test.collection", "custom", common.CustomEvent())
c.GetEvent(t).Equals(t, "test.collection.custom", common.CustomEvent())

// Send event on model and validate no event is sent to client
s.ResourceEvent("test.model", "custom", customEvent)
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.AssertNoEvent(t, "test.model")
})
}
13 changes: 5 additions & 8 deletions test/08collection_event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ func TestAddRemoveEventsOnCachedCollection(t *testing.T) {
// Test add event with new resource reference
func TestAddEventWithNewResourceReference(t *testing.T) {
model := resourceData("test.model")
customEvent := json.RawMessage(`{"foo":"bar"}`)

runTest(t, func(s *Session) {

Expand All @@ -91,29 +90,27 @@ func TestAddEventWithNewResourceReference(t *testing.T) {
c.GetEvent(t).Equals(t, "test.collection.add", json.RawMessage(`{"idx":1,"value":{"rid":"test.model"},"models":{"test.model":`+model+`}}`))

// Send event on model and validate client event
s.ResourceEvent("test.model", "custom", customEvent)
c.GetEvent(t).Equals(t, "test.model.custom", customEvent)
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent())
})
}

// Test remove event with removed resource reference
func TestRemoveEventWithRemovedResourceReference(t *testing.T) {
customEvent := json.RawMessage(`{"foo":"bar"}`)

runTest(t, func(s *Session) {
c := s.Connect()
subscribeToTestCollectionParent(t, s, c, false)

// Send event on collection and validate client event
s.ResourceEvent("test.collection", "custom", customEvent)
c.GetEvent(t).Equals(t, "test.collection.custom", customEvent)
s.ResourceEvent("test.collection", "custom", common.CustomEvent())
c.GetEvent(t).Equals(t, "test.collection.custom", common.CustomEvent())

// Send event on collection and validate client event
s.ResourceEvent("test.collection.parent", "remove", json.RawMessage(`{"idx":1}`))
c.GetEvent(t).Equals(t, "test.collection.parent.remove", json.RawMessage(`{"idx":1}`))

// Send event on collection and validate client event is not sent to client
s.ResourceEvent("test.collection", "custom", customEvent)
s.ResourceEvent("test.collection", "custom", common.CustomEvent())
c.AssertNoEvent(t, "test.collection")
})
}
74 changes: 74 additions & 0 deletions test/11system_event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"testing"

"github.com/resgateio/resgate/server/reserr"
)

// Test system reset event
Expand Down Expand Up @@ -207,3 +209,75 @@ func TestSystemResetTriggersGetRequestOnQueryModel(t *testing.T) {
})
}
}

func TestSystemReset_NotFoundResponseOnModel_GeneratesDeleteEvent(t *testing.T) {
runTest(t, func(s *Session) {
c := s.Connect()
// Get model
subscribeToTestModel(t, s, c)
// Send system reset
s.SystemEvent("reset", json.RawMessage(`{"resources":["test.>"]}`))
// Respond to get request with system.notFound error
s.GetRequest(t).AssertSubject(t, "get.test.model").RespondError(reserr.ErrNotFound)
// Validate delete event is sent to client
c.GetEvent(t).AssertEventName(t, "test.model.delete").AssertData(t, nil)
// Validate subsequent events are not sent to client
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.AssertNoEvent(t, "test.model")
})
}

func TestSystemReset_NotFoundResponseOnCollection_GeneratesDeleteEvent(t *testing.T) {
runTest(t, func(s *Session) {
c := s.Connect()
// Get model
subscribeToTestCollection(t, s, c)
// Send system reset
s.SystemEvent("reset", json.RawMessage(`{"resources":["test.>"]}`))
// Respond to get request with system.notFound error
s.GetRequest(t).AssertSubject(t, "get.test.collection").RespondError(reserr.ErrNotFound)
// Validate delete event is sent to client
c.GetEvent(t).AssertEventName(t, "test.collection.delete").AssertData(t, nil) // Send custom event on collection and validate no event
// Validate subsequent events are not sent to client
s.ResourceEvent("test.collection", "custom", common.CustomEvent())
c.AssertNoEvent(t, "test.collection")
})
}

func TestSystemReset_InternalErrorResponseOnModel_LogsError(t *testing.T) {
runTest(t, func(s *Session) {
c := s.Connect()
// Get model
subscribeToTestModel(t, s, c)
// Send system reset
s.SystemEvent("reset", json.RawMessage(`{"resources":["test.>"]}`))
// Respond to get request with system.notFound error
s.GetRequest(t).AssertSubject(t, "get.test.model").RespondError(reserr.ErrInternalError)
// Validate no delete event is sent to client
c.AssertNoEvent(t, "test.model")
// Validate subsequent events are sent to client
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent())
// Assert error is logged
s.AssertErrorsLogged(t, 1)
})
}

func TestSystemReset_InternalErrorResponseOnCollection_LogsError(t *testing.T) {
runTest(t, func(s *Session) {
c := s.Connect()
// Get collection
subscribeToTestCollection(t, s, c)
// Send system reset
s.SystemEvent("reset", json.RawMessage(`{"resources":["test.>"]}`))
// Respond to get request with system.notFound error
s.GetRequest(t).AssertSubject(t, "get.test.collection").RespondError(reserr.ErrInternalError)
// Validate no delete event is sent to client
c.AssertNoEvent(t, "test.collection")
// Validate subsequent events are sent to client
s.ResourceEvent("test.collection", "custom", common.CustomEvent())
c.GetEvent(t).Equals(t, "test.collection.custom", common.CustomEvent())
// Assert error is logged
s.AssertErrorsLogged(t, 1)
})
}
32 changes: 9 additions & 23 deletions test/29delete_event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ func TestDeleteEvent_OnModel_SentToClient(t *testing.T) {

// Validate the delete event is sent to client
c.GetEvent(t).Equals(t, "test.model.delete", nil)
s.AssertNoErrorsLogged(t)
})
}

Expand All @@ -29,45 +28,38 @@ func TestDeleteEvent_OnCollection_SentToClient(t *testing.T) {

// Validate the delete event is sent to client
c.GetEvent(t).Equals(t, "test.collection.delete", nil)
s.AssertNoErrorsLogged(t)
})
}

func TestDeleteEvent_AndCustomEventOnModel_CustomEventNotSentToClient(t *testing.T) {
customEvent := json.RawMessage(`{"foo":"bar"}`)
runTest(t, func(s *Session) {
c := s.Connect()
subscribeToTestModel(t, s, c)
// Send delete event
s.ResourceEvent("test.model", "delete", nil)
c.GetEvent(t).Equals(t, "test.model.delete", nil)
// Send custom event on model and validate no event
s.ResourceEvent("test.model", "custom", customEvent)
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.AssertNoEvent(t, "test.model")
s.AssertNoErrorsLogged(t)
})
}

func TestDeleteEvent_AndCustomEventOnCollection_CustomEventNotSentToClient(t *testing.T) {
customEvent := json.RawMessage(`{"foo":"bar"}`)
runTest(t, func(s *Session) {
c := s.Connect()
subscribeToTestCollection(t, s, c)
// Send delete event
s.ResourceEvent("test.collection", "delete", nil)
c.GetEvent(t).Equals(t, "test.collection.delete", nil)
// Send custom event on collection and validate no event
s.ResourceEvent("test.collection", "custom", customEvent)
s.ResourceEvent("test.collection", "custom", common.CustomEvent())
c.AssertNoEvent(t, "test.collection")
s.AssertNoErrorsLogged(t)
})
}

func TestDeleteEvent_PriorToGetResponse_IsDiscarded(t *testing.T) {
runTest(t, func(s *Session) {
model := resourceData("test.model")
customEvent := json.RawMessage(`{"foo":"bar"}`)

c := s.Connect()

// Send subscribe request
Expand All @@ -83,15 +75,13 @@ func TestDeleteEvent_PriorToGetResponse_IsDiscarded(t *testing.T) {
creq.GetResponse(t)
c.AssertNoEvent(t, "test.model")
// Send event on model and validate it is sent
s.ResourceEvent("test.model", "custom", customEvent)
c.GetEvent(t).Equals(t, "test.model.custom", customEvent)
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent())
})
}

func TestDeleteEvent_FollowedBySubscribe_IsNotCached(t *testing.T) {
runTest(t, func(s *Session) {
customEvent := json.RawMessage(`{"foo":"bar"}`)

c1 := s.Connect()
c2 := s.Connect()

Expand All @@ -105,17 +95,14 @@ func TestDeleteEvent_FollowedBySubscribe_IsNotCached(t *testing.T) {
// Subscribe with second client
subscribeToTestModel(t, s, c2)
// Send custom event
s.ResourceEvent("test.model", "custom", customEvent)
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c1.AssertNoEvent(t, "test.model")
c2.GetEvent(t).Equals(t, "test.model.custom", customEvent)
s.AssertNoErrorsLogged(t)
c2.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent())
})
}

func TestDeleteEvent_FollowedByResubscribe_IsNotCached(t *testing.T) {
runTest(t, func(s *Session) {
customEvent := json.RawMessage(`{"foo":"bar"}`)

c := s.Connect()

// Subscribe with first client
Expand All @@ -125,15 +112,14 @@ func TestDeleteEvent_FollowedByResubscribe_IsNotCached(t *testing.T) {
// Validate the delete event is sent to client
c.GetEvent(t).Equals(t, "test.model.delete", nil)
// Send custom event and assert event not sent to client
s.ResourceEvent("test.model", "custom", customEvent)
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.AssertNoEvent(t, "test.model")
// Resubscribe
creq := c.Request("unsubscribe.test.model", nil)
creq.GetResponse(t)
subscribeToTestModel(t, s, c)
// Send custom event and assert event is sent to client
s.ResourceEvent("test.model", "custom", customEvent)
c.GetEvent(t).Equals(t, "test.model.custom", customEvent)
s.AssertNoErrorsLogged(t)
s.ResourceEvent("test.model", "custom", common.CustomEvent())
c.GetEvent(t).Equals(t, "test.model.custom", common.CustomEvent())
})
}
6 changes: 6 additions & 0 deletions test/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import (
"testing"
)

type commonData struct{}

var common = commonData{}

func (c *commonData) CustomEvent() json.RawMessage { return json.RawMessage(`{"foo":"bar"}`) }

// subscribeToTestModel makes a successful subscription to test.model
// Returns the connection ID (cid)
func subscribeToTestModel(t *testing.T, s *Session, c *Conn) string {
Expand Down

0 comments on commit 7932b7e

Please sign in to comment.