From: lukasz-jarocki-sonarsource Date: Fri, 11 Oct 2024 10:13:17 +0000 (+0200) Subject: SONAR-23327 Added support for sending telemetry data in compute engine process X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=38a765efec578eadc53393e65f3b6b892ab83bcf;p=sonarqube.git SONAR-23327 Added support for sending telemetry data in compute engine process --- diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReader.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReader.java index de85e094a67..5b908c4d4a3 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReader.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReader.java @@ -72,4 +72,6 @@ public interface BatchReportReader { CloseableIterator readAnalysisWarnings(); CloseableIterator readCves(); + + CloseableIterator readTelemetryEntries(); } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java index df17a5ac89b..740d7a259d7 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java @@ -233,4 +233,10 @@ public class BatchReportReaderImpl implements BatchReportReader { ensureInitialized(); return delegate.readCves(); } + + @Override + public CloseableIterator readTelemetryEntries() { + ensureInitialized(); + return delegate.readTelemetryEntries(); + } } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java index 466be8f175f..00c21bd5357 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java @@ -41,6 +41,7 @@ public class ReportComputationSteps extends AbstractComputationSteps { private static final List> STEPS = Arrays.asList( ExtractReportStep.class, + SendAnalysisTelemetryStep.class, PersistScannerContextStep.class, PersistAnalysisWarningsStep.class, GenerateAnalysisUuid.class, diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/SendAnalysisTelemetryStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/SendAnalysisTelemetryStep.java new file mode 100644 index 00000000000..62700fad800 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/SendAnalysisTelemetryStep.java @@ -0,0 +1,92 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.ce.task.projectanalysis.step; + +import java.util.HashSet; +import java.util.Set; +import org.sonar.api.config.Configuration; +import org.sonar.api.platform.Server; +import org.sonar.ce.task.projectanalysis.batch.BatchReportReader; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.core.util.CloseableIterator; +import org.sonar.core.util.UuidFactory; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.telemetry.core.Dimension; +import org.sonar.telemetry.core.MessageSerializer; +import org.sonar.telemetry.core.TelemetryClient; +import org.sonar.telemetry.core.schema.AnalysisMetric; +import org.sonar.telemetry.core.schema.BaseMessage; +import org.sonar.telemetry.core.schema.Metric; + +import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_ENABLE; + +public class SendAnalysisTelemetryStep implements ComputationStep { + + private final TelemetryClient telemetryClient; + private final BatchReportReader batchReportReader; + private final Server server; + private final UuidFactory uuidFactory; + private final Configuration config; + + public SendAnalysisTelemetryStep(TelemetryClient telemetryClient, BatchReportReader batchReportReader, + UuidFactory uuidFactory, Server server, Configuration configuration) { + this.telemetryClient = telemetryClient; + this.batchReportReader = batchReportReader; + this.server = server; + this.uuidFactory = uuidFactory; + this.config = configuration; + } + + @Override + public void execute(Context context) { + if (!config.getBoolean(SONAR_TELEMETRY_ENABLE.getKey()).orElse(false)) { + return; + } + try (CloseableIterator it = batchReportReader.readTelemetryEntries()) { + Set metrics = new HashSet<>(); + // it was agreed to limit the number of telemetry entries to 1000 per one analysis + final int limit = 1000; + int count = 0; + while (it.hasNext() && count++ < limit) { + ScannerReport.TelemetryEntry telemetryEntry = it.next(); + metrics.add(new AnalysisMetric(telemetryEntry.getKey(), telemetryEntry.getValue())); + } + + if (metrics.isEmpty()) { + return; + } + BaseMessage baseMessage = new BaseMessage.Builder() + .setMessageUuid(uuidFactory.create()) + .setInstallationId(server.getId()) + .setDimension(Dimension.ANALYSIS) + .setMetrics(metrics) + .build(); + + String jsonString = MessageSerializer.serialize(baseMessage); + telemetryClient.uploadMetricAsync(jsonString); + } + + } + + @Override + public String getDescription() { + return "This step pushes telemetry data from the Sonar analyzers to Telemetry V2 server in case telemetry is enabled."; + } +} \ No newline at end of file diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java index b960803b18a..9eda82d4f2c 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java @@ -63,6 +63,7 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader, After private List analysisWarnings = Collections.emptyList(); private byte[] analysisCache; private List cves = new ArrayList<>(); + private List telemetryEntries = new ArrayList<>(); @Override public Statement apply(final Statement statement, Description description) { @@ -333,6 +334,16 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader, After return CloseableIterator.from(cves.iterator()); } + @Override + public CloseableIterator readTelemetryEntries() { + return CloseableIterator.from(telemetryEntries.iterator()); + } + + public BatchReportReaderRule putTelemetry(List telemetryEntries) { + this.telemetryEntries = telemetryEntries; + return this; + } + public BatchReportReaderRule putCves(List cves) { this.cves = cves; return this; diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportComputationStepsTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportComputationStepsTest.java index cdbd274b5f6..e060a69ba47 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportComputationStepsTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportComputationStepsTest.java @@ -56,6 +56,6 @@ public class ReportComputationStepsTest { Iterable instances = new ReportComputationSteps(computeEngineContainer).instances(); assertThatThrownBy(() -> newArrayList(instances)) .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("org.sonar.ce.task.projectanalysis.step.PersistScannerContextStep"); + .hasMessageContaining("org.sonar.ce.task.projectanalysis.step.SendAnalysisTelemetryStep"); } } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/SendAnalysisTelemetryStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/SendAnalysisTelemetryStepTest.java new file mode 100644 index 00000000000..7e0c196c1bb --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/SendAnalysisTelemetryStepTest.java @@ -0,0 +1,111 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.ce.task.projectanalysis.step; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.sonar.api.config.Configuration; +import org.sonar.api.platform.Server; +import org.sonar.ce.task.projectanalysis.batch.BatchReportReader; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.core.util.CloseableIterator; +import org.sonar.core.util.UuidFactory; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.telemetry.core.TelemetryClient; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +class SendAnalysisTelemetryStepTest { + + private final TelemetryClient telemetryClient = mock(); + private final BatchReportReader batchReportReader = mock(); + private final UuidFactory uuidFactory = mock(); + private final Server server = mock(); + private final ComputationStep.Context context = mock(); + private final Configuration configuration = mock(); + private final SendAnalysisTelemetryStep underTest = new SendAnalysisTelemetryStep(telemetryClient, batchReportReader, uuidFactory, + server, configuration); + + { + when(uuidFactory.create()).thenReturn("uuid"); + when(server.getId()).thenReturn("serverId"); + when(configuration.getBoolean("sonar.telemetry.enable")).thenReturn(Optional.of(true)); + } + + @Test + void execute_whenNoMetrics_dontSendAnything() { + when(batchReportReader.readTelemetryEntries()).thenReturn(CloseableIterator.emptyCloseableIterator()); + + underTest.execute(context); + + verifyNoInteractions(telemetryClient); + } + + @Test + void execute_whenTwoMetrics_callTelemetryClientOnce() { + Set telemetryEntries = Set.of( + ScannerReport.TelemetryEntry.newBuilder().setKey("key1").setValue("value1").build(), + ScannerReport.TelemetryEntry.newBuilder().setKey("key2").setValue("value2").build()); + when(batchReportReader.readTelemetryEntries()).thenReturn(CloseableIterator.from(telemetryEntries.iterator())); + + underTest.execute(context); + + verify(telemetryClient, times(1)).uploadMetricAsync(anyString()); + } + + @Test + void execute_whenMetricsPresentAndTelemetryNotEnabled_dontCallTelemetryClient() { + when(configuration.getBoolean("sonar.telemetry.enable")).thenReturn(Optional.of(false)); + Set telemetryEntries = Set.of( + ScannerReport.TelemetryEntry.newBuilder().setKey("key1").setValue("value1").build(), + ScannerReport.TelemetryEntry.newBuilder().setKey("key2").setValue("value2").build()); + when(batchReportReader.readTelemetryEntries()).thenReturn(CloseableIterator.from(telemetryEntries.iterator())); + + underTest.execute(context); + + verifyNoInteractions(telemetryClient); + } + + @Test + void execute_when2000entries_sendOnly1000entries() { + Set telemetryEntries = new HashSet<>(); + for (int i = 0; i < 2000; i++) { + telemetryEntries.add(ScannerReport.TelemetryEntry.newBuilder().setKey(String.valueOf(i)).setValue("value" + i).build()); + } + when(batchReportReader.readTelemetryEntries()).thenReturn(CloseableIterator.from(telemetryEntries.iterator())); + + underTest.execute(context); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); + verify(telemetryClient, times(1)).uploadMetricAsync(argumentCaptor.capture()); + + String capturedArgument = argumentCaptor.getValue(); + assertEquals(1000 + 1, capturedArgument.split("key").length); + } +} diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/CeTaskCommonsModule.java b/server/sonar-ce/src/main/java/org/sonar/ce/CeTaskCommonsModule.java index 7a8a110c856..396a50eb5fd 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/CeTaskCommonsModule.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/CeTaskCommonsModule.java @@ -23,6 +23,7 @@ import org.sonar.ce.task.projectanalysis.purge.IndexPurgeListener; import org.sonar.ce.task.projectanalysis.purge.ProjectCleaner; import org.sonar.core.platform.Module; import org.sonar.db.purge.period.DefaultPeriodCleaner; +import org.sonar.telemetry.core.TelemetryClient; /** * Globally available components in CE for tasks to use. @@ -33,6 +34,7 @@ public class CeTaskCommonsModule extends Module { add( DefaultPeriodCleaner.class, ProjectCleaner.class, - IndexPurgeListener.class); + IndexPurgeListener.class, + TelemetryClient.class); } } diff --git a/server/sonar-telemetry-core/build.gradle b/server/sonar-telemetry-core/build.gradle index 47330b97821..7ae81051fd7 100644 --- a/server/sonar-telemetry-core/build.gradle +++ b/server/sonar-telemetry-core/build.gradle @@ -9,12 +9,16 @@ sonar { dependencies { compileOnlyApi 'com.github.spotbugs:spotbugs-annotations' + implementation project(':sonar-core') + implementation project(':server:sonar-process') + implementation 'com.fasterxml.jackson.core:jackson-databind' testImplementation(platform("org.junit:junit-bom")) testImplementation 'org.assertj:assertj-core' testImplementation 'org.junit.jupiter:junit-jupiter-api' testImplementation 'org.junit.jupiter:junit-jupiter-params' + testImplementation 'org.mockito:mockito-core' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' } diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/Dimension.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/Dimension.java index 96f9ce5b5fd..e3362120f22 100644 --- a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/Dimension.java +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/Dimension.java @@ -30,7 +30,8 @@ public enum Dimension { INSTALLATION("installation"), USER("user"), PROJECT("project"), - LANGUAGE("language"); + LANGUAGE("language"), + ANALYSIS("analysis"); private final String value; diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/MessageSerializer.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/MessageSerializer.java new file mode 100644 index 00000000000..fef42ad9077 --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/MessageSerializer.java @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.UncheckedIOException; +import org.sonar.telemetry.core.schema.BaseMessage; + +public class MessageSerializer { + + private MessageSerializer() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static String serialize(BaseMessage message) { + ObjectMapper mapper = new ObjectMapper(); + try { + return mapper.writeValueAsString(message); + } catch (IOException ioException) { + throw new UncheckedIOException(ioException); + } + } + +} diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/TelemetryClient.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/TelemetryClient.java new file mode 100644 index 00000000000..d17a103182f --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/TelemetryClient.java @@ -0,0 +1,147 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core; + +import java.io.IOException; +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okio.BufferedSink; +import okio.GzipSink; +import okio.Okio; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.Startable; +import org.sonar.api.ce.ComputeEngineSide; +import org.sonar.api.config.Configuration; +import org.sonar.api.server.ServerSide; + +import static org.sonar.process.ProcessProperties.Property; + +@ComputeEngineSide +@ServerSide +public class TelemetryClient implements Startable { + private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + private static final Logger LOG = LoggerFactory.getLogger(TelemetryClient.class); + + private final OkHttpClient okHttpClient; + private final Configuration config; + private String serverUrl; + private String metricsServerUrl; + private boolean compression; + + public TelemetryClient(OkHttpClient okHttpClient, Configuration config) { + this.config = config; + this.okHttpClient = okHttpClient; + } + + public void upload(String json) throws IOException { + Request request = buildHttpRequest(serverUrl, json); + execute(okHttpClient.newCall(request)); + } + + public void uploadMetric(String json) throws IOException { + Request request = buildHttpRequest(metricsServerUrl, json); + execute(okHttpClient.newCall(request)); + } + + public void optOut(String json) { + Request.Builder request = new Request.Builder(); + request.url(serverUrl); + RequestBody body = RequestBody.create(JSON, json); + request.delete(body); + try { + execute(okHttpClient.newCall(request.build())); + } catch (IOException e) { + LOG.debug("Error when sending opt-out usage statistics: {}", e.getMessage()); + } + } + + private Request buildHttpRequest(String serverUrl, String json) { + Request.Builder request = new Request.Builder(); + request.addHeader("Content-Encoding", "gzip"); + request.addHeader("Content-Type", "application/json"); + request.url(serverUrl); + RequestBody body = RequestBody.create(JSON, json); + if (compression) { + request.post(gzip(body)); + } else { + request.post(body); + } + return request.build(); + } + + private static RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override + public MediaType contentType() { + return body.contentType(); + } + + @Override + public long contentLength() { + // We don't know the compressed length in advance! + return -1; + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); + body.writeTo(gzipSink); + gzipSink.close(); + } + }; + } + + private static void execute(Call call) throws IOException { + try (Response ignored = call.execute()) { + // auto close connection to avoid leaked connection + } + } + + @Override + public void start() { + this.serverUrl = config.get(Property.SONAR_TELEMETRY_URL.getKey()) + .orElseThrow(() -> new IllegalStateException(String.format("Setting '%s' must be provided.", Property.SONAR_TELEMETRY_URL))); + this.metricsServerUrl = config.get(Property.SONAR_TELEMETRY_METRICS_URL.getKey()) + .orElseThrow(() -> new IllegalStateException(String.format("Setting '%s' must be provided.", Property.SONAR_TELEMETRY_METRICS_URL))); + this.compression = config.getBoolean(Property.SONAR_TELEMETRY_COMPRESSION.getKey()).orElse(true); + } + + @Override + public void stop() { + // Nothing to do + } + + public void uploadMetricAsync(String jsonString) { + Thread thread = new Thread(() -> { + try { + uploadMetric(jsonString); + } catch (IOException e) { + LOG.debug("Sending telemetry messages has failed", e); + } + }); + thread.setDaemon(true); + thread.start(); + } +} diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/AnalysisMetric.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/AnalysisMetric.java new file mode 100644 index 00000000000..66372e8ddbb --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/AnalysisMetric.java @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +public class AnalysisMetric extends Metric { + + public AnalysisMetric(String key, String value) { + this.key = key; + this.value = value; + this.type = TelemetryDataType.STRING; + this.granularity = Granularity.ADHOC; + } +} diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/BaseMessage.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/BaseMessage.java new file mode 100644 index 00000000000..11d57b49cdb --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/BaseMessage.java @@ -0,0 +1,98 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Set; +import org.sonar.telemetry.core.Dimension; + +public class BaseMessage { + @JsonProperty("message_uuid") + private String messageUuid; + + @JsonProperty("installation_id") + private String installationId; + + @JsonProperty("dimension") + private Dimension dimension; + + @JsonProperty("metric_values") + private Set metrics; + + protected BaseMessage(String messageUuid, String installationId, Dimension dimension, Set metrics) { + this.messageUuid = messageUuid; + this.installationId = installationId; + this.dimension = dimension; + this.metrics = metrics; + } + + public String getMessageUuid() { + return messageUuid; + } + + public String getInstallationId() { + return installationId; + } + + public Dimension getDimension() { + return dimension; + } + + public Set getMetrics() { + return metrics; + } + + public static class Builder { + private String messageUuid; + private String installationId; + private Dimension dimension; + private Set metrics; + + public Builder setMessageUuid(String messageUuid) { + this.messageUuid = messageUuid; + return this; + } + + public Builder setInstallationId(String installationId) { + this.installationId = installationId; + return this; + } + + public Builder setDimension(Dimension dimension) { + this.dimension = dimension; + return this; + } + + public Builder setMetrics(Set metrics) { + this.metrics = metrics; + return this; + } + + public BaseMessage build() { + Objects.requireNonNull(messageUuid, "messageUuid must be specified"); + Objects.requireNonNull(installationId, "installationId must be specified"); + Objects.requireNonNull(dimension, "dimension must be specified"); + Objects.requireNonNull(metrics, "metrics must be specified"); + + return new BaseMessage(messageUuid, installationId, dimension, metrics); + } + } +} diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/InstallationMetric.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/InstallationMetric.java new file mode 100644 index 00000000000..583711d23a8 --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/InstallationMetric.java @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import javax.annotation.Nullable; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +public class InstallationMetric extends Metric { + + public InstallationMetric(String key, @Nullable Object value, TelemetryDataType type, Granularity granularity) { + this.key = key; + this.value = value; + this.type = type; + this.granularity = granularity; + } + +} diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/LanguageMetric.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/LanguageMetric.java new file mode 100644 index 00000000000..0f1974b1427 --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/LanguageMetric.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +public class LanguageMetric extends Metric { + + @JsonProperty("language") + private String language; + + public LanguageMetric(String key, Object value, String language, TelemetryDataType type, Granularity granularity) { + this.key = key; + this.value = value; + this.language = language; + this.type = type; + this.granularity = granularity; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } +} diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/Metric.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/Metric.java new file mode 100644 index 00000000000..dfe8b7fdcfb --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/Metric.java @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +public abstract class Metric { + @JsonProperty("key") + protected String key; + + @JsonProperty("value") + protected Object value; + + @JsonProperty("type") + protected TelemetryDataType type; + + @JsonProperty("granularity") + protected Granularity granularity; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public TelemetryDataType getType() { + return type; + } + + public void setType(TelemetryDataType type) { + this.type = type; + } + + public Granularity getGranularity() { + return granularity; + } + + public void setGranularity(Granularity granularity) { + this.granularity = granularity; + } +} diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/ProjectMetric.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/ProjectMetric.java new file mode 100644 index 00000000000..83eb95b491e --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/ProjectMetric.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +public class ProjectMetric extends Metric { + + @JsonProperty("project_uuid") + private String projectUuid; + + public ProjectMetric(String key, Object value, String projectUuid, TelemetryDataType type, Granularity granularity) { + this.key = key; + this.value = value; + this.projectUuid = projectUuid; + this.type = type; + this.granularity = granularity; + } + + public String getProjectUuid() { + return projectUuid; + } + + public void setProjectUuid(String projectUuid) { + this.projectUuid = projectUuid; + } + +} diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/UserMetric.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/UserMetric.java new file mode 100644 index 00000000000..a9e49e0cca5 --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/UserMetric.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +public class UserMetric extends Metric { + + @JsonProperty("user_uuid") + private String userUuid; + + public UserMetric(String key, Object value, String userUuid, TelemetryDataType type, Granularity granularity) { + this.key = key; + this.value = value; + this.userUuid = userUuid; + this.type = type; + this.granularity = granularity; + } + + public String getUserUuid() { + return userUuid; + } + + public void setUserUuid(String userUuid) { + this.userUuid = userUuid; + } + +} diff --git a/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/package-info.java b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/package-info.java new file mode 100644 index 00000000000..4656410152f --- /dev/null +++ b/server/sonar-telemetry-core/src/main/java/org/sonar/telemetry/core/schema/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.telemetry.core.schema; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/TelemetryClientTest.java b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/TelemetryClientTest.java new file mode 100644 index 00000000000..3e3c7a026eb --- /dev/null +++ b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/TelemetryClientTest.java @@ -0,0 +1,107 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core; + +import java.io.IOException; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okio.Buffer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.sonar.api.config.internal.MapSettings; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_COMPRESSION; +import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_METRICS_URL; +import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_URL; + +class TelemetryClientTest { + + private static final String JSON = "{\"key\":\"value\"}"; + private static final String TELEMETRY_URL = "https://telemetry.com/url"; + private static final String METRICS_TELEMETRY_URL = "https://telemetry.com/url/metrics"; + + private final OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class, Mockito.RETURNS_DEEP_STUBS); + private final MapSettings settings = new MapSettings(); + + private final TelemetryClient underTest = new TelemetryClient(okHttpClient, settings.asConfig()); + + @BeforeEach + void setProperties() { + settings.setProperty(SONAR_TELEMETRY_URL.getKey(), TELEMETRY_URL); + settings.setProperty(SONAR_TELEMETRY_METRICS_URL.getKey(), METRICS_TELEMETRY_URL); + } + + @Test + void upload() throws IOException { + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(Request.class); + settings.setProperty(SONAR_TELEMETRY_COMPRESSION.getKey(), false); + underTest.start(); + + underTest.upload(JSON); + + Mockito.verify(okHttpClient).newCall(requestCaptor.capture()); + Request request = requestCaptor.getValue(); + assertThat(request.method()).isEqualTo("POST"); + assertThat(request.body().contentType()).isEqualTo(MediaType.parse("application/json; charset=utf-8")); + Buffer body = new Buffer(); + request.body().writeTo(body); + assertThat(body.readUtf8()).isEqualTo(JSON); + assertThat(request.url()).hasToString(TELEMETRY_URL); + } + + @Test + void uploadMetric() throws IOException { + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(Request.class); + settings.setProperty(SONAR_TELEMETRY_COMPRESSION.getKey(), false); + underTest.start(); + + underTest.uploadMetric(JSON); + + Mockito.verify(okHttpClient).newCall(requestCaptor.capture()); + Request request = requestCaptor.getValue(); + assertThat(request.method()).isEqualTo("POST"); + assertThat(request.body().contentType()).isEqualTo(MediaType.parse("application/json; charset=utf-8")); + Buffer body = new Buffer(); + request.body().writeTo(body); + assertThat(body.readUtf8()).isEqualTo(JSON); + assertThat(request.url()).hasToString(METRICS_TELEMETRY_URL); + } + + @Test + void opt_out() throws IOException { + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(Request.class); + underTest.start(); + + underTest.optOut(JSON); + + Mockito.verify(okHttpClient).newCall(requestCaptor.capture()); + Request request = requestCaptor.getValue(); + assertThat(request.method()).isEqualTo("DELETE"); + assertThat(request.body().contentType()).isEqualTo(MediaType.parse("application/json; charset=utf-8")); + Buffer body = new Buffer(); + request.body().writeTo(body); + assertThat(body.readUtf8()).isEqualTo(JSON); + assertThat(request.url()).hasToString(TELEMETRY_URL); + } +} diff --git a/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/AnalysisMetricTest.java b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/AnalysisMetricTest.java new file mode 100644 index 00000000000..13e0ee017fb --- /dev/null +++ b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/AnalysisMetricTest.java @@ -0,0 +1,40 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.telemetry.core.schema; + +import org.junit.jupiter.api.Test; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +import static org.assertj.core.api.Assertions.assertThat; + +class AnalysisMetricTest { + + @Test + void getters() { + AnalysisMetric metric = new AnalysisMetric("memory", "100"); + + assertThat(metric.getKey()).isEqualTo("memory"); + assertThat(metric.getValue()).isEqualTo("100"); + assertThat(metric.getGranularity()).isEqualTo(Granularity.ADHOC); + assertThat(metric.getType()).isEqualTo(TelemetryDataType.STRING); + } +} diff --git a/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/BaseMessageTest.java b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/BaseMessageTest.java new file mode 100644 index 00000000000..0710eb8dc34 --- /dev/null +++ b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/BaseMessageTest.java @@ -0,0 +1,105 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.sonar.telemetry.core.Dimension; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class BaseMessageTest { + + @Test + void build() { + BaseMessage message = new BaseMessage.Builder() + .setMessageUuid("123e4567-e89b-12d3-a456-426614174000") + .setInstallationId("installation-id") + .setDimension(Dimension.INSTALLATION) + .setMetrics(installationMetrics()) + .build(); + + assertThat(message.getMessageUuid()).isEqualTo("123e4567-e89b-12d3-a456-426614174000"); + assertThat(message.getInstallationId()).isEqualTo("installation-id"); + assertThat(message.getDimension()).isEqualTo(Dimension.INSTALLATION); + Set installationMetrics = (Set) (Set) message.getMetrics(); + assertThat(installationMetrics) + .extracting(InstallationMetric::getKey, InstallationMetric::getGranularity, InstallationMetric::getType, InstallationMetric::getValue) + .containsExactlyInAnyOrder( + tuple("key-0", Granularity.DAILY, TelemetryDataType.INTEGER, 0), + tuple("key-1", Granularity.DAILY, TelemetryDataType.INTEGER, 1), + tuple("key-2", Granularity.DAILY, TelemetryDataType.INTEGER, 2) + ); + } + + @ParameterizedTest + @MethodSource("invalidBaseMessageProvider") + void build_invalidCases(BaseMessage.Builder builder, String expectedErrorMessage) { + Exception exception = assertThrows(NullPointerException.class, builder::build); + assertEquals(expectedErrorMessage, exception.getMessage()); + } + + private static Stream invalidBaseMessageProvider() { + return Stream.of( + Arguments.of( + new BaseMessage.Builder() + .setInstallationId("installation-id") + .setDimension(Dimension.INSTALLATION) + .setMetrics(installationMetrics()), + "messageUuid must be specified" + ), + Arguments.of( + new BaseMessage.Builder() + .setMessageUuid("some-uuid") + .setInstallationId("installation-id") + .setMetrics(installationMetrics()), + "dimension must be specified" + ), + Arguments.of( + new BaseMessage.Builder() + .setMessageUuid("some-uuid") + .setDimension(Dimension.INSTALLATION) + .setMetrics(installationMetrics()), + "installationId must be specified" + ) + ); + } + + private static Set installationMetrics() { + return IntStream.range(0, 3) + .mapToObj(i -> new InstallationMetric( + "key-" + i, + i, + TelemetryDataType.INTEGER, + Granularity.DAILY + )).collect(Collectors.toSet()); + } +} diff --git a/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/InstallationMetricTest.java b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/InstallationMetricTest.java new file mode 100644 index 00000000000..7d0b11c406a --- /dev/null +++ b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/InstallationMetricTest.java @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import org.junit.jupiter.api.Test; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +import static org.assertj.core.api.Assertions.assertThat; + +class InstallationMetricTest { + + @Test + void constructor() { + InstallationMetric metric = new InstallationMetric( + "installation-key-1", + "value", + TelemetryDataType.STRING, + Granularity.WEEKLY + ); + + assertThat(metric.getValue()).isEqualTo("value"); + assertThat(metric.getKey()).isEqualTo("installation-key-1"); + assertThat(metric.getGranularity()).isEqualTo(Granularity.WEEKLY); + assertThat(metric.getType()).isEqualTo(TelemetryDataType.STRING); + } + + @Test + void constructor_shouldAcceptNullValue() { + InstallationMetric metric = new InstallationMetric( + "installation-key-1", + null, + TelemetryDataType.STRING, + Granularity.WEEKLY + ); + + assertThat(metric.getValue()).isNull(); + assertThat(metric.getKey()).isEqualTo("installation-key-1"); + assertThat(metric.getGranularity()).isEqualTo(Granularity.WEEKLY); + assertThat(metric.getType()).isEqualTo(TelemetryDataType.STRING); + } + +} diff --git a/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/LanguageMetricTest.java b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/LanguageMetricTest.java new file mode 100644 index 00000000000..1b10608ccdc --- /dev/null +++ b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/LanguageMetricTest.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import org.junit.jupiter.api.Test; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +import static org.assertj.core.api.Assertions.assertThat; + +class LanguageMetricTest { + + @Test + void gettersAndSetters() { + LanguageMetric metric = new LanguageMetric("ncloc", 100, "java", TelemetryDataType.INTEGER, Granularity.MONTHLY); + + assertThat(metric.getLanguage()).isEqualTo("java"); + assertThat(metric.getValue()).isEqualTo(100); + assertThat(metric.getKey()).isEqualTo("ncloc"); + assertThat(metric.getGranularity()).isEqualTo(Granularity.MONTHLY); + assertThat(metric.getType()).isEqualTo(TelemetryDataType.INTEGER); + } + +} diff --git a/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/ProjectMetricTest.java b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/ProjectMetricTest.java new file mode 100644 index 00000000000..636b1fb2c0f --- /dev/null +++ b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/ProjectMetricTest.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import org.junit.jupiter.api.Test; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +import static org.assertj.core.api.Assertions.assertThat; + +class ProjectMetricTest { + + @Test + void gettersAndSetters() { + ProjectMetric metric = new ProjectMetric( + "project-key-1", + 1.0998, + "project-uuid", + TelemetryDataType.FLOAT, + Granularity.DAILY + ); + + assertThat(metric.getValue()).isEqualTo(1.0998); + assertThat(metric.getKey()).isEqualTo("project-key-1"); + assertThat(metric.getGranularity()).isEqualTo(Granularity.DAILY); + assertThat(metric.getType()).isEqualTo(TelemetryDataType.FLOAT); + assertThat(metric.getProjectUuid()).isEqualTo("project-uuid"); + } + +} diff --git a/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/UserMetricTest.java b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/UserMetricTest.java new file mode 100644 index 00000000000..dca676a8efe --- /dev/null +++ b/server/sonar-telemetry-core/src/test/java/org/sonar/telemetry/core/schema/UserMetricTest.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.telemetry.core.schema; + +import org.junit.jupiter.api.Test; +import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.TelemetryDataType; + +import static org.assertj.core.api.Assertions.assertThat; + +class UserMetricTest { + + @Test + void gettersAndSetters() { + UserMetric metric = new UserMetric( + "user-key-1", + true, + "user-uuid", + TelemetryDataType.BOOLEAN, + Granularity.DAILY + ); + + assertThat(metric.getValue()).isEqualTo(true); + assertThat(metric.getKey()).isEqualTo("user-key-1"); + assertThat(metric.getGranularity()).isEqualTo(Granularity.DAILY); + assertThat(metric.getType()).isEqualTo(TelemetryDataType.BOOLEAN); + assertThat(metric.getUserUuid()).isEqualTo("user-uuid"); + } + +} diff --git a/server/sonar-telemetry/src/it/java/org/sonar/telemetry/metrics/TelemetryMetricsLoaderIT.java b/server/sonar-telemetry/src/it/java/org/sonar/telemetry/metrics/TelemetryMetricsLoaderIT.java index 67e2ea872e8..7587e7c3111 100644 --- a/server/sonar-telemetry/src/it/java/org/sonar/telemetry/metrics/TelemetryMetricsLoaderIT.java +++ b/server/sonar-telemetry/src/it/java/org/sonar/telemetry/metrics/TelemetryMetricsLoaderIT.java @@ -40,11 +40,11 @@ import org.sonar.telemetry.core.Dimension; import org.sonar.telemetry.core.Granularity; import org.sonar.telemetry.core.TelemetryDataProvider; import org.sonar.telemetry.core.TelemetryDataType; -import org.sonar.telemetry.metrics.schema.BaseMessage; -import org.sonar.telemetry.metrics.schema.InstallationMetric; -import org.sonar.telemetry.metrics.schema.LanguageMetric; -import org.sonar.telemetry.metrics.schema.ProjectMetric; -import org.sonar.telemetry.metrics.schema.UserMetric; +import org.sonar.telemetry.core.schema.BaseMessage; +import org.sonar.telemetry.core.schema.InstallationMetric; +import org.sonar.telemetry.core.schema.LanguageMetric; +import org.sonar.telemetry.core.schema.ProjectMetric; +import org.sonar.telemetry.core.schema.UserMetric; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryClient.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryClient.java deleted file mode 100644 index c1a0a94ec7d..00000000000 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryClient.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry; - -import java.io.IOException; -import okhttp3.Call; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okio.BufferedSink; -import okio.GzipSink; -import okio.Okio; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.Startable; -import org.sonar.api.config.Configuration; -import org.sonar.api.server.ServerSide; - -import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_COMPRESSION; -import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_URL; -import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_METRICS_URL; - -@ServerSide -public class TelemetryClient implements Startable { - private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); - private static final Logger LOG = LoggerFactory.getLogger(TelemetryClient.class); - - private final OkHttpClient okHttpClient; - private final Configuration config; - private String serverUrl; - private String metricsServerUrl; - private boolean compression; - - public TelemetryClient(OkHttpClient okHttpClient, Configuration config) { - this.config = config; - this.okHttpClient = okHttpClient; - } - - void upload(String json) throws IOException { - Request request = buildHttpRequest(serverUrl, json); - execute(okHttpClient.newCall(request)); - } - - void uploadMetric(String json) throws IOException { - Request request = buildHttpRequest(metricsServerUrl, json); - execute(okHttpClient.newCall(request)); - } - - void optOut(String json) { - Request.Builder request = new Request.Builder(); - request.url(serverUrl); - RequestBody body = RequestBody.create(JSON, json); - request.delete(body); - try { - execute(okHttpClient.newCall(request.build())); - } catch (IOException e) { - LOG.debug("Error when sending opt-out usage statistics: {}", e.getMessage()); - } - } - - private Request buildHttpRequest(String serverUrl, String json) { - Request.Builder request = new Request.Builder(); - request.addHeader("Content-Encoding", "gzip"); - request.addHeader("Content-Type", "application/json"); - request.url(serverUrl); - RequestBody body = RequestBody.create(JSON, json); - if (compression) { - request.post(gzip(body)); - } else { - request.post(body); - } - return request.build(); - } - - private static RequestBody gzip(final RequestBody body) { - return new RequestBody() { - @Override - public MediaType contentType() { - return body.contentType(); - } - - @Override - public long contentLength() { - // We don't know the compressed length in advance! - return -1; - } - - @Override - public void writeTo(BufferedSink sink) throws IOException { - BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); - body.writeTo(gzipSink); - gzipSink.close(); - } - }; - } - - private static void execute(Call call) throws IOException { - try (Response ignored = call.execute()) { - // auto close connection to avoid leaked connection - } - } - - @Override - public void start() { - this.serverUrl = config.get(SONAR_TELEMETRY_URL.getKey()) - .orElseThrow(() -> new IllegalStateException(String.format("Setting '%s' must be provided.", SONAR_TELEMETRY_URL))); - this.metricsServerUrl = config.get(SONAR_TELEMETRY_METRICS_URL.getKey()) - .orElseThrow(() -> new IllegalStateException(String.format("Setting '%s' must be provided.", SONAR_TELEMETRY_METRICS_URL))); - this.compression = config.getBoolean(SONAR_TELEMETRY_COMPRESSION.getKey()).orElse(true); - } - - @Override - public void stop() { - // Nothing to do - } -} diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryDaemon.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryDaemon.java index 4f2c42689a0..821b0bbede8 100644 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryDaemon.java +++ b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryDaemon.java @@ -38,12 +38,13 @@ import org.sonar.db.DbSession; import org.sonar.server.property.InternalProperties; import org.sonar.server.util.AbstractStoppableScheduledExecutorServiceImpl; import org.sonar.server.util.GlobalLockManager; +import org.sonar.telemetry.core.TelemetryClient; import org.sonar.telemetry.legacy.TelemetryData; import org.sonar.telemetry.legacy.TelemetryDataJsonWriter; import org.sonar.telemetry.legacy.TelemetryDataLoader; import org.sonar.telemetry.metrics.TelemetryMetricsLoader; -import org.sonar.telemetry.metrics.schema.BaseMessage; -import org.sonar.telemetry.metrics.util.MessageSerializer; +import org.sonar.telemetry.core.schema.BaseMessage; +import org.sonar.telemetry.core.MessageSerializer; import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_ENABLE; import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_FREQUENCY_IN_SECONDS; diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/TelemetryMetricsLoader.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/TelemetryMetricsLoader.java index aeb5f3c3074..7909baaaa98 100644 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/TelemetryMetricsLoader.java +++ b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/TelemetryMetricsLoader.java @@ -35,8 +35,8 @@ import org.sonar.db.DbSession; import org.sonar.db.telemetry.TelemetryMetricsSentDto; import org.sonar.telemetry.core.Dimension; import org.sonar.telemetry.core.TelemetryDataProvider; -import org.sonar.telemetry.metrics.schema.BaseMessage; -import org.sonar.telemetry.metrics.schema.Metric; +import org.sonar.telemetry.core.schema.BaseMessage; +import org.sonar.telemetry.core.schema.Metric; import org.sonar.telemetry.metrics.util.SentMetricsStorage; public class TelemetryMetricsLoader { diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/TelemetryMetricsMapper.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/TelemetryMetricsMapper.java index d282608d9c2..b005743172c 100644 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/TelemetryMetricsMapper.java +++ b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/TelemetryMetricsMapper.java @@ -25,11 +25,11 @@ import java.util.Set; import java.util.stream.Collectors; import org.sonar.telemetry.core.Granularity; import org.sonar.telemetry.core.TelemetryDataProvider; -import org.sonar.telemetry.metrics.schema.InstallationMetric; -import org.sonar.telemetry.metrics.schema.LanguageMetric; -import org.sonar.telemetry.metrics.schema.Metric; -import org.sonar.telemetry.metrics.schema.ProjectMetric; -import org.sonar.telemetry.metrics.schema.UserMetric; +import org.sonar.telemetry.core.schema.InstallationMetric; +import org.sonar.telemetry.core.schema.LanguageMetric; +import org.sonar.telemetry.core.schema.Metric; +import org.sonar.telemetry.core.schema.ProjectMetric; +import org.sonar.telemetry.core.schema.UserMetric; public class TelemetryMetricsMapper { diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/BaseMessage.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/BaseMessage.java deleted file mode 100644 index 451480a8a90..00000000000 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/BaseMessage.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Objects; -import java.util.Set; -import org.sonar.telemetry.core.Dimension; - -public class BaseMessage { - @JsonProperty("message_uuid") - private String messageUuid; - - @JsonProperty("installation_id") - private String installationId; - - @JsonProperty("dimension") - private Dimension dimension; - - @JsonProperty("metric_values") - private Set metrics; - - protected BaseMessage(String messageUuid, String installationId, Dimension dimension, Set metrics) { - this.messageUuid = messageUuid; - this.installationId = installationId; - this.dimension = dimension; - this.metrics = metrics; - } - - public String getMessageUuid() { - return messageUuid; - } - - public String getInstallationId() { - return installationId; - } - - public Dimension getDimension() { - return dimension; - } - - public Set getMetrics() { - return metrics; - } - - public static class Builder { - private String messageUuid; - private String installationId; - private Dimension dimension; - private Set metrics; - - public Builder setMessageUuid(String messageUuid) { - this.messageUuid = messageUuid; - return this; - } - - public Builder setInstallationId(String installationId) { - this.installationId = installationId; - return this; - } - - public Builder setDimension(Dimension dimension) { - this.dimension = dimension; - return this; - } - - public Builder setMetrics(Set metrics) { - this.metrics = metrics; - return this; - } - - public BaseMessage build() { - Objects.requireNonNull(messageUuid, "messageUuid must be specified"); - Objects.requireNonNull(installationId, "installationId must be specified"); - Objects.requireNonNull(dimension, "dimension must be specified"); - Objects.requireNonNull(metrics, "metrics must be specified"); - - return new BaseMessage(messageUuid, installationId, dimension, metrics); - } - } -} diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/InstallationMetric.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/InstallationMetric.java deleted file mode 100644 index 68dc2e83590..00000000000 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/InstallationMetric.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import javax.annotation.Nullable; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -public class InstallationMetric extends Metric { - - public InstallationMetric(String key, @Nullable Object value, TelemetryDataType type, Granularity granularity) { - this.key = key; - this.value = value; - this.type = type; - this.granularity = granularity; - } - -} diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/LanguageMetric.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/LanguageMetric.java deleted file mode 100644 index 236c31d7d38..00000000000 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/LanguageMetric.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import com.fasterxml.jackson.annotation.JsonProperty; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -public class LanguageMetric extends Metric { - - @JsonProperty("language") - private String language; - - public LanguageMetric(String key, Object value, String language, TelemetryDataType type, Granularity granularity) { - this.key = key; - this.value = value; - this.language = language; - this.type = type; - this.granularity = granularity; - } - - public String getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } -} diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/Metric.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/Metric.java deleted file mode 100644 index ad5c17f5194..00000000000 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/Metric.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import com.fasterxml.jackson.annotation.JsonProperty; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -public abstract class Metric { - @JsonProperty("key") - protected String key; - - @JsonProperty("value") - protected Object value; - - @JsonProperty("type") - protected TelemetryDataType type; - - @JsonProperty("granularity") - protected Granularity granularity; - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public Object getValue() { - return value; - } - - public void setValue(Object value) { - this.value = value; - } - - public TelemetryDataType getType() { - return type; - } - - public void setType(TelemetryDataType type) { - this.type = type; - } - - public Granularity getGranularity() { - return granularity; - } - - public void setGranularity(Granularity granularity) { - this.granularity = granularity; - } -} diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/ProjectMetric.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/ProjectMetric.java deleted file mode 100644 index fc9ff8e4cd4..00000000000 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/ProjectMetric.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import com.fasterxml.jackson.annotation.JsonProperty; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -public class ProjectMetric extends Metric { - - @JsonProperty("project_uuid") - private String projectUuid; - - public ProjectMetric(String key, Object value, String projectUuid, TelemetryDataType type, Granularity granularity) { - this.key = key; - this.value = value; - this.projectUuid = projectUuid; - this.type = type; - this.granularity = granularity; - } - - public String getProjectUuid() { - return projectUuid; - } - - public void setProjectUuid(String projectUuid) { - this.projectUuid = projectUuid; - } - -} diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/UserMetric.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/UserMetric.java deleted file mode 100644 index 2af08ca023e..00000000000 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/UserMetric.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import com.fasterxml.jackson.annotation.JsonProperty; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -public class UserMetric extends Metric { - - @JsonProperty("user_uuid") - private String userUuid; - - public UserMetric(String key, Object value, String userUuid, TelemetryDataType type, Granularity granularity) { - this.key = key; - this.value = value; - this.userUuid = userUuid; - this.type = type; - this.granularity = granularity; - } - - public String getUserUuid() { - return userUuid; - } - - public void setUserUuid(String userUuid) { - this.userUuid = userUuid; - } - -} diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/package-info.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/package-info.java deleted file mode 100644 index d1b9bb184e6..00000000000 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/schema/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -@ParametersAreNonnullByDefault -package org.sonar.telemetry.metrics.schema; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/util/MessageSerializer.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/util/MessageSerializer.java deleted file mode 100644 index a55c771552c..00000000000 --- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/metrics/util/MessageSerializer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.io.UncheckedIOException; -import org.sonar.telemetry.metrics.schema.BaseMessage; - -public class MessageSerializer { - - private MessageSerializer() { - throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); - } - - public static String serialize(BaseMessage message) { - ObjectMapper mapper = new ObjectMapper(); - try { - return mapper.writeValueAsString(message); - } catch (IOException ioException) { - throw new UncheckedIOException(ioException); - } - } - -} diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryClientCompressionTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryClientCompressionTest.java index 6ac5d708553..6297d2e8cea 100644 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryClientCompressionTest.java +++ b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryClientCompressionTest.java @@ -31,6 +31,7 @@ import okio.Okio; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.sonar.api.config.internal.MapSettings; +import org.sonar.telemetry.core.TelemetryClient; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_METRICS_URL; diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryClientTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryClientTest.java deleted file mode 100644 index ffb94d77fac..00000000000 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryClientTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry; - -import java.io.IOException; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okio.Buffer; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.sonar.api.config.internal.MapSettings; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_COMPRESSION; -import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_METRICS_URL; -import static org.sonar.process.ProcessProperties.Property.SONAR_TELEMETRY_URL; - -class TelemetryClientTest { - - private static final String JSON = "{\"key\":\"value\"}"; - private static final String TELEMETRY_URL = "https://telemetry.com/url"; - private static final String METRICS_TELEMETRY_URL = "https://telemetry.com/url/metrics"; - - private final OkHttpClient okHttpClient = mock(OkHttpClient.class, RETURNS_DEEP_STUBS); - private final MapSettings settings = new MapSettings(); - - private final TelemetryClient underTest = new TelemetryClient(okHttpClient, settings.asConfig()); - - @BeforeEach - void setProperties() { - settings.setProperty(SONAR_TELEMETRY_URL.getKey(), TELEMETRY_URL); - settings.setProperty(SONAR_TELEMETRY_METRICS_URL.getKey(), METRICS_TELEMETRY_URL); - } - - @Test - void upload() throws IOException { - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(Request.class); - settings.setProperty(SONAR_TELEMETRY_COMPRESSION.getKey(), false); - underTest.start(); - - underTest.upload(JSON); - - verify(okHttpClient).newCall(requestCaptor.capture()); - Request request = requestCaptor.getValue(); - assertThat(request.method()).isEqualTo("POST"); - assertThat(request.body().contentType()).isEqualTo(MediaType.parse("application/json; charset=utf-8")); - Buffer body = new Buffer(); - request.body().writeTo(body); - assertThat(body.readUtf8()).isEqualTo(JSON); - assertThat(request.url()).hasToString(TELEMETRY_URL); - } - - @Test - void uploadMetric() throws IOException { - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(Request.class); - settings.setProperty(SONAR_TELEMETRY_COMPRESSION.getKey(), false); - underTest.start(); - - underTest.uploadMetric(JSON); - - verify(okHttpClient).newCall(requestCaptor.capture()); - Request request = requestCaptor.getValue(); - assertThat(request.method()).isEqualTo("POST"); - assertThat(request.body().contentType()).isEqualTo(MediaType.parse("application/json; charset=utf-8")); - Buffer body = new Buffer(); - request.body().writeTo(body); - assertThat(body.readUtf8()).isEqualTo(JSON); - assertThat(request.url()).hasToString(METRICS_TELEMETRY_URL); - } - - @Test - void opt_out() throws IOException { - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(Request.class); - underTest.start(); - - underTest.optOut(JSON); - - verify(okHttpClient).newCall(requestCaptor.capture()); - Request request = requestCaptor.getValue(); - assertThat(request.method()).isEqualTo("DELETE"); - assertThat(request.body().contentType()).isEqualTo(MediaType.parse("application/json; charset=utf-8")); - Buffer body = new Buffer(); - request.body().writeTo(body); - assertThat(body.readUtf8()).isEqualTo(JSON); - assertThat(request.url()).hasToString(TELEMETRY_URL); - } -} diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryDaemonTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryDaemonTest.java index bcee58e5c6f..0ae19fc5b24 100644 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryDaemonTest.java +++ b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryDaemonTest.java @@ -35,6 +35,7 @@ import org.sonar.server.property.InternalProperties; import org.sonar.server.property.MapInternalProperties; import org.sonar.server.util.GlobalLockManager; import org.sonar.server.util.GlobalLockManagerImpl; +import org.sonar.telemetry.core.TelemetryClient; import org.sonar.telemetry.legacy.TelemetryData; import org.sonar.telemetry.legacy.TelemetryDataJsonWriter; import org.sonar.telemetry.legacy.TelemetryDataLoader; diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/TelemetryMetricsMapperTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/TelemetryMetricsMapperTest.java index d822623d167..d311a0e88a7 100644 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/TelemetryMetricsMapperTest.java +++ b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/TelemetryMetricsMapperTest.java @@ -28,11 +28,11 @@ import org.sonar.telemetry.core.Dimension; import org.sonar.telemetry.core.Granularity; import org.sonar.telemetry.core.TelemetryDataProvider; import org.sonar.telemetry.core.TelemetryDataType; -import org.sonar.telemetry.metrics.schema.InstallationMetric; -import org.sonar.telemetry.metrics.schema.LanguageMetric; -import org.sonar.telemetry.metrics.schema.Metric; -import org.sonar.telemetry.metrics.schema.ProjectMetric; -import org.sonar.telemetry.metrics.schema.UserMetric; +import org.sonar.telemetry.core.schema.InstallationMetric; +import org.sonar.telemetry.core.schema.LanguageMetric; +import org.sonar.telemetry.core.schema.Metric; +import org.sonar.telemetry.core.schema.ProjectMetric; +import org.sonar.telemetry.core.schema.UserMetric; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/BaseMessageTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/BaseMessageTest.java deleted file mode 100644 index e702018f37e..00000000000 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/BaseMessageTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.sonar.telemetry.core.Dimension; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.tuple; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -class BaseMessageTest { - - @Test - void build() { - BaseMessage message = new BaseMessage.Builder() - .setMessageUuid("123e4567-e89b-12d3-a456-426614174000") - .setInstallationId("installation-id") - .setDimension(Dimension.INSTALLATION) - .setMetrics(installationMetrics()) - .build(); - - assertThat(message.getMessageUuid()).isEqualTo("123e4567-e89b-12d3-a456-426614174000"); - assertThat(message.getInstallationId()).isEqualTo("installation-id"); - assertThat(message.getDimension()).isEqualTo(Dimension.INSTALLATION); - Set installationMetrics = (Set) (Set) message.getMetrics(); - assertThat(installationMetrics) - .extracting(InstallationMetric::getKey, InstallationMetric::getGranularity, InstallationMetric::getType, InstallationMetric::getValue) - .containsExactlyInAnyOrder( - tuple("key-0", Granularity.DAILY, TelemetryDataType.INTEGER, 0), - tuple("key-1", Granularity.DAILY, TelemetryDataType.INTEGER, 1), - tuple("key-2", Granularity.DAILY, TelemetryDataType.INTEGER, 2) - ); - } - - @ParameterizedTest - @MethodSource("invalidBaseMessageProvider") - void build_invalidCases(BaseMessage.Builder builder, String expectedErrorMessage) { - Exception exception = assertThrows(NullPointerException.class, builder::build); - assertEquals(expectedErrorMessage, exception.getMessage()); - } - - private static Stream invalidBaseMessageProvider() { - return Stream.of( - Arguments.of( - new BaseMessage.Builder() - .setInstallationId("installation-id") - .setDimension(Dimension.INSTALLATION) - .setMetrics(installationMetrics()), - "messageUuid must be specified" - ), - Arguments.of( - new BaseMessage.Builder() - .setMessageUuid("some-uuid") - .setInstallationId("installation-id") - .setMetrics(installationMetrics()), - "dimension must be specified" - ), - Arguments.of( - new BaseMessage.Builder() - .setMessageUuid("some-uuid") - .setDimension(Dimension.INSTALLATION) - .setMetrics(installationMetrics()), - "installationId must be specified" - ) - ); - } - - private static Set installationMetrics() { - return IntStream.range(0, 3) - .mapToObj(i -> new InstallationMetric( - "key-" + i, - i, - TelemetryDataType.INTEGER, - Granularity.DAILY - )).collect(Collectors.toSet()); - } -} diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/InstallationMetricTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/InstallationMetricTest.java deleted file mode 100644 index b3ecd01b7a0..00000000000 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/InstallationMetricTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import org.junit.jupiter.api.Test; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -import static org.assertj.core.api.Assertions.assertThat; - -class InstallationMetricTest { - - @Test - void constructor() { - InstallationMetric metric = new InstallationMetric( - "installation-key-1", - "value", - TelemetryDataType.STRING, - Granularity.WEEKLY - ); - - assertThat(metric.getValue()).isEqualTo("value"); - assertThat(metric.getKey()).isEqualTo("installation-key-1"); - assertThat(metric.getGranularity()).isEqualTo(Granularity.WEEKLY); - assertThat(metric.getType()).isEqualTo(TelemetryDataType.STRING); - } - - @Test - void constructor_shouldAcceptNullValue() { - InstallationMetric metric = new InstallationMetric( - "installation-key-1", - null, - TelemetryDataType.STRING, - Granularity.WEEKLY - ); - - assertThat(metric.getValue()).isNull(); - assertThat(metric.getKey()).isEqualTo("installation-key-1"); - assertThat(metric.getGranularity()).isEqualTo(Granularity.WEEKLY); - assertThat(metric.getType()).isEqualTo(TelemetryDataType.STRING); - } - -} diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/LanguageMetricTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/LanguageMetricTest.java deleted file mode 100644 index 832ec3d8f03..00000000000 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/LanguageMetricTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import org.junit.jupiter.api.Test; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -import static org.assertj.core.api.Assertions.assertThat; - -class LanguageMetricTest { - - @Test - void gettersAndSetters() { - LanguageMetric metric = new LanguageMetric("ncloc", 100, "java", TelemetryDataType.INTEGER, Granularity.MONTHLY); - - assertThat(metric.getLanguage()).isEqualTo("java"); - assertThat(metric.getValue()).isEqualTo(100); - assertThat(metric.getKey()).isEqualTo("ncloc"); - assertThat(metric.getGranularity()).isEqualTo(Granularity.MONTHLY); - assertThat(metric.getType()).isEqualTo(TelemetryDataType.INTEGER); - } - -} diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/ProjectMetricTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/ProjectMetricTest.java deleted file mode 100644 index 7eb23fcfcf1..00000000000 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/ProjectMetricTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import org.junit.jupiter.api.Test; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -import static org.assertj.core.api.Assertions.assertThat; - -class ProjectMetricTest { - - @Test - void gettersAndSetters() { - ProjectMetric metric = new ProjectMetric( - "project-key-1", - 1.0998, - "project-uuid", - TelemetryDataType.FLOAT, - Granularity.DAILY - ); - - assertThat(metric.getValue()).isEqualTo(1.0998); - assertThat(metric.getKey()).isEqualTo("project-key-1"); - assertThat(metric.getGranularity()).isEqualTo(Granularity.DAILY); - assertThat(metric.getType()).isEqualTo(TelemetryDataType.FLOAT); - assertThat(metric.getProjectUuid()).isEqualTo("project-uuid"); - } - -} diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/UserMetricTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/UserMetricTest.java deleted file mode 100644 index 04466d0909f..00000000000 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/schema/UserMetricTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.telemetry.metrics.schema; - -import org.junit.jupiter.api.Test; -import org.sonar.telemetry.core.Granularity; -import org.sonar.telemetry.core.TelemetryDataType; - -import static org.assertj.core.api.Assertions.assertThat; - -class UserMetricTest { - - @Test - void gettersAndSetters() { - UserMetric metric = new UserMetric( - "user-key-1", - true, - "user-uuid", - TelemetryDataType.BOOLEAN, - Granularity.DAILY - ); - - assertThat(metric.getValue()).isEqualTo(true); - assertThat(metric.getKey()).isEqualTo("user-key-1"); - assertThat(metric.getGranularity()).isEqualTo(Granularity.DAILY); - assertThat(metric.getType()).isEqualTo(TelemetryDataType.BOOLEAN); - assertThat(metric.getUserUuid()).isEqualTo("user-uuid"); - } - -} diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/util/MessageSerializerTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/util/MessageSerializerTest.java index 57aa2e858e0..b3563e54368 100644 --- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/util/MessageSerializerTest.java +++ b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/metrics/util/MessageSerializerTest.java @@ -27,13 +27,14 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.sonar.telemetry.core.Dimension; import org.sonar.telemetry.core.Granularity; +import org.sonar.telemetry.core.MessageSerializer; import org.sonar.telemetry.core.TelemetryDataType; -import org.sonar.telemetry.metrics.schema.BaseMessage; -import org.sonar.telemetry.metrics.schema.InstallationMetric; -import org.sonar.telemetry.metrics.schema.LanguageMetric; -import org.sonar.telemetry.metrics.schema.Metric; -import org.sonar.telemetry.metrics.schema.ProjectMetric; -import org.sonar.telemetry.metrics.schema.UserMetric; +import org.sonar.telemetry.core.schema.BaseMessage; +import org.sonar.telemetry.core.schema.InstallationMetric; +import org.sonar.telemetry.core.schema.LanguageMetric; +import org.sonar.telemetry.core.schema.Metric; +import org.sonar.telemetry.core.schema.ProjectMetric; +import org.sonar.telemetry.core.schema.UserMetric; import static org.sonar.test.JsonAssert.assertJson; diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 95e617507d1..a947a254e31 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -291,7 +291,7 @@ import org.sonar.server.webhook.WebhookQGChangeEventListener; import org.sonar.server.webhook.ws.WebhooksWsModule; import org.sonar.server.ws.WebServiceEngine; import org.sonar.server.ws.ws.WebServicesWsModule; -import org.sonar.telemetry.TelemetryClient; +import org.sonar.telemetry.core.TelemetryClient; import org.sonar.telemetry.TelemetryDaemon; import org.sonar.telemetry.legacy.CloudUsageDataProvider; import org.sonar.telemetry.legacy.ProjectLocDistributionDataProvider; diff --git a/sonar-scanner-protocol/src/it/java/org/sonar/scanner/protocol/output/ScannerReportReaderIT.java b/sonar-scanner-protocol/src/it/java/org/sonar/scanner/protocol/output/ScannerReportReaderIT.java index b2bf317afa7..7aa0556e55a 100644 --- a/sonar-scanner-protocol/src/it/java/org/sonar/scanner/protocol/output/ScannerReportReaderIT.java +++ b/sonar-scanner-protocol/src/it/java/org/sonar/scanner/protocol/output/ScannerReportReaderIT.java @@ -373,4 +373,20 @@ public class ScannerReportReaderIT { public void return_null_when_no_file_source() { assertThat(underTest.readFileSource(UNKNOWN_COMPONENT_REF)).isNull(); } + + @Test + public void readTelemetryEntries_whenFileExists() { + ScannerReportWriter writer = new ScannerReportWriter(fileStructure); + ScannerReport.TelemetryEntry.Builder telemetry = ScannerReport.TelemetryEntry.newBuilder() + .setKey("key") + .setValue("value"); + writer.writeTelemetry(List.of(telemetry.build())); + + assertThat(underTest.readTelemetryEntries()).toIterable().hasSize(1); + } + + @Test + public void readTelemetryEntries_whenFileDoesntExists() { + assertThat(underTest.readTelemetryEntries()).toIterable().isEmpty(); + } } diff --git a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java index 4569a347891..807509a83fb 100644 --- a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java +++ b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java @@ -233,4 +233,12 @@ public class ScannerReportReader { public FileStructure getFileStructure() { return fileStructure; } + + public CloseableIterator readTelemetryEntries() { + File file = fileStructure.telemetryEntries(); + if (!fileExists(file)) { + return emptyCloseableIterator(); + } + return Protobuf.readStream(file, ScannerReport.TelemetryEntry.parser()); + } }