diff --git a/api/function.go b/api/function.go index 263a025..2083139 100644 --- a/api/function.go +++ b/api/function.go @@ -21,6 +21,8 @@ type FunctionDataCmdInterface interface { type FunctionDataInterface interface { // Get the function type FunctionType() model.FunctionType + // Return if this function supports partial writes + SupportsPartialWrite() bool // Get a copy of the functions data DataCopyAny() any // Update the functions data diff --git a/mocks/FunctionDataCmdInterface.go b/mocks/FunctionDataCmdInterface.go index 1e521f3..533cfb9 100644 --- a/mocks/FunctionDataCmdInterface.go +++ b/mocks/FunctionDataCmdInterface.go @@ -254,6 +254,51 @@ func (_c *FunctionDataCmdInterface_ReplyCmdType_Call) RunAndReturn(run func(bool return _c } +// SupportsPartialWrite provides a mock function with given fields: +func (_m *FunctionDataCmdInterface) SupportsPartialWrite() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for SupportsPartialWrite") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// FunctionDataCmdInterface_SupportsPartialWrite_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SupportsPartialWrite' +type FunctionDataCmdInterface_SupportsPartialWrite_Call struct { + *mock.Call +} + +// SupportsPartialWrite is a helper method to define mock.On call +func (_e *FunctionDataCmdInterface_Expecter) SupportsPartialWrite() *FunctionDataCmdInterface_SupportsPartialWrite_Call { + return &FunctionDataCmdInterface_SupportsPartialWrite_Call{Call: _e.mock.On("SupportsPartialWrite")} +} + +func (_c *FunctionDataCmdInterface_SupportsPartialWrite_Call) Run(run func()) *FunctionDataCmdInterface_SupportsPartialWrite_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *FunctionDataCmdInterface_SupportsPartialWrite_Call) Return(_a0 bool) *FunctionDataCmdInterface_SupportsPartialWrite_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *FunctionDataCmdInterface_SupportsPartialWrite_Call) RunAndReturn(run func() bool) *FunctionDataCmdInterface_SupportsPartialWrite_Call { + _c.Call.Return(run) + return _c +} + // UpdateDataAny provides a mock function with given fields: remoteWrite, data, filterPartial, filterDelete func (_m *FunctionDataCmdInterface) UpdateDataAny(remoteWrite bool, data interface{}, filterPartial *model.FilterType, filterDelete *model.FilterType) *model.ErrorType { ret := _m.Called(remoteWrite, data, filterPartial, filterDelete) diff --git a/mocks/FunctionDataInterface.go b/mocks/FunctionDataInterface.go index 2bf05c2..956dbfd 100644 --- a/mocks/FunctionDataInterface.go +++ b/mocks/FunctionDataInterface.go @@ -112,6 +112,51 @@ func (_c *FunctionDataInterface_FunctionType_Call) RunAndReturn(run func() model return _c } +// SupportsPartialWrite provides a mock function with given fields: +func (_m *FunctionDataInterface) SupportsPartialWrite() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for SupportsPartialWrite") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// FunctionDataInterface_SupportsPartialWrite_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SupportsPartialWrite' +type FunctionDataInterface_SupportsPartialWrite_Call struct { + *mock.Call +} + +// SupportsPartialWrite is a helper method to define mock.On call +func (_e *FunctionDataInterface_Expecter) SupportsPartialWrite() *FunctionDataInterface_SupportsPartialWrite_Call { + return &FunctionDataInterface_SupportsPartialWrite_Call{Call: _e.mock.On("SupportsPartialWrite")} +} + +func (_c *FunctionDataInterface_SupportsPartialWrite_Call) Run(run func()) *FunctionDataInterface_SupportsPartialWrite_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *FunctionDataInterface_SupportsPartialWrite_Call) Return(_a0 bool) *FunctionDataInterface_SupportsPartialWrite_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *FunctionDataInterface_SupportsPartialWrite_Call) RunAndReturn(run func() bool) *FunctionDataInterface_SupportsPartialWrite_Call { + _c.Call.Return(run) + return _c +} + // UpdateDataAny provides a mock function with given fields: remoteWrite, data, filterPartial, filterDelete func (_m *FunctionDataInterface) UpdateDataAny(remoteWrite bool, data interface{}, filterPartial *model.FilterType, filterDelete *model.FilterType) *model.ErrorType { ret := _m.Called(remoteWrite, data, filterPartial, filterDelete) diff --git a/spine/feature_local.go b/spine/feature_local.go index feadfa7..246f449 100644 --- a/spine/feature_local.go +++ b/spine/feature_local.go @@ -76,7 +76,15 @@ func (r *FeatureLocal) AddFunctionType(function model.FunctionType, read, write if r.operations[function] != nil { return } - r.operations[function] = NewOperations(read, false, write, write) + writePartial := false + if write { + // partials are not supported on all features and functions, so check if this function supports it + if fctData := r.functionData(function); fctData != nil { + writePartial = fctData.SupportsPartialWrite() + } + } + // partial reads are currently not supported! + r.operations[function] = NewOperations(read, false, write, writePartial) if r.role == model.RoleTypeServer && r.ftype == model.FeatureTypeTypeDeviceDiagnosis && diff --git a/spine/function_data.go b/spine/function_data.go index 71a375a..5e679e8 100644 --- a/spine/function_data.go +++ b/spine/function_data.go @@ -31,6 +31,10 @@ func (r *FunctionData[T]) FunctionType() model.FunctionType { return r.functionType } +func (r *FunctionData[T]) SupportsPartialWrite() bool { + return util.Implements[T, model.Updater]() +} + func (r *FunctionData[T]) DataCopy() *T { r.mux.Lock() defer r.mux.Unlock() @@ -58,8 +62,7 @@ func (r *FunctionData[T]) UpdateData(remoteWrite bool, newData *T, filterPartial return nil } - supported := util.Implements[T, model.Updater]() - if !supported { + if !r.SupportsPartialWrite() { return model.NewErrorTypeFromString(fmt.Sprintf("partial updates are not supported for type '%s'", util.Type[T]().Name())) } diff --git a/spine/function_data_test.go b/spine/function_data_test.go index cf748e6..edf85fd 100644 --- a/spine/function_data_test.go +++ b/spine/function_data_test.go @@ -82,6 +82,14 @@ func TestFunctionData_UpdateDataPartial_Supported(t *testing.T) { functionType := model.FunctionTypeHvacOverrunListData sut := NewFunctionData[model.HvacOverrunListDataType](functionType) + ok := sut.SupportsPartialWrite() + assert.True(t, ok) + err := sut.UpdateData(false, newData, &model.FilterType{CmdControl: &model.CmdControlType{Partial: &model.ElementTagType{}}}, nil) assert.Nil(t, err) + + functionType = model.FunctionTypeNetworkManagementAddNodeCall + sut2 := NewFunctionData[model.NetworkManagementAddNodeCallType](functionType) + ok = sut2.SupportsPartialWrite() + assert.False(t, ok) }