From 00df27b89f986682075323e52179d7df46585748 Mon Sep 17 00:00:00 2001 From: Sophio Japharidze Date: Tue, 24 Sep 2024 10:46:17 +0200 Subject: [PATCH] SLLS-266 correctly handle JSON values in settings --- .../ls/settings/SettingsManager.java | 31 +++++-- .../ls/settings/SettingsManagerTests.java | 80 +++++++++++++++++-- 2 files changed, 97 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/sonarsource/sonarlint/ls/settings/SettingsManager.java b/src/main/java/org/sonarsource/sonarlint/ls/settings/SettingsManager.java index 283e4bf70..2d13fb23c 100644 --- a/src/main/java/org/sonarsource/sonarlint/ls/settings/SettingsManager.java +++ b/src/main/java/org/sonarsource/sonarlint/ls/settings/SettingsManager.java @@ -20,8 +20,12 @@ package org.sonarsource.sonarlint.ls.settings; import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import java.net.URI; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -281,24 +285,39 @@ static Map updateProperties(@org.jetbrains.annotations.Nullable var analyzerProperties = (Map) (sonarLintSettingsMap == null ? Maps.newHashMap() : sonarLintSettingsMap.getOrDefault(ANALYZER_PROPERTIES, Maps.newHashMap())); - var analysisExcludes = (String) settingsMap.getOrDefault(ANALYSIS_EXCLUDES, ""); + var analysisExcludes = getStringValue(settingsMap, ANALYSIS_EXCLUDES, ""); forceIgnoreRazorFiles(analyzerProperties); - var solutionRelativePath = settingsMap.getOrDefault(DOTNET_DEFAULT_SOLUTION_PATH, "").toString(); + var solutionRelativePath = getStringValue(settingsMap, DOTNET_DEFAULT_SOLUTION_PATH, ""); if (!solutionRelativePath.isEmpty() && workspaceUri != null) { // uri: file:///Users/me/Documents/Sonar/roslyn // solutionPath: Roslyn.sln // we want: /Users/me/Documents/Sonar/roslyn/Roslyn.sln - analyzerProperties.put("sonar.cs.internal.solutionPath", Path.of(workspaceUri).resolve(solutionRelativePath).toAbsolutePath().toString()); + try { + analyzerProperties.put("sonar.cs.internal.solutionPath", Path.of(workspaceUri).resolve(solutionRelativePath).toAbsolutePath().toString()); + } catch (InvalidPathException e) { + analyzerProperties.put("sonar.cs.internal.solutionPath", ""); + } } - analyzerProperties.put("sonar.cs.internal.useNet6", settingsMap.getOrDefault(OMNISHARP_USE_MODERN_NET, "true").toString()); - analyzerProperties.put("sonar.cs.internal.loadProjectOnDemand", settingsMap.getOrDefault(OMNISHARP_LOAD_PROJECT_ON_DEMAND, "false").toString()); - analyzerProperties.put("sonar.cs.internal.loadProjectsTimeout", settingsMap.getOrDefault(OMNISHARP_PROJECT_LOAD_TIMEOUT, "60").toString()); + analyzerProperties.put("sonar.cs.internal.useNet6", getStringValue(settingsMap, OMNISHARP_USE_MODERN_NET, "true")); + analyzerProperties.put("sonar.cs.internal.loadProjectOnDemand", getStringValue(settingsMap, OMNISHARP_LOAD_PROJECT_ON_DEMAND, "false")); + analyzerProperties.put("sonar.cs.internal.loadProjectsTimeout", getStringValue(settingsMap, OMNISHARP_PROJECT_LOAD_TIMEOUT, "60")); settingsMap.put(ANALYZER_PROPERTIES, analyzerProperties); settingsMap.put(ANALYSIS_EXCLUDES, addVscodeExcludesToSonarLintExcludes(analysisExcludes, settingsMap)); return settingsMap; } + private static String getStringValue(Map settingsMap, String key, String defaultValue) { + String finalValue; + try { + var string = new Gson().fromJson((JsonElement) settingsMap.get(key), String.class); + finalValue = string == null || string.isEmpty() ? defaultValue : string; + } catch (JsonParseException e) { + finalValue = defaultValue; + } + return finalValue; + } + private static String addVscodeExcludesToSonarLintExcludes(String sonarLintExcludes, Map settingsMap) { var vscodeFilesExcludeMap = Utils.parseToMap(settingsMap.getOrDefault(VSCODE_FILE_EXCLUDES, new JsonObject())); var globPatterns = new StringBuilder(); diff --git a/src/test/java/org/sonarsource/sonarlint/ls/settings/SettingsManagerTests.java b/src/test/java/org/sonarsource/sonarlint/ls/settings/SettingsManagerTests.java index d6937699c..801751594 100644 --- a/src/test/java/org/sonarsource/sonarlint/ls/settings/SettingsManagerTests.java +++ b/src/test/java/org/sonarsource/sonarlint/ls/settings/SettingsManagerTests.java @@ -21,6 +21,7 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import java.io.File; @@ -731,10 +732,10 @@ void shouldUpdateAnalyzerProperties() { sonarLintSettings.add("disableTelemetry", new JsonPrimitive(false)); sonarLintSettings.add("focusOnNewCode", new JsonPrimitive(true)); Map settingsMap = new HashMap<>(Map.of(SONARLINT_CONFIGURATION_NAMESPACE, sonarLintSettings, - DOTNET_DEFAULT_SOLUTION_PATH, "Roslyn.sln", - OMNISHARP_USE_MODERN_NET, "true", - OMNISHARP_LOAD_PROJECT_ON_DEMAND, "false", - OMNISHARP_PROJECT_LOAD_TIMEOUT, "600")); + DOTNET_DEFAULT_SOLUTION_PATH, new JsonPrimitive("Roslyn.sln"), + OMNISHARP_USE_MODERN_NET, new JsonPrimitive("true"), + OMNISHARP_LOAD_PROJECT_ON_DEMAND, new JsonPrimitive("false"), + OMNISHARP_PROJECT_LOAD_TIMEOUT, new JsonPrimitive("600"))); var result = SettingsManager.updateProperties(workspaceUri, settingsMap); @@ -762,10 +763,10 @@ void shouldAddVSCodeExcludesInFileExclusions() { sonarLintSettings.add("disableTelemetry", new JsonPrimitive(false)); sonarLintSettings.add("focusOnNewCode", new JsonPrimitive(true)); Map settingsMap = new HashMap<>(Map.of(SONARLINT_CONFIGURATION_NAMESPACE, sonarLintSettings, - DOTNET_DEFAULT_SOLUTION_PATH, "Roslyn.sln", - OMNISHARP_USE_MODERN_NET, "true", - OMNISHARP_LOAD_PROJECT_ON_DEMAND, "false", - OMNISHARP_PROJECT_LOAD_TIMEOUT, "600", + DOTNET_DEFAULT_SOLUTION_PATH, new JsonPrimitive("Roslyn.sln"), + OMNISHARP_USE_MODERN_NET, new JsonPrimitive("true"), + OMNISHARP_LOAD_PROJECT_ON_DEMAND, new JsonPrimitive("false"), + OMNISHARP_PROJECT_LOAD_TIMEOUT, new JsonPrimitive("600"), VSCODE_FILE_EXCLUDES, vscodeExclusions)); var result = SettingsManager.updateProperties(workspaceUri, settingsMap); @@ -823,6 +824,69 @@ void shouldNotNotifyAboutNodeJsChange() { verify(backendService, never()).didChangeClientNodeJsPath(any()); } + @Test + void shouldUpdatePropertiesWithDefaultValuesWhenNullSettings() { + var workspaceUri = URI.create("file:///User/user/documents/project"); + var sonarLintSettings = new JsonObject(); + sonarLintSettings.add("disableTelemetry", new JsonPrimitive(false)); + sonarLintSettings.add("focusOnNewCode", new JsonPrimitive(true)); + Map settingsMap = new HashMap<>(Map.of(SONARLINT_CONFIGURATION_NAMESPACE, sonarLintSettings, + DOTNET_DEFAULT_SOLUTION_PATH, JsonNull.INSTANCE, + OMNISHARP_USE_MODERN_NET, JsonNull.INSTANCE, + OMNISHARP_LOAD_PROJECT_ON_DEMAND, JsonNull.INSTANCE, + OMNISHARP_PROJECT_LOAD_TIMEOUT, JsonNull.INSTANCE)); + + var result = SettingsManager.updateProperties(workspaceUri, settingsMap); + + var analyzerProperties = (Map) result.get(ANALYZER_PROPERTIES); + assertThat(analyzerProperties).contains(entry("sonar.cs.internal.useNet6", "true"), + entry("sonar.cs.internal.loadProjectOnDemand", "false"), + entry("sonar.cs.internal.loadProjectsTimeout", "60")); + assertThat(analyzerProperties.get("sonar.cs.internal.solutionPath")).isNull(); + } + + @Test + void shouldUpdatePropertiesWithDefaultValuesWhenEmptySettings() { + var workspaceUri = URI.create("file:///User/user/documents/project"); + var sonarLintSettings = new JsonObject(); + sonarLintSettings.add("disableTelemetry", new JsonPrimitive(false)); + sonarLintSettings.add("focusOnNewCode", new JsonPrimitive(true)); + Map settingsMap = new HashMap<>(Map.of(SONARLINT_CONFIGURATION_NAMESPACE, sonarLintSettings, + DOTNET_DEFAULT_SOLUTION_PATH, new JsonPrimitive(""), + OMNISHARP_USE_MODERN_NET, new JsonPrimitive(""), + OMNISHARP_LOAD_PROJECT_ON_DEMAND, new JsonPrimitive(""), + OMNISHARP_PROJECT_LOAD_TIMEOUT, new JsonPrimitive(""))); + + var result = SettingsManager.updateProperties(workspaceUri, settingsMap); + + var analyzerProperties = (Map) result.get(ANALYZER_PROPERTIES); + assertThat(analyzerProperties).contains(entry("sonar.cs.internal.useNet6", "true"), + entry("sonar.cs.internal.loadProjectOnDemand", "false"), + entry("sonar.cs.internal.loadProjectsTimeout", "60")); + assertThat(analyzerProperties.get("sonar.cs.internal.solutionPath")).isNull(); + } + + @Test + void shouldUpdatePropertiesWithDefaultValuesWhenParsingFails() { + var workspaceUri = URI.create("file:///User/user/documents/project"); + var sonarLintSettings = new JsonObject(); + sonarLintSettings.add("disableTelemetry", new JsonPrimitive(false)); + sonarLintSettings.add("focusOnNewCode", new JsonPrimitive(true)); + Map settingsMap = new HashMap<>(Map.of(SONARLINT_CONFIGURATION_NAMESPACE, sonarLintSettings, + DOTNET_DEFAULT_SOLUTION_PATH, new JsonObject(), + OMNISHARP_USE_MODERN_NET, new JsonObject(), + OMNISHARP_LOAD_PROJECT_ON_DEMAND, new JsonObject(), + OMNISHARP_PROJECT_LOAD_TIMEOUT, new JsonObject())); + + var result = SettingsManager.updateProperties(workspaceUri, settingsMap); + + var analyzerProperties = (Map) result.get(ANALYZER_PROPERTIES); + assertThat(analyzerProperties).contains(entry("sonar.cs.internal.useNet6", "true"), + entry("sonar.cs.internal.loadProjectOnDemand", "false"), + entry("sonar.cs.internal.loadProjectsTimeout", "60")); + assertThat(analyzerProperties.get("sonar.cs.internal.solutionPath")).isNull(); + } + private static Map fromJsonString(String json) { return Utils.parseToMap(new Gson().fromJson(json, JsonElement.class)); }