30
30
import java .util .ArrayList ;
31
31
import java .util .Collection ;
32
32
import java .util .Collections ;
33
+ import java .util .HashMap ;
34
+ import java .util .HashSet ;
33
35
import java .util .List ;
34
36
import java .util .Map ;
35
37
import java .util .Set ;
36
38
import java .util .stream .Collectors ;
37
-
38
39
import org .junit .jupiter .api .Test ;
39
- import org .sonar .api .internal .apachecommons .io .FileUtils ;
40
40
import org .slf4j .Logger ;
41
41
import org .slf4j .LoggerFactory ;
42
+ import org .sonar .api .internal .apachecommons .io .FileUtils ;
42
43
import org .sonar .plugins .common .Check ;
43
44
import org .sonar .plugins .secrets .SecretsRulesDefinition ;
44
45
import org .sonar .plugins .secrets .api .SpecificationLoader ;
45
46
import org .sonar .plugins .secrets .configuration .model .Rule ;
46
47
47
- // This name is intentionally not ending in "Test" to not get picked up automatically
48
+ // This name is intentionally not ending in "Test" to not get picked up automatically by maven
48
49
@ SuppressWarnings ("java:S3577" )
49
50
class UpdatingSpecificationFilesGenerator {
50
51
52
+ private final static String CHECK_TESTS_PATH_PREFIX = String .join (File .separator , "src" , "test" , "java" , "org" , "sonar" , "plugins" , "secrets" , "checks" );
53
+ private final static String SECRETS_MODULE_PATH_PREFIX = String .join (File .separator , "src" , "main" , "java" , "org" , "sonar" , "plugins" , "secrets" );
54
+ private final static String SECRETS_MODULE_RESOURCE_PATH_PREFIX = String .join (File .separator , "src" , "main" , "resources" , "org" , "sonar" );
55
+ private final static String SPECIFICATION_FILES_PATH = String .join (File .separator , SECRETS_MODULE_RESOURCE_PATH_PREFIX , "plugins" , "secrets" );
56
+ private final static String CHECK_PATH_PREFIX = String .join (File .separator , SECRETS_MODULE_PATH_PREFIX , "checks" );
57
+ private final static String TEMPLATE_PATH_PREFIX = String .join (File .separator , "src" , "test" , "resources" , "templates" );
58
+ private final static String RSPEC_FILES_PATH_PREFIX = String .join (File .separator , SECRETS_MODULE_RESOURCE_PATH_PREFIX , "l10n" , "secrets" , "rules" , "secrets" );
51
59
private final Charset charset = StandardCharsets .UTF_8 ;
52
60
private static final Logger LOG = LoggerFactory .getLogger (UpdatingSpecificationFilesGenerator .class );
53
61
@@ -62,28 +70,34 @@ void firstStep() {
62
70
void secondStep () {
63
71
SpecificationLoader specificationLoader = new SpecificationLoader ();
64
72
65
- List <String > listOfAlreadyExistingKeys = retrieveAlreadyExistingKeys ();
66
- Set <String > setOfExistingCheckClassNames = SecretsRulesDefinition .checks ().stream ().map (Class ::getSimpleName ).collect (Collectors .toSet ());
73
+ Map <String , String > existingKeysMappedToFileName = retrieveAlreadyExistingKeys ();
67
74
68
75
Map <String , List <Rule >> rulesMappedToKey = specificationLoader .getRulesMappedToKey ();
69
76
70
- Set <String > keysToImplementChecksFor = rulesMappedToKey .keySet ();
71
- keysToImplementChecksFor .removeAll (listOfAlreadyExistingKeys );
77
+ Set <String > keysToImplementChecksFor = new HashSet <>(rulesMappedToKey .keySet ());
78
+ keysToImplementChecksFor .removeAll (existingKeysMappedToFileName .keySet ());
79
+
80
+ Set <String > keysNotUsedAnymore = new HashSet <>(existingKeysMappedToFileName .keySet ());
81
+ keysNotUsedAnymore .removeAll (rulesMappedToKey .keySet ());
72
82
73
83
List <String > newCheckNames = new ArrayList <>();
74
84
for (String rspecKey : keysToImplementChecksFor ) {
75
85
String checkName = rulesMappedToKey .get (rspecKey ).get (0 ).getProvider ().getMetadata ().getName ();
76
- checkName = sanitizeCheckName (checkName , setOfExistingCheckClassNames );
86
+ checkName = sanitizeCheckName (checkName , rspecKey , existingKeysMappedToFileName );
77
87
writeCheckFile (checkName , rspecKey );
78
88
writeCheckTestFile (checkName );
79
89
newCheckNames .add (checkName );
80
90
}
81
91
82
- writeUpdatedRulesDefinition (newCheckNames );
92
+ Set <String > checkNamesNotUsedAnymore = keysNotUsedAnymore .stream ().map (existingKeysMappedToFileName ::get ).collect (Collectors .toSet ());
93
+
94
+ removeUnusedChecks (keysNotUsedAnymore , existingKeysMappedToFileName );
95
+ writeUpdatedRulesDefinition (newCheckNames , checkNamesNotUsedAnymore );
96
+ constructFileForRulesAPI (keysToImplementChecksFor );
83
97
}
84
98
85
- private List < String > retrieveAlreadyExistingKeys () {
86
- List <String > rspecKeys = new ArrayList <>();
99
+ private Map < String , String > retrieveAlreadyExistingKeys () {
100
+ Map <String , String > keyToFileName = new HashMap <>();
87
101
88
102
List <Class <?>> checks = SecretsRulesDefinition .checks ();
89
103
for (Class <?> check : checks ) {
@@ -95,26 +109,26 @@ private List<String> retrieveAlreadyExistingKeys() {
95
109
LOG .error ("Error while retrieving already existing keys" );
96
110
throw new RuntimeException (e );
97
111
}
98
- rspecKeys . add (instantiatedCheck .ruleKey .rule ());
112
+ keyToFileName . put (instantiatedCheck .ruleKey .rule (), check . getSimpleName ());
99
113
}
100
114
101
- return rspecKeys ;
115
+ return keyToFileName ;
102
116
}
103
117
104
- private String sanitizeCheckName (String checkName , Set < String > existingClassNames ) {
118
+ private String sanitizeCheckName (String checkName , String rspecKey , Map < String , String > existingClassNames ) {
105
119
checkName = checkName .replaceAll ("[^a-zA-Z]" , "" );
106
120
checkName = checkName + "Check" ;
107
121
108
- if (existingClassNames .contains (checkName )) {
122
+ if (existingClassNames .containsValue (checkName )) {
109
123
checkName = checkName .replace ("Check" , "UniqueNameCheck" );
110
124
}
111
- existingClassNames .add ( checkName );
125
+ existingClassNames .put ( rspecKey , checkName );
112
126
return checkName ;
113
127
}
114
128
115
129
private void writeCheckFile (String checkName , String rspecKey ) {
116
- Path checkPath = Path .of ("src" , "main" , "java" , "org" , "sonar" , "plugins" , "secrets" , "checks" , checkName + ".java" );
117
- Path checkTemplatePath = Path .of ("src" , "test" , "resources" , "templates" , "GenericCheckTemplate.java" );
130
+ Path checkPath = Path .of (CHECK_PATH_PREFIX , checkName + ".java" );
131
+ Path checkTemplatePath = Path .of (TEMPLATE_PATH_PREFIX , "GenericCheckTemplate.java" );
118
132
try {
119
133
Files .copy (checkTemplatePath , checkPath );
120
134
@@ -127,13 +141,12 @@ private void writeCheckFile(String checkName, String rspecKey) {
127
141
throw new RuntimeException (e );
128
142
}
129
143
130
- String successMessage = String .format ("Successfully generated Check \" %s.java\" with rspecKey %s" , checkName , rspecKey );
131
- LOG .info (successMessage );
144
+ LOG .info ("Successfully generated Check \" {}.java\" with rspecKey {}" , checkName , rspecKey );
132
145
}
133
146
134
147
private void writeCheckTestFile (String checkName ) {
135
- Path checkTestPath = Path .of ("src" , "test" , "java" , "org" , "sonar" , "plugins" , "secrets" , "checks" , checkName + "Test.java" );
136
- Path checkTestTemplatePath = Path .of ("src" , "test" , "resources" , "templates" , "GenericCheckTestTemplate.java" );
148
+ Path checkTestPath = Path .of (CHECK_TESTS_PATH_PREFIX , checkName + "Test.java" );
149
+ Path checkTestTemplatePath = Path .of (TEMPLATE_PATH_PREFIX , "GenericCheckTestTemplate.java" );
137
150
try {
138
151
Files .copy (checkTestTemplatePath , checkTestPath );
139
152
@@ -146,19 +159,18 @@ private void writeCheckTestFile(String checkName) {
146
159
throw new RuntimeException (e );
147
160
}
148
161
149
- String successMessage = String .format ("Successfully generated Check \" %sTest.java\" " , checkName );
150
- LOG .info (successMessage );
162
+ LOG .info ("Successfully generated Check \" {}Test.java\" " , checkName );
151
163
}
152
164
153
165
private void writeSpecificationFileDefinition () {
154
- Path specificationsDirectoy = Path .of ("src" , "main" , "resources" , "org" , "sonar" , "plugins" , "secrets" , "configuration" );
166
+ Path specificationsDirectory = Path .of (SPECIFICATION_FILES_PATH , "configuration" );
155
167
String [] extensionsToSearchFor = new String [] {"yaml" };
156
- Collection <File > files = FileUtils .listFiles (new File (specificationsDirectoy .toUri ()), extensionsToSearchFor , false );
168
+ Collection <File > files = FileUtils .listFiles (new File (specificationsDirectory .toUri ()), extensionsToSearchFor , false );
157
169
158
170
List <String > listOfFileNames = files .stream ().map (File ::getName ).sorted ().collect (Collectors .toList ());
159
171
160
- Path specificationDefinitionPath = Path .of ("src" , "main" , "java" , "org" , "sonar" , "plugins" , "secrets" , "SecretsSpecificationFilesDefinition.java" );
161
- Path specificationDefinitionTemplatePath = Path .of ("src" , "test" , "resources" , "templates" , "SecretsSpecificationFilesDefinitionTemplate.java" );
172
+ Path specificationDefinitionPath = Path .of (SECRETS_MODULE_PATH_PREFIX , "SecretsSpecificationFilesDefinition.java" );
173
+ Path specificationDefinitionTemplatePath = Path .of (TEMPLATE_PATH_PREFIX , "SecretsSpecificationFilesDefinitionTemplate.java" );
162
174
163
175
try {
164
176
Files .copy (specificationDefinitionTemplatePath , specificationDefinitionPath , StandardCopyOption .REPLACE_EXISTING );
@@ -174,16 +186,46 @@ private void writeSpecificationFileDefinition() {
174
186
LOG .info ("Successfully generated SecretsSpecificationFilesDefinition" );
175
187
}
176
188
177
- private void writeUpdatedRulesDefinition (List <String > checkNames ) {
189
+ private void removeUnusedChecks (Set <String > keysNotUsedAnymore , Map <String , String > rspecKeysMappedToCheckNames ) {
190
+ for (String key : keysNotUsedAnymore ) {
191
+ String filename = rspecKeysMappedToCheckNames .get (key );
192
+
193
+ if (filename != null ) {
194
+ removeUnusedCheck (filename , key );
195
+ }
196
+ }
197
+ }
198
+
199
+ private void removeUnusedCheck (String checkName , String rspecKey ) {
200
+ Path checkPath = Path .of (CHECK_PATH_PREFIX , checkName + ".java" );
201
+ Path checkTestPath = Path .of (CHECK_TESTS_PATH_PREFIX , checkName + "Test.java" );
202
+ Path rspecJson = Path .of (RSPEC_FILES_PATH_PREFIX , rspecKey + ".json" );
203
+ Path rspecHtml = Path .of (RSPEC_FILES_PATH_PREFIX , rspecKey + ".html" );
204
+
205
+ try {
206
+ Files .delete (checkPath );
207
+ Files .delete (checkTestPath );
208
+ Files .delete (rspecJson );
209
+ Files .delete (rspecHtml );
210
+ } catch (IOException e ) {
211
+ LOG .error ("Error while deleting Check with name \" " + checkName + "\" , please fix manually" , e );
212
+ throw new RuntimeException (e );
213
+ }
214
+
215
+ LOG .info ("Successfully removed Check \" {}\" with rspecKey %s" , checkName );
216
+ }
217
+
218
+ private void writeUpdatedRulesDefinition (List <String > checkNames , Set <String > checkNamesNotUsedAnymore ) {
178
219
Set <String > uniqueCheckNames = SecretsRulesDefinition .checks ().stream ().map (Class ::getSimpleName ).collect (Collectors .toSet ());
179
220
uniqueCheckNames .addAll (checkNames );
221
+ uniqueCheckNames .removeAll (checkNamesNotUsedAnymore );
180
222
181
223
List <String > updatedCheckNames = new ArrayList <>(uniqueCheckNames );
182
224
183
225
Collections .sort (updatedCheckNames );
184
226
185
- Path checkTestTemplatePath = Path .of ("src" , "test" , "resources" , "templates" , "SecretsRulesDefinitionTemplate.java" );
186
- Path rulesDefPath = Path .of ("src" , "main" , "java" , "org" , "sonar" , "plugins" , "secrets" , "SecretsRulesDefinition.java" );
227
+ Path checkTestTemplatePath = Path .of (TEMPLATE_PATH_PREFIX , "SecretsRulesDefinitionTemplate.java" );
228
+ Path rulesDefPath = Path .of (SECRETS_MODULE_PATH_PREFIX , "SecretsRulesDefinition.java" );
187
229
try {
188
230
Files .copy (checkTestTemplatePath , rulesDefPath , StandardCopyOption .REPLACE_EXISTING );
189
231
@@ -252,4 +294,28 @@ private String generateImportsFor(List<String> checkNames) {
252
294
}
253
295
return sb .toString ();
254
296
}
297
+
298
+ private void constructFileForRulesAPI (Set <String > keysToUpdateRuleAPIFor ) {
299
+ Path pathToWriteUpdateFileTo = Path .of (TEMPLATE_PATH_PREFIX , "rspecKeysToUpdate.txt" );
300
+ String content = generateContentForRulesAPIUpdateFile (keysToUpdateRuleAPIFor );
301
+
302
+ try {
303
+ Files .write (pathToWriteUpdateFileTo , content .getBytes (charset ));
304
+ } catch (IOException e ) {
305
+ LOG .error ("Error when trying to write update file ruleAPI, please fix manually" , e );
306
+ throw new RuntimeException (e );
307
+ }
308
+
309
+ LOG .info ("Successfully generated rule-api update file" );
310
+ }
311
+
312
+ private String generateContentForRulesAPIUpdateFile (Set <String > keysToUpdateRuleAPIFor ) {
313
+ StringBuilder sb = new StringBuilder ();
314
+
315
+ for (String key : keysToUpdateRuleAPIFor ) {
316
+ sb .append (key );
317
+ sb .append (System .lineSeparator ());
318
+ }
319
+ return sb .toString ();
320
+ }
255
321
}
0 commit comments