Skip to content

Commit acff1ec

Browse files
SLCORE-1521 Add telemetry for binding suggestion
1 parent 0d28247 commit acff1ec

File tree

10 files changed

+40
-9
lines changed

10 files changed

+40
-9
lines changed

backend/core/src/main/java/org/sonarsource/sonarlint/core/BindingSuggestionProvider.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingSuggestionDto;
5050
import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.SuggestBindingParams;
5151
import org.sonarsource.sonarlint.core.serverapi.ServerApi;
52+
import org.sonarsource.sonarlint.core.telemetry.TelemetryService;
5253
import org.springframework.context.event.EventListener;
5354

5455
import static java.lang.String.join;
@@ -69,17 +70,19 @@ public class BindingSuggestionProvider {
6970
private final AtomicBoolean enabled = new AtomicBoolean(true);
7071
private final SonarQubeClientManager sonarQubeClientManager;
7172
private final ClientFileSystemService clientFs;
73+
private final TelemetryService telemetryService;
7274

7375
@Inject
7476
public BindingSuggestionProvider(ConfigurationRepository configRepository, ConnectionConfigurationRepository connectionRepository, SonarLintRpcClient client,
75-
BindingClueProvider bindingClueProvider, SonarProjectsCache sonarProjectsCache, SonarQubeClientManager sonarQubeClientManager, ClientFileSystemService clientFs) {
77+
BindingClueProvider bindingClueProvider, SonarProjectsCache sonarProjectsCache, SonarQubeClientManager sonarQubeClientManager, ClientFileSystemService clientFs, TelemetryService telemetryService) {
7678
this.configRepository = configRepository;
7779
this.connectionRepository = connectionRepository;
7880
this.client = client;
7981
this.bindingClueProvider = bindingClueProvider;
8082
this.sonarProjectsCache = sonarProjectsCache;
8183
this.sonarQubeClientManager = sonarQubeClientManager;
8284
this.clientFs = clientFs;
85+
this.telemetryService = telemetryService;
8386
this.executorService = new ExecutorServiceShutdownWatchable<>(FailSafeExecutors.newSingleThreadExecutor("Binding Suggestion Provider"));
8487
}
8588

@@ -192,6 +195,9 @@ private List<BindingSuggestionDto> suggestBindingForEligibleScope(String checked
192195

193196
if (suggestions.isEmpty()) {
194197
searchByRemoteUrlInConnections(suggestions, checkedConfigScopeId, candidateConnectionIds, cancelMonitor);
198+
if (!suggestions.isEmpty()) {
199+
telemetryService.suggestedRemoteBinding();
200+
}
195201
}
196202

197203
return suggestions;

backend/core/src/main/java/org/sonarsource/sonarlint/core/telemetry/TelemetryService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ public void exportedConnectedMode() {
234234
updateTelemetry(TelemetryLocalStorage::incrementExportedConnectedModeCount);
235235
}
236236

237+
public void suggestedRemoteBinding() {
238+
updateTelemetry(TelemetryLocalStorage::incrementSuggestedRemoteBindingsCount);
239+
}
240+
241+
237242
public void toolCalled(ToolCalledParams params) {
238243
updateTelemetry(storage -> storage.incrementToolCalledCount(params.getToolName(), params.isSucceeded()));
239244
}

backend/core/src/test/java/org/sonarsource/sonarlint/core/BindingSuggestionProviderTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingSuggestionDto;
4949
import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.SuggestBindingParams;
5050
import org.sonarsource.sonarlint.core.serverapi.component.ServerProject;
51+
import org.sonarsource.sonarlint.core.telemetry.TelemetryService;
5152

5253
import static org.assertj.core.api.Assertions.assertThat;
5354
import static org.assertj.core.api.Assertions.tuple;
@@ -82,8 +83,9 @@ class BindingSuggestionProviderTests {
8283
private final SonarProjectsCache sonarProjectsCache = mock(SonarProjectsCache.class);
8384
private final SonarQubeClientManager sonarQubeClientManager = mock(SonarQubeClientManager.class);
8485
private final ClientFileSystemService clientFs = mock(ClientFileSystemService.class);
86+
private final TelemetryService telemetryService = mock(TelemetryService.class);
8587

86-
private final BindingSuggestionProvider underTest = new BindingSuggestionProvider(configRepository, connectionRepository, client, bindingClueProvider, sonarProjectsCache, sonarQubeClientManager, clientFs);
88+
private final BindingSuggestionProvider underTest = new BindingSuggestionProvider(configRepository, connectionRepository, client, bindingClueProvider, sonarProjectsCache, sonarQubeClientManager, clientFs, telemetryService);
8789

8890
@BeforeEach
8991
public void setup() {

backend/server-api/src/main/java/org/sonarsource/sonarlint/core/serverapi/component/ComponentApi.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
public class ComponentApi {
3535
private static final SonarLintLogger LOG = SonarLintLogger.get();
36+
private static final String ORGANIZATION_PARAM = "&organization=";
3637

3738
private final ServerApiHelper helper;
3839

@@ -56,7 +57,7 @@ private String buildAllFileKeysPath(String projectKey) {
5657
var url = new StringBuilder();
5758
url.append("api/components/tree.protobuf?qualifiers=FIL,UTS&");
5859
url.append("component=").append(UrlUtils.urlEncode(projectKey));
59-
helper.getOrganizationKey().ifPresent(org -> url.append("&organization=").append(UrlUtils.urlEncode(org)));
60+
helper.getOrganizationKey().ifPresent(org -> url.append(ORGANIZATION_PARAM).append(UrlUtils.urlEncode(org)));
6061
return url.toString();
6162
}
6263

@@ -80,7 +81,7 @@ private String getAllProjectsUrl() {
8081
var searchUrl = new StringBuilder();
8182
searchUrl.append("api/components/search.protobuf?qualifiers=TRK");
8283
helper.getOrganizationKey()
83-
.ifPresent(org -> searchUrl.append("&organization=").append(UrlUtils.urlEncode(org)));
84+
.ifPresent(org -> searchUrl.append(ORGANIZATION_PARAM).append(UrlUtils.urlEncode(org)));
8485
return searchUrl.toString();
8586
}
8687

@@ -93,7 +94,7 @@ public SearchProjectResponse searchProjects(String projectId, SonarLintCancelMon
9394
LOG.warn("Organization key is not set, cannot search projects for ID: {}", projectId);
9495
return null;
9596
}
96-
var path = "/api/components/search_projects?projectIds=" + encodedProjectId + "&organization=" + organization.get();
97+
var path = "/api/components/search_projects?projectIds=" + encodedProjectId + ORGANIZATION_PARAM + organization.get();
9798

9899
try (var response = helper.rawGet(path, cancelMonitor)) {
99100
if (response.isSuccessful()) {

backend/server-api/src/test/java/org/sonarsource/sonarlint/core/serverapi/component/ComponentApiTests.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929

3030
import static org.assertj.core.api.Assertions.assertThat;
3131
import static org.assertj.core.api.Assertions.tuple;
32-
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
3332

3433
class ComponentApiTests {
3534
@RegisterExtension

backend/server-api/src/test/java/org/sonarsource/sonarlint/core/serverapi/projectbindings/ProjectBindingsApiTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ void should_return_empty_when_request_fails() {
8989
var url = "https://github.com/foo/bar";
9090
var encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);
9191
mockServer.addResponse("/dop-translation/project-bindings?url=" + encodedUrl,
92-
new MockResponse().setResponseCode(500).setBody("Internal error"));
92+
new MockResponse.Builder().code(500).body("Internal error").build());
9393

9494
var result = underTest.getSQCProjectBindings(url, new SonarLintCancelMonitor());
9595

@@ -140,11 +140,11 @@ void should_return_empty_when_request_fails() {
140140
var url = "https://github.com/foo/bar";
141141
var encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);
142142
mockServer.addResponse("/api/v2/dop-translation/project-bindings?repositoryUrl=" + encodedUrl,
143-
new MockResponse().setResponseCode(500).setBody("Internal error"));
143+
new MockResponse.Builder().code(500).body("Internal error").build());
144144

145145
var result = underTest.getSQSProjectBindings(url, new SonarLintCancelMonitor());
146146

147147
assertThat(result).isNull();
148148
}
149149
}
150-
}
150+
}

backend/telemetry/src/main/java/org/sonarsource/sonarlint/core/telemetry/TelemetryLocalStorage.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public class TelemetryLocalStorage {
7474
private int importedAddedBindingsCount;
7575
private int autoAddedBindingsCount;
7676
private int exportedConnectedModeCount;
77+
private int suggestedRemoteBindingsCount;
7778
private long newIssuesFoundCount;
7879
private long issuesFixedCount;
7980
private int biggestNumberOfFilesInConfigScope;
@@ -238,6 +239,7 @@ void clearAfterPing() {
238239
importedAddedBindingsCount = 0;
239240
autoAddedBindingsCount = 0;
240241
exportedConnectedModeCount = 0;
242+
suggestedRemoteBindingsCount = 0;
241243
newIssuesFoundCount = 0;
242244
issuesFixedCount = 0;
243245
biggestNumberOfFilesInConfigScope = 0;
@@ -484,10 +486,18 @@ public void incrementExportedConnectedModeCount() {
484486
exportedConnectedModeCount++;
485487
}
486488

489+
public void incrementSuggestedRemoteBindingsCount() {
490+
suggestedRemoteBindingsCount++;
491+
}
492+
487493
public int getExportedConnectedModeCount() {
488494
return exportedConnectedModeCount;
489495
}
490496

497+
public int getSuggestedRemoteBindingsCount() {
498+
return suggestedRemoteBindingsCount;
499+
}
500+
491501
public void addNewlyFoundIssues(long newIssues) {
492502
markSonarLintAsUsedToday();
493503
newIssuesFoundCount += newIssues;

backend/telemetry/src/main/java/org/sonarsource/sonarlint/core/telemetry/measures/payload/TelemetryMeasuresBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ private void addConnectedModeMeasures(ArrayList<TelemetryMeasuresValue> values)
9191
values.add(new TelemetryMeasuresValue("shared_connected_mode.imported", String.valueOf(storage.getImportedAddedBindingsCount()), INTEGER, DAILY));
9292
values.add(new TelemetryMeasuresValue("shared_connected_mode.auto", String.valueOf(storage.getAutoAddedBindingsCount()), INTEGER, DAILY));
9393
values.add(new TelemetryMeasuresValue("shared_connected_mode.exported", String.valueOf(storage.getExportedConnectedModeCount()), INTEGER, DAILY));
94+
values.add(new TelemetryMeasuresValue("shared_connected_mode.remote_url", String.valueOf(storage.getSuggestedRemoteBindingsCount()), INTEGER, DAILY));
9495

9596
values.add(new TelemetryMeasuresValue("bindings.child_count", String.valueOf(liveAttributes.countChildBindings()), INTEGER, DAILY));
9697
values.add(new TelemetryMeasuresValue("bindings.server_count", String.valueOf(liveAttributes.countSonarQubeServerBindings()), INTEGER, DAILY));

backend/telemetry/src/test/java/org/sonarsource/sonarlint/core/telemetry/payload/TelemetryMeasuresPayloadTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ void testGenerationJson() {
6363
"{\"key\":\"shared_connected_mode.imported\",\"value\":\"2\",\"type\":\"integer\",\"granularity\":\"daily\"}," +
6464
"{\"key\":\"shared_connected_mode.auto\",\"value\":\"3\",\"type\":\"integer\",\"granularity\":\"daily\"}," +
6565
"{\"key\":\"shared_connected_mode.exported\",\"value\":\"4\",\"type\":\"integer\",\"granularity\":\"daily\"}," +
66+
"{\"key\":\"shared_connected_mode.remote_url\",\"value\":\"5\",\"type\":\"integer\",\"granularity\":\"daily\"}," +
6667
"{\"key\":\"bindings.child_count\",\"value\":\"1\",\"type\":\"integer\",\"granularity\":\"daily\"}," +
6768
"{\"key\":\"bindings.server_count\",\"value\":\"2\",\"type\":\"integer\",\"granularity\":\"daily\"}," +
6869
"{\"key\":\"bindings.cloud_eu_count\",\"value\":\"0\",\"type\":\"integer\",\"granularity\":\"daily\"}," +
@@ -88,6 +89,7 @@ private List<TelemetryMeasuresValue> generateMeasures() {
8889
values.add(new TelemetryMeasuresValue("shared_connected_mode.imported", String.valueOf(2), INTEGER, DAILY));
8990
values.add(new TelemetryMeasuresValue("shared_connected_mode.auto", String.valueOf(3), INTEGER, DAILY));
9091
values.add(new TelemetryMeasuresValue("shared_connected_mode.exported", String.valueOf(4), INTEGER, DAILY));
92+
values.add(new TelemetryMeasuresValue("shared_connected_mode.remote_url", String.valueOf(5), INTEGER, DAILY));
9193

9294
values.add(new TelemetryMeasuresValue("bindings.child_count", String.valueOf(1), INTEGER, DAILY));
9395
values.add(new TelemetryMeasuresValue("bindings.server_count", String.valueOf(2), INTEGER, DAILY));
@@ -109,6 +111,7 @@ private static void assertValues(List<TelemetryMeasuresValue> values) {
109111
.contains(tuple("shared_connected_mode.imported", "2", INTEGER, DAILY))
110112
.contains(tuple("shared_connected_mode.auto", "3", INTEGER, DAILY))
111113
.contains(tuple("shared_connected_mode.exported", "4", INTEGER, DAILY))
114+
.contains(tuple("shared_connected_mode.remote_url", "5", INTEGER, DAILY))
112115
.contains(tuple("help_and_feedback.doc_link", "5", INTEGER, DAILY))
113116
.contains(tuple("analysis_reporting.trigger_count_vcs_changed_files", "7", INTEGER, DAILY));
114117
}

medium-tests/src/test/java/mediumtest/BindingSuggestionsMediumTests.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found(SonarL
366366
.withSonarQubeCloudEuRegionApiUri(scServer.baseUrl())
367367
.withSonarCloudConnection(MYSONAR, "orgKey")
368368
.withUnboundConfigScope(CONFIG_SCOPE_ID, "unmatched-project-name")
369+
.withTelemetryEnabled()
369370
.start(fakeClient);
370371

371372
scServer.getMockServer().stubFor(get(urlEqualTo(expectedPath))
@@ -396,6 +397,7 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found(SonarL
396397
verify(fakeClient, timeout(5000)).suggestBinding(suggestionCaptor.capture());
397398

398399
var bindingSuggestions = suggestionCaptor.getValue();
400+
assertThat(backend.telemetryFileContent().getSuggestedRemoteBindingsCount()).isEqualTo(1);
399401
assertThat(bindingSuggestions).containsOnlyKeys(CONFIG_SCOPE_ID);
400402
assertThat(bindingSuggestions.get(CONFIG_SCOPE_ID))
401403
.extracting(BindingSuggestionDto::getConnectionId, BindingSuggestionDto::getSonarProjectKey, BindingSuggestionDto::getSonarProjectName)
@@ -430,6 +432,7 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found_for_so
430432
var backend = harness.newBackend()
431433
.withSonarQubeConnection(MYSONAR, sqServer.baseUrl())
432434
.withUnboundConfigScope(CONFIG_SCOPE_ID, "unmatched-project-name")
435+
.withTelemetryEnabled()
433436
.start(fakeClient);
434437

435438
sqServer.getMockServer().stubFor(get(urlEqualTo(expectedPath))
@@ -455,6 +458,7 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found_for_so
455458
verify(fakeClient, timeout(5000)).suggestBinding(suggestionCaptor.capture());
456459

457460
var bindingSuggestions = suggestionCaptor.getValue();
461+
assertThat(backend.telemetryFileContent().getSuggestedRemoteBindingsCount()).isEqualTo(1);
458462
assertThat(bindingSuggestions).containsOnlyKeys(CONFIG_SCOPE_ID);
459463
assertThat(bindingSuggestions.get(CONFIG_SCOPE_ID))
460464
.extracting(BindingSuggestionDto::getConnectionId, BindingSuggestionDto::getSonarProjectKey, BindingSuggestionDto::getSonarProjectName)

0 commit comments

Comments
 (0)