Skip to content

Commit 998e8c3

Browse files
authored
Treat literal "null" string as null when reading marketplace CSV (#6826)
* Treat literal "null" string as null when reading marketplace CSV RecipeMarketplaceReader already normalizes blank CSV values to null, but does not handle the literal string "null". This can cause RecipeBundle.version and requestedVersion to become the string "null" after a CSV round-trip, leading to display artifacts like "file:///path/to/file.yml:null" in mod config recipes list. * Rely on univocity default null handling instead of manual isBlank check Remove setNullValue("") so univocity returns null for empty fields natively, eliminating the need for the redundant StringUtils.isBlank check in the reading loop. * Restore the blank handling
1 parent 1f37320 commit 998e8c3

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

rewrite-core/src/main/java/org/openrewrite/marketplace/RecipeMarketplaceReader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ private void readRecipe(@Nullable String[] row, RecipeMarketplace marketplace, L
159159
String value = row[i];
160160
if (value != null) {
161161
value = value.trim();
162-
if (StringUtils.isBlank(value)) {
162+
if (StringUtils.isBlank(value) || "null".equals(value)) {
163163
value = null;
164164
}
165165
}

rewrite-core/src/test/java/org/openrewrite/marketplace/RecipeMarketplaceReaderTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,44 @@ void recipeInMultipleCategoriesHasSeparateBundleInstances() {
350350
assertThat(rtJsListing.getBundle().getRequestedVersion()).isEqualTo(requestedVersion);
351351
}
352352

353+
@Test
354+
void nullLiteralVersionIsTreatedAsNull() {
355+
// When a CSV contains the literal string "null" for version/requestedVersion,
356+
// the reader should treat it as null (not the string "null")
357+
RecipeMarketplace marketplace = new RecipeMarketplaceReader().fromCsv("""
358+
name,displayName,description,category,ecosystem,packageName,requestedVersion,version
359+
org.openrewrite.java.cleanup.UnnecessaryParentheses,Remove Unnecessary Parentheses,Removes unnecessary parentheses,Java Cleanup,maven,org.openrewrite:rewrite-java,null,null
360+
""");
361+
362+
RecipeListing listing = marketplace.getAllRecipes().iterator().next();
363+
RecipeBundle bundle = listing.getBundle();
364+
assertThat(bundle.getRequestedVersion()).isNull();
365+
assertThat(bundle.getVersion()).isNull();
366+
}
367+
368+
@Test
369+
void roundTripPreservesNullVersions() {
370+
// Create a marketplace with null versions, write to CSV, read back,
371+
// and verify versions remain null (not the string "null")
372+
RecipeMarketplace marketplace = new RecipeMarketplaceReader().fromCsv("""
373+
name,displayName,description,category,ecosystem,packageName
374+
org.openrewrite.java.cleanup.UnnecessaryParentheses,Remove Unnecessary Parentheses,Removes unnecessary parentheses,Java Cleanup,maven,org.openrewrite:rewrite-java
375+
""");
376+
377+
// Verify initial state has null versions
378+
RecipeListing listing = marketplace.getAllRecipes().iterator().next();
379+
assertThat(listing.getBundle().getVersion()).isNull();
380+
assertThat(listing.getBundle().getRequestedVersion()).isNull();
381+
382+
// Round-trip through CSV
383+
@Language("csv") String csv = new RecipeMarketplaceWriter().toCsv(marketplace);
384+
RecipeMarketplace roundTripped = new RecipeMarketplaceReader().fromCsv(csv);
385+
386+
RecipeListing rtListing = roundTripped.getAllRecipes().iterator().next();
387+
assertThat(rtListing.getBundle().getVersion()).isNull();
388+
assertThat(rtListing.getBundle().getRequestedVersion()).isNull();
389+
}
390+
353391
private void setVersionRecursive(RecipeMarketplace.Category category, String packageName,
354392
String requestedVersion, String version) {
355393
for (RecipeListing recipe : category.getRecipes()) {

0 commit comments

Comments
 (0)