Skip to content

Commit

Permalink
Allow multiple write callbacks per feature
Browse files Browse the repository at this point in the history
  • Loading branch information
DerAndereAndi committed May 16, 2024
1 parent ad75244 commit b3c1a8a
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 13 deletions.
4 changes: 2 additions & 2 deletions api/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ type FeatureLocalInterface interface {
AddResponseCallback(msgCounterReference model.MsgCounterType, function func(msg ResponseMessage)) error
// Add a callback function to be invoked when a result message comes in for this feature
AddResultCallback(function func(msg ResponseMessage))
// Sets the callback method for a server feature which is invoked to
// Add a callback method for a server feature which is invoked to
// check wether an incoming write message shall be permitted or declined
SetWriteApprovalCallback(function WriteApprovalCallbackFunc) error
AddWriteApprovalCallback(function WriteApprovalCallbackFunc) error
// Needs to be invoked within 1 minute of WritePermissionCheckCallbackFunc of
// SetWritePermissionCheckCallback being invoked by the stack returning wether
// the remote requested write command shall be allowed or not
Expand Down
17 changes: 13 additions & 4 deletions spine/feature_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type FeatureLocal struct {
responseCallbacks []func(result api.ResponseMessage)

writeTimeout time.Duration
writeApprovalCallback api.WriteApprovalCallbackFunc
writeApprovalCallback []api.WriteApprovalCallbackFunc
pendingWriteApprovals map[model.MsgCounterType]*time.Timer

bindings []*model.FeatureAddressType // bindings to remote features
Expand Down Expand Up @@ -141,19 +141,28 @@ func (r *FeatureLocal) processResultCallbacks(msg api.ResponseMessage) {
}
}

func (r *FeatureLocal) SetWriteApprovalCallback(function api.WriteApprovalCallbackFunc) error {
func (r *FeatureLocal) AddWriteApprovalCallback(function api.WriteApprovalCallbackFunc) error {
if r.Role() != model.RoleTypeServer {
return errors.New("only allowed on a server feature")
}

r.muxResponseCB.Lock()
defer r.muxResponseCB.Unlock()

r.writeApprovalCallback = function
r.writeApprovalCallback = append(r.writeApprovalCallback, function)

return nil
}

func (r *FeatureLocal) processWriteApprovalCallbacks(msg *api.Message) {
r.muxResponseCB.Lock()
defer r.muxResponseCB.Unlock()

for _, cb := range r.writeApprovalCallback {
go cb(msg)
}
}

func (r *FeatureLocal) addPendingApproval(msg *api.Message) {
if r.Role() != model.RoleTypeServer || msg.RequestHeader == nil || msg.RequestHeader.MsgCounter == nil {
return
Expand Down Expand Up @@ -459,7 +468,7 @@ func (r *FeatureLocal) HandleMessage(message *api.Message) *model.ErrorType {
// if there is a write permission check callback set, invoke this instead of directly allowing the write
if r.writeApprovalCallback != nil {
r.addPendingApproval(message)
r.writeApprovalCallback(message)
r.processWriteApprovalCallbacks(message)
} else {
// this method handles ack and error results, so no need to return an error
r.processWrite(message)
Expand Down
20 changes: 13 additions & 7 deletions spine/feature_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,14 +377,14 @@ func (suite *DeviceClassificationTestSuite) Test_Write() {

func (suite *DeviceClassificationTestSuite) Test_SetWriteApprovalCallback_Invalid() {
cb := func(msg *api.Message) {}
err := suite.localFeature.SetWriteApprovalCallback(cb)
err := suite.localFeature.AddWriteApprovalCallback(cb)
assert.NotNil(suite.T(), err)
suite.localFeature.ApproveOrDenyWrite(&api.Message{}, true, "")
}

func (suite *DeviceClassificationTestSuite) Test_AddPendingApproval_Invalid() {
cb := func(msg *api.Message) {}
err := suite.localServerFeatureWrite.SetWriteApprovalCallback(cb)
err := suite.localServerFeatureWrite.AddWriteApprovalCallback(cb)
assert.Nil(suite.T(), err)

msg := &api.Message{
Expand Down Expand Up @@ -415,7 +415,7 @@ func (suite *DeviceClassificationTestSuite) Test_Write_Callback() {
suite.localServerFeatureWrite.ApproveOrDenyWrite(msg, true, "")
}

suite.localServerFeatureWrite.SetWriteApprovalCallback(cb)
suite.localServerFeatureWrite.AddWriteApprovalCallback(cb)
err := suite.localServerFeatureWrite.HandleMessage(msg)
assert.Nil(suite.T(), err)

Expand All @@ -425,13 +425,16 @@ func (suite *DeviceClassificationTestSuite) Test_Write_Callback() {
suite.localServerFeatureWrite.ApproveOrDenyWrite(msg, false, "not allowed by application")
}

suite.localServerFeatureWrite.SetWriteApprovalCallback(cb)
suite.localServerFeatureWrite.AddWriteApprovalCallback(cb)
err = suite.localServerFeatureWrite.HandleMessage(msg)
assert.Nil(suite.T(), err)

// callback is called asynchronously
time.Sleep(time.Millisecond * 200)
}

func (suite *DeviceClassificationTestSuite) Test_Write_Callback_Timeout() {
suite.localServerFeatureWrite.SetWriteApprovalTimeout(time.Second * 1)
suite.localServerFeatureWrite.SetWriteApprovalTimeout(time.Millisecond * 500)

suite.senderMock.EXPECT().ResultError(mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()

Expand All @@ -447,11 +450,14 @@ func (suite *DeviceClassificationTestSuite) Test_Write_Callback_Timeout() {
}

cb := func(msg *api.Message) {
time.Sleep(time.Second * 2)
time.Sleep(time.Second * 1)
suite.localServerFeatureWrite.ApproveOrDenyWrite(msg, true, "")
}

suite.localServerFeatureWrite.SetWriteApprovalCallback(cb)
suite.localServerFeatureWrite.AddWriteApprovalCallback(cb)
err := suite.localServerFeatureWrite.HandleMessage(msg)
assert.Nil(suite.T(), err)

// callback is called asynchronously
time.Sleep(time.Second * 1)
}

0 comments on commit b3c1a8a

Please sign in to comment.