Skip to content

Commit 850a4c3

Browse files
authored
ISSUE-212 - Fix bug in persisting view properties while connecting to stream (#213)
* [ISSUE-212] Fix bug in accidentally persisting View partitions on stream connect * update javadoc
1 parent 488d35a commit 850a4c3

File tree

5 files changed

+113
-8
lines changed

5 files changed

+113
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
55
## 2.5.1 (05/19/2020)
66
- [ISSUE-209](https://github.com/SourceLabOrg/kafka-webview/issues/209) Expose HealthCheck and App Info endpoints without requiring authentication.
77
- [ISSUE-161](https://github.com/SourceLabOrg/kafka-webview/issues/161) Add dedicated 'Apply' and 'Reset' button to Partition and Record Filters.
8+
- [ISSUE-212](https://github.com/SourceLabOrg/kafka-webview/issues/212) Bugfix for partition filters being persisted when toggled on from `Stream` page.
89

910
#### Internal Dependency Updates
1011
-[PR-198](https://github.com/SourceLabOrg/kafka-webview/pull/198) Upgrade from SpringBoot 2.1.9 to 2.1.14.

kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/stream/StreamController.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@
4444
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
4545
import org.springframework.security.core.Authentication;
4646
import org.springframework.stereotype.Controller;
47+
import org.springframework.transaction.annotation.Transactional;
4748
import org.springframework.ui.Model;
4849
import org.springframework.web.bind.annotation.PathVariable;
4950
import org.springframework.web.bind.annotation.RequestMapping;
5051
import org.springframework.web.bind.annotation.RequestMethod;
5152
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
5253

53-
import javax.transaction.Transactional;
5454
import java.util.List;
5555
import java.util.Optional;
5656

@@ -118,7 +118,7 @@ public String stream(
118118
* Serves websocket requests, requesting to start a stream on the given view.
119119
*/
120120
@MessageMapping("/consume/{viewId}")
121-
@Transactional
121+
@Transactional(readOnly = true)
122122
public String newConsumer(
123123
@DestinationVariable final Long viewId,
124124
final ConsumeRequest consumeRequest,
@@ -153,7 +153,7 @@ public String newConsumer(
153153
* Serves web socket requests, requesting to pause a consumer.
154154
*/
155155
@MessageMapping("/pause/{viewId}")
156-
@Transactional
156+
@Transactional(readOnly = true)
157157
public String pauseConsumer(
158158
@DestinationVariable final Long viewId,
159159
final SimpMessageHeaderAccessor headerAccessor) {
@@ -169,7 +169,7 @@ public String pauseConsumer(
169169
* Serves web socket requests, requesting to resume a consumer.
170170
*/
171171
@MessageMapping("/resume/{viewId}")
172-
@Transactional
172+
@Transactional(readOnly = true)
173173
public String resumeConsumer(
174174
@DestinationVariable final Long viewId,
175175
final SimpMessageHeaderAccessor headerAccessor) {

kafka-webview-ui/src/main/resources/templates/stream/index.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,6 @@ <h4 class="alert-heading">Stream Disconnected</h4>
217217
// Merge parameters with start position parameters.
218218
jQuery.extend(params, SocketDetails.getStartPositionParams());
219219

220-
// TODO remove.
221-
console.log("Params: " + JSON.stringify(params));
222-
223220
// Send request down websocket.
224221
SocketDetails.stompClient.send(SocketDetails.getSubscribeUrl(), {}, JSON.stringify(params));
225222
},

kafka-webview-ui/src/test/java/org/sourcelab/kafka/webview/ui/controller/stream/StreamControllerTest.java

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,33 @@
2424

2525
package org.sourcelab.kafka.webview.ui.controller.stream;
2626

27+
import com.salesforce.kafka.test.junit4.SharedKafkaTestResource;
28+
import org.junit.Assert;
29+
import org.junit.ClassRule;
2730
import org.junit.Test;
2831
import org.junit.runner.RunWith;
2932
import org.sourcelab.kafka.webview.ui.controller.AbstractMvcTest;
33+
import org.sourcelab.kafka.webview.ui.controller.api.requests.ConsumeRequest;
34+
import org.sourcelab.kafka.webview.ui.manager.socket.WebSocketConsumersManager;
35+
import org.sourcelab.kafka.webview.ui.model.Cluster;
3036
import org.sourcelab.kafka.webview.ui.model.View;
37+
import org.sourcelab.kafka.webview.ui.repository.ViewRepository;
38+
import org.sourcelab.kafka.webview.ui.tools.ClusterTestTools;
3139
import org.sourcelab.kafka.webview.ui.tools.ViewTestTools;
3240
import org.springframework.beans.factory.annotation.Autowired;
3341
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
3442
import org.springframework.boot.test.context.SpringBootTest;
43+
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
44+
import org.springframework.security.core.Authentication;
3545
import org.springframework.test.context.junit4.SpringRunner;
3646
import org.springframework.transaction.annotation.Transactional;
3747

48+
import java.util.ArrayList;
49+
3850
import static org.hamcrest.Matchers.containsString;
51+
import static org.junit.Assert.assertEquals;
52+
import static org.mockito.Mockito.mock;
53+
import static org.mockito.Mockito.when;
3954
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
4055
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
4156
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -45,10 +60,24 @@
4560
@SpringBootTest
4661
@AutoConfigureMockMvc
4762
public class StreamControllerTest extends AbstractMvcTest {
63+
@ClassRule
64+
public static SharedKafkaTestResource sharedKafkaTestResource = new SharedKafkaTestResource();
4865

4966
@Autowired
5067
private ViewTestTools viewTestTools;
5168

69+
@Autowired
70+
private ViewRepository viewRepository;
71+
72+
@Autowired
73+
private ClusterTestTools clusterTestTools;
74+
75+
@Autowired
76+
private StreamController streamController;
77+
78+
@Autowired
79+
private WebSocketConsumersManager webSocketConsumersManager;
80+
5281
/**
5382
* Ensure authentication is required.
5483
*/
@@ -68,6 +97,7 @@ public void testUrlsRequireAuthentication() throws Exception {
6897
@Test
6998
@Transactional
7099
public void smokeTestStream() throws Exception {
100+
71101
final View view = viewTestTools.createView("TestView");
72102

73103
// Hit the stream page for specified view.
@@ -82,4 +112,68 @@ public void smokeTestStream() throws Exception {
82112
.andExpect(content().string(containsString("Switch to View")))
83113
.andExpect(content().string(containsString("href=\"/view/" + view.getId() + "\"")));
84114
}
115+
116+
/**
117+
* Test starting a new websocket consumer.
118+
* Verifies bug uncovered in ISSUE-212 https://github.com/SourceLabOrg/kafka-webview/issues/212
119+
*/
120+
@Test
121+
public void shouldReceiveAMessageFromTheServer() throws Exception {
122+
final String expectedSessionId = "MYSESSIONID";
123+
final String topicName = "TestTopic" + System.currentTimeMillis();
124+
125+
// Sanity test, no active consumers
126+
Assert.assertEquals("Should have no active consumers", 0, webSocketConsumersManager.countActiveConsumers());
127+
128+
// Create a topic
129+
sharedKafkaTestResource
130+
.getKafkaTestUtils()
131+
.createTopic(topicName, 2, (short) 1);
132+
133+
// Create cluster
134+
final Cluster cluster = clusterTestTools
135+
.createCluster("TestCluster", sharedKafkaTestResource.getKafkaConnectString());
136+
137+
// Create view
138+
final View view = viewTestTools
139+
.createViewWithCluster("TestView", cluster);
140+
141+
// Sanity test, no enforced partitions
142+
assertEquals("Partitions Property should be empty string", "", view.getPartitions());
143+
144+
final ConsumeRequest consumeRequest = new ConsumeRequest();
145+
consumeRequest.setAction("head");
146+
consumeRequest.setPartitions("0,1");
147+
consumeRequest.setFilters(new ArrayList<>());
148+
consumeRequest.setResultsPerPartition(10);
149+
150+
final SimpMessageHeaderAccessor mockHeaderAccessor = mock(SimpMessageHeaderAccessor.class);
151+
final Authentication mockPrincipal = mock(Authentication.class);
152+
when(mockHeaderAccessor.getUser())
153+
.thenReturn(mockPrincipal);
154+
when(mockPrincipal.getPrincipal())
155+
.thenReturn(nonAdminUserDetails);
156+
when(mockHeaderAccessor.getSessionId())
157+
.thenReturn(expectedSessionId);
158+
159+
try {
160+
final String result = streamController.newConsumer(
161+
view.getId(),
162+
consumeRequest,
163+
mockHeaderAccessor
164+
);
165+
assertEquals("Should return success response", "{success: true}", result);
166+
167+
// Verify consumer stood up
168+
assertEquals("Should have one active consumer", 1, webSocketConsumersManager.countActiveConsumers());
169+
170+
// Lets refresh the view entity and verify the partitions field is still empty.
171+
// Validates ISSUE-212
172+
final View updatedView = viewRepository.findById(view.getId()).get();
173+
assertEquals("Partitions Property should be empty string", "", updatedView.getPartitions());
174+
} finally {
175+
// Cleanup, disconnect websocket consumers
176+
webSocketConsumersManager.removeConsumersForSessionId(expectedSessionId);
177+
}
178+
}
85179
}

kafka-webview-ui/src/test/java/org/sourcelab/kafka/webview/ui/tools/ViewTestTools.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.beans.factory.annotation.Autowired;
3232
import org.springframework.stereotype.Component;
3333

34+
import javax.persistence.EntityManager;
3435
import java.sql.Timestamp;
3536

3637
/**
@@ -47,15 +48,18 @@ public class ViewTestTools {
4748
private final ViewRepository viewRepository;
4849
private final ClusterTestTools clusterTestTools;
4950
private final MessageFormatTestTools messageFormatTestTools;
51+
private final EntityManager entityManager;
5052

5153
@Autowired
5254
public ViewTestTools(
5355
final ViewRepository viewRepository,
5456
final ClusterTestTools clusterTestTools,
55-
final MessageFormatTestTools messageFormatTestTools) {
57+
final MessageFormatTestTools messageFormatTestTools,
58+
final EntityManager entityManager) {
5659
this.viewRepository = viewRepository;
5760
this.clusterTestTools = clusterTestTools;
5861
this.messageFormatTestTools = messageFormatTestTools;
62+
this.entityManager = entityManager;
5963
}
6064

6165
public View createView(final String name) {
@@ -118,4 +122,13 @@ public void save(final View view) {
118122
viewRepository.save(view);
119123
}
120124

125+
/**
126+
* Refresh/reload entity from Database.
127+
* @param view View instance to reload.
128+
* @return View instance.
129+
*/
130+
public View reload(final View view) {
131+
entityManager.refresh(view);
132+
return view;
133+
}
121134
}

0 commit comments

Comments
 (0)