Skip to content

Commit d5a9ecf

Browse files
authored
JS-1447 Generate built-in profiles from rspec metadata (#6583)
1 parent d0c0bbe commit d5a9ecf

5 files changed

Lines changed: 249 additions & 67 deletions

File tree

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@
321321
<exclude>**/*.d.ts</exclude>
322322
<!-- Folders -->
323323
<exclude>its/**/*</exclude>
324+
<exclude>tsgolint/**/*</exclude>
324325
<!-- Generated -->
325326
<exclude>**/bin/**/*</exclude>
326327
<exclude>**/dist/**/*</exclude>

sonar-plugin/css/src/main/resources/org/sonar/l10n/css/rules/rules.json

Lines changed: 0 additions & 31 deletions
This file was deleted.

sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/JavaScriptProfilesDefinition.java

Lines changed: 87 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@
1616
*/
1717
package org.sonar.plugins.javascript;
1818

19+
import com.google.gson.JsonArray;
20+
import com.google.gson.JsonObject;
21+
import com.google.gson.JsonParser;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.io.InputStreamReader;
1925
import java.lang.reflect.InvocationTargetException;
2026
import java.lang.reflect.Method;
27+
import java.nio.charset.StandardCharsets;
2128
import java.util.ArrayList;
2229
import java.util.Collections;
2330
import java.util.EnumMap;
24-
import java.util.HashMap;
2531
import java.util.List;
2632
import java.util.Map;
2733
import java.util.Set;
@@ -45,15 +51,12 @@ public class JavaScriptProfilesDefinition implements BuiltInQualityProfilesDefin
4551

4652
public static final String RESOURCE_PATH = "org/sonar/l10n/javascript/rules/javascript";
4753
public static final String SONAR_WAY_JSON = RESOURCE_PATH + "/Sonar_way_profile.json";
54+
public static final String PROFILES_JSON = RESOURCE_PATH + "/profiles.json";
4855

49-
private static final Map<String, String> PROFILES = new HashMap<>();
56+
private static final List<ProfileDefinition> PROFILES = loadProfiles();
5057
static final String SONAR_JASMIN_RULES_CLASS_NAME = "com.sonar.plugins.jasmin.api.JsRules";
5158
public static final String SECURITY_RULE_KEYS_METHOD_NAME = "getSecurityRuleKeys";
5259

53-
static {
54-
PROFILES.put(SONAR_WAY, SONAR_WAY_JSON);
55-
}
56-
5760
private static final Map<Language, String> REPO_BY_LANGUAGE = new EnumMap<>(Language.class);
5861

5962
static {
@@ -84,15 +87,24 @@ public JavaScriptProfilesDefinition(ProfileRegistrar[] profileRegistrars) {
8487

8588
@Override
8689
public void define(Context context) {
87-
createSonarWayProfile(JavaScriptLanguage.KEY, context);
88-
createSonarWayProfile(TypeScriptLanguage.KEY, context);
90+
createProfiles(JavaScriptLanguage.KEY, context);
91+
createProfiles(TypeScriptLanguage.KEY, context);
8992
}
9093

91-
private void createSonarWayProfile(String language, Context context) {
92-
NewBuiltInQualityProfile newProfile = context.createBuiltInQualityProfile(SONAR_WAY, language);
93-
activateBuiltInRules(newProfile);
94-
activateAdditionalRules(newProfile);
95-
activateSecurityRules(newProfile, language);
94+
private void createProfiles(String language, Context context) {
95+
PROFILES.forEach(profile -> createProfile(profile, language, context));
96+
}
97+
98+
private void createProfile(ProfileDefinition profile, String language, Context context) {
99+
NewBuiltInQualityProfile newProfile = context.createBuiltInQualityProfile(
100+
profile.name(),
101+
language
102+
);
103+
activateBuiltInRules(newProfile, profile.path());
104+
if (SONAR_WAY.equals(profile.name())) {
105+
activateAdditionalRules(newProfile);
106+
activateSecurityRules(newProfile, language);
107+
}
96108
newProfile.done();
97109
}
98110

@@ -101,19 +113,57 @@ private void createSonarWayProfile(String language, Context context) {
101113
*
102114
* @param profile profile to activate the rules for
103115
*/
104-
private static void activateBuiltInRules(NewBuiltInQualityProfile profile) {
116+
private static void activateBuiltInRules(
117+
NewBuiltInQualityProfile profile,
118+
String jsonProfilePath
119+
) {
105120
var language = Language.of(profile.language());
106121
String repositoryKey = REPO_BY_LANGUAGE.get(language);
107-
var jsonProfilePath = PROFILES.get(profile.name());
122+
Set<String> activeKeys = BuiltInQualityProfileJsonLoader.loadActiveKeysFromJsonProfile(
123+
jsonProfilePath
124+
);
108125

109-
Set<String> activeKeysForBothLanguages =
110-
BuiltInQualityProfileJsonLoader.loadActiveKeysFromJsonProfile(jsonProfilePath);
111126
ruleKeys(CheckList.getChecksForLanguage(language))
112127
.stream()
113-
.filter(activeKeysForBothLanguages::contains)
128+
.filter(activeKeys::contains)
114129
.forEach(key -> profile.activateRule(repositoryKey, key));
115130
}
116131

132+
private static List<ProfileDefinition> loadProfiles() {
133+
try (
134+
InputStream inputStream =
135+
JavaScriptProfilesDefinition.class.getClassLoader().getResourceAsStream(PROFILES_JSON)
136+
) {
137+
if (inputStream == null) {
138+
throw new IllegalStateException("Missing built-in quality profile index: " + PROFILES_JSON);
139+
}
140+
JsonArray profiles = JsonParser.parseReader(
141+
new InputStreamReader(inputStream, StandardCharsets.UTF_8)
142+
).getAsJsonArray();
143+
List<ProfileDefinition> definitions = new ArrayList<>();
144+
profiles.forEach(profile -> {
145+
JsonObject profileJson = profile.getAsJsonObject();
146+
String name = profileJson.get("name").getAsString();
147+
String fileName = profileJson.get("fileName").getAsString();
148+
definitions.add(new ProfileDefinition(name, RESOURCE_PATH + "/" + fileName));
149+
});
150+
if (
151+
definitions
152+
.stream()
153+
.noneMatch(
154+
profile -> SONAR_WAY.equals(profile.name()) && SONAR_WAY_JSON.equals(profile.path())
155+
)
156+
) {
157+
throw new IllegalStateException(
158+
"Missing required built-in quality profile in " + PROFILES_JSON + ": " + SONAR_WAY
159+
);
160+
}
161+
return definitions;
162+
} catch (IOException e) {
163+
throw new IllegalStateException("Failed to load built-in quality profile index", e);
164+
}
165+
}
166+
117167
/**
118168
* Activate additional rules that are provided by other plugins.
119169
*
@@ -175,4 +225,23 @@ private static Set<String> ruleKeys(List<Class<? extends EslintHook>> checks) {
175225
private static String securityRuleMessage(Exception e) {
176226
return "no security rules added to builtin profile: " + e.getMessage();
177227
}
228+
229+
private static class ProfileDefinition {
230+
231+
private final String name;
232+
private final String path;
233+
234+
private ProfileDefinition(String name, String path) {
235+
this.name = name;
236+
this.path = path;
237+
}
238+
239+
String name() {
240+
return name;
241+
}
242+
243+
String path() {
244+
return path;
245+
}
246+
}
178247
}

sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/JavaScriptProfilesDefinitionTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@
1717
package org.sonar.plugins.javascript;
1818

1919
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.sonar.plugins.javascript.JavaScriptProfilesDefinition.PROFILES_JSON;
2021
import static org.sonar.plugins.javascript.JavaScriptProfilesDefinition.SECURITY_RULE_KEYS_METHOD_NAME;
2122
import static org.sonar.plugins.javascript.JavaScriptProfilesDefinition.SONAR_JASMIN_RULES_CLASS_NAME;
2223
import static org.sonar.plugins.javascript.JavaScriptProfilesDefinition.SONAR_WAY_JSON;
2324
import static org.sonar.plugins.javascript.JavaScriptProfilesDefinition.getSecurityRuleKeys;
2425

26+
import com.google.gson.JsonParser;
2527
import com.sonar.plugins.jasmin.api.JsRules;
28+
import java.io.InputStreamReader;
2629
import java.lang.annotation.Annotation;
30+
import java.nio.charset.StandardCharsets;
31+
import java.util.HashSet;
2732
import java.util.List;
2833
import java.util.Set;
2934
import java.util.stream.Collectors;
@@ -154,6 +159,23 @@ void no_legacy_Key_in_profile_json() {
154159
assertThat(sonarWayKeys).isSubsetOf(allKeys);
155160
}
156161

162+
@Test
163+
void should_define_all_profiles_from_generated_index() {
164+
var profilesIndex = getClass().getClassLoader().getResourceAsStream(PROFILES_JSON);
165+
assertThat(profilesIndex).isNotNull();
166+
167+
Set<String> profileNames = new HashSet<>();
168+
JsonParser.parseReader(new InputStreamReader(profilesIndex, StandardCharsets.UTF_8))
169+
.getAsJsonArray()
170+
.forEach(profile -> profileNames.add(profile.getAsJsonObject().get("name").getAsString()));
171+
172+
assertThat(profileNames).isNotEmpty();
173+
profileNames.forEach(profileName -> {
174+
assertThat(context.profile(JavaScriptLanguage.KEY, profileName)).isNotNull();
175+
assertThat(context.profile(TypeScriptLanguage.KEY, profileName)).isNotNull();
176+
});
177+
}
178+
157179
@Test
158180
void should_contains_security_rules_if_available() {
159181
// no security rule available

0 commit comments

Comments
 (0)