diff --git a/device/device.go b/device/device.go index 32467c2d..ba76e762 100644 --- a/device/device.go +++ b/device/device.go @@ -100,6 +100,9 @@ type Interface interface { // CloseReason returns the metadata explaining why a device was closed. If this device // is not closed, this method's return is undefined. CloseReason() CloseReason + + // IntermediateContext returns any additional information if sent by the device. + IntermediateContext() string } // device is the internal Interface implementation. This type holds the internal @@ -124,6 +127,8 @@ type device struct { metadata *Metadata closeReason atomic.Value + + intermediateContext string } type deviceOptions struct { @@ -320,3 +325,7 @@ func (d *device) CloseReason() CloseReason { return CloseReason{} } + +func (d *device) IntermediateContext() string { + return d.intermediateContext +} diff --git a/device/device_test.go b/device/device_test.go index 5a3ec0cd..387d1dfa 100644 --- a/device/device_test.go +++ b/device/device_test.go @@ -110,3 +110,75 @@ func TestDevice(t *testing.T) { assert.Error(err) } } + +func TestDevice_IntermediateContext(t *testing.T) { + tests := []struct { + name string + intermediateContext string + expectedIntermediateContext string + }{ + { + name: "empty intermediate context", + intermediateContext: "", + expectedIntermediateContext: "", + }, + { + name: "non-empty intermediate context", + intermediateContext: "some-context-value", + expectedIntermediateContext: "some-context-value", + }, + { + name: "intermediate context with special characters", + intermediateContext: "context/with/special-chars_123", + expectedIntermediateContext: "context/with/special-chars_123", + }, + { + name: "intermediate context with JSON-like content", + intermediateContext: `{"key": "value", "nested": {"data": 123}}`, + expectedIntermediateContext: `{"key": "value", "nested": {"data": 123}}`, + }, + { + name: "intermediate context with whitespace", + intermediateContext: " spaced context ", + expectedIntermediateContext: " spaced context ", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + device := newDevice(deviceOptions{ + ID: ID("test-device"), + QueueSize: 10, + Logger: sallust.Default(), + Metadata: new(Metadata), + }) + require.NotNil(device) + + // Set the intermediateContext field directly since it's an internal field + device.intermediateContext = tc.intermediateContext + + assert.Equal(tc.expectedIntermediateContext, device.IntermediateContext()) + }) + } +} + +func TestDevice_IntermediateContext_Default(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + // Create a device without setting intermediateContext + device := newDevice(deviceOptions{ + ID: ID("test-device"), + QueueSize: 10, + Logger: sallust.Default(), + Metadata: new(Metadata), + }) + require.NotNil(device) + + // Default value should be empty string + assert.Empty(device.IntermediateContext()) + assert.Equal("", device.IntermediateContext()) +} diff --git a/device/manager.go b/device/manager.go index a218e83d..400eba2b 100644 --- a/device/manager.go +++ b/device/manager.go @@ -212,6 +212,7 @@ func (m *manager) Connect(response http.ResponseWriter, request *http.Request, r Metadata: metadata, Logger: m.logger, }) + d.intermediateContext = request.Header.Get("X-Intermediate-Context") if allow, matchResults := m.filter.AllowConnection(d); !allow { d.logger.Info("filter match found", zap.String("location", matchResults.Location), zap.String("key", matchResults.Key)) diff --git a/device/mocks.go b/device/mocks.go index 2c4f7763..9492895e 100644 --- a/device/mocks.go +++ b/device/mocks.go @@ -137,6 +137,10 @@ func (m *MockDevice) CloseReason() CloseReason { return first } +func (m *MockDevice) IntermediateContext() string { + return "" +} + func (m *MockDevice) Send(request *Request) (*Response, error) { // nolint: typecheck arguments := m.Called(request)