From f44e5f653d8f47580907e88a3fea5fa4babc7260 Mon Sep 17 00:00:00 2001 From: aboyko Date: Thu, 26 Jan 2023 21:58:45 -0500 Subject: [PATCH] BootUpgrade quickfix improvements. Springify version validation. --- .../util/SimpleLanguageServer.java | 11 +- .../starter/LanguageServerAutoConf.java | 8 +- ...va => BootDiagnosticSeverityProvider.java} | 40 +++--- .../boot/app/BootLanguageServerBootApp.java | 40 ------ .../boot/app/BootVersionValidationConfig.java | 85 ++++++++++++ .../java/rewrite/RewriteRecipeRepository.java | 6 +- .../boot/java/rewrite/SpringBootUpgrade.java | 48 +++++-- .../BootVersionValidationEngine.java | 27 +--- .../AbstractDiagnosticValidator.java | 7 +- .../ProjectVersionDiagnosticProvider.java | 6 +- .../generations/UpdateBootVersion.java | 121 ++++++++++++++++++ .../generations/VersionValidators.java | 106 +-------------- .../java/rewrite/SpringBootUpgradeTest.java | 51 ++++++-- 13 files changed, 329 insertions(+), 227 deletions(-) rename headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/{ProblemSeverityConfigurer.java => BootDiagnosticSeverityProvider.java} (67%) create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index c3db70a36..54b27a9ae 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2022 VMware Inc. + * Copyright (c) 2016, 2023 VMware Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -652,8 +652,6 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC return workspace; } - private DiagnosticSeverityProvider severityProvider = DiagnosticSeverityProvider.DEFAULT; - /** * Keeps track of reconcile requests that have been requested but not yet started. * This is used to more efficiently deal with situation where many requests are fired @@ -783,6 +781,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC @Override public void accept(ReconcileProblem problem) { + DiagnosticSeverityProvider severityProvider = getDiagnosticSeverityProvider(); try { DiagnosticSeverity severity = severityProvider.getDiagnosticSeverity(problem); @@ -855,7 +854,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC } public DiagnosticSeverityProvider getDiagnosticSeverityProvider() { - return severityProvider; + return appContext.getBean(DiagnosticSeverityProvider.class); } @Override @@ -905,10 +904,6 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC return classpathListenerManager.addClasspathListener(classpathListener); } - public void setDiagnosticSeverityProvider(DiagnosticSeverityProvider severities) { - this.severityProvider = severities; - } - public void setCompletionFilter(Optional completionFilter) { this.completionFilter = completionFilter; } diff --git a/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java b/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java index 643558f18..247592dcb 100644 --- a/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java +++ b/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java @@ -46,13 +46,11 @@ public class LanguageServerAutoConf { @Bean(destroyMethod = "") public SimpleLanguageServer languageServer( LanguageServerProperties props, - Optional severities, Optional completionFilter, ApplicationContext appContext ) throws Exception { SimpleLanguageServer server = new SimpleLanguageServer(props.getExtensionId(), appContext, props); server.setCompletionFilter(completionFilter); - severities.ifPresent(server::setDiagnosticSeverityProvider); return server; } @@ -112,4 +110,10 @@ public class LanguageServerAutoConf { }; } + @ConditionalOnMissingBean(DiagnosticSeverityProvider.class) + @Bean + DiagnosticSeverityProvider defaultDiagnosticSeverityProvider() { + return DiagnosticSeverityProvider.DEFAULT; + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/ProblemSeverityConfigurer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootDiagnosticSeverityProvider.java similarity index 67% rename from headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/ProblemSeverityConfigurer.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootDiagnosticSeverityProvider.java index f23fce37d..7e6340067 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/ProblemSeverityConfigurer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootDiagnosticSeverityProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020 Pivotal, Inc. + * Copyright (c) 2020, 2023 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,36 +13,32 @@ package org.springframework.ide.vscode.boot.app; import java.util.HashMap; import java.util.Map; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; +import org.eclipse.lsp4j.DiagnosticSeverity; import org.springframework.ide.vscode.commons.languageserver.reconcile.DiagnosticSeverityProvider; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; import org.springframework.ide.vscode.commons.languageserver.util.Settings; -import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.util.Assert; import org.springframework.stereotype.Component; @Component -public class ProblemSeverityConfigurer implements InitializingBean { +public class BootDiagnosticSeverityProvider implements DiagnosticSeverityProvider { - @Autowired - private SimpleLanguageServer server; - - @Autowired private BootJavaConfig config; - @Override - public void afterPropertiesSet() throws Exception { + private Map severityOverrides; + + public BootDiagnosticSeverityProvider(BootJavaConfig config) { + this.config = config; config.addListener((x) -> configChanged()); configChanged(); } - - private void configChanged() { + + private synchronized void configChanged() { Settings settings = config.getRawSettings(); settings = settings.navigate("spring-boot", "ls", "problem"); - Map severityOverrides = new HashMap<>(); + severityOverrides = new HashMap<>(); for (String editorType : settings.keys()) { Settings problemConf = settings.navigate(editorType); for (String code : problemConf.keys()) { @@ -51,12 +47,14 @@ public class ProblemSeverityConfigurer implements InitializingBean { severityOverrides.put(code, ProblemSeverity.valueOf(severity)); } } - server.setDiagnosticSeverityProvider((ProblemType problem) -> { - ProblemSeverity severity = severityOverrides.get(problem.getCode()); - if (severity==null) { - severity = problem.getDefaultSeverity(); - } - return DiagnosticSeverityProvider.diagnosticSeverity(severity); - }); + } + + @Override + public synchronized DiagnosticSeverity getDiagnosticSeverity(ProblemType problem) { + ProblemSeverity severity = severityOverrides.get(problem.getCode()); + if (severity==null) { + severity = problem.getDefaultSeverity(); + } + return DiagnosticSeverityProvider.diagnosticSeverity(severity); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java index a9c91959b..9a50d3d50 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java @@ -26,8 +26,6 @@ import org.eclipse.lsp4j.CodeActionKind; import org.eclipse.lsp4j.CodeActionOptions; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.ServerCapabilities; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.SpringApplication; @@ -40,7 +38,6 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; -import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler; import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory; import org.springframework.ide.vscode.boot.common.RelaxedNameConfig; import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider; @@ -75,11 +72,9 @@ import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexPro import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex; import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry; import org.springframework.ide.vscode.boot.properties.completions.SpringPropertiesCompletionEngine; -import org.springframework.ide.vscode.boot.validation.BootVersionValidationEngine; import org.springframework.ide.vscode.boot.xml.SpringXMLCompletionEngine; import org.springframework.ide.vscode.boot.yaml.completions.ApplicationYamlAssistContext; import org.springframework.ide.vscode.boot.yaml.completions.SpringYamlCompletionEngine; -import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.LanguageServerRunner; import org.springframework.ide.vscode.commons.languageserver.java.FutureProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; @@ -129,8 +124,6 @@ public class BootLanguageServerBootApp { private static final String SERVER_NAME = "boot-language-server"; - private static final Logger log = LoggerFactory.getLogger(BootLanguageServerBootApp.class); - public static void main(String[] args) throws Exception { Hooks.onOperatorDebug(); System.setProperty(LanguageServerRunner.SYSPROP_LANGUAGESERVER_NAME, SERVER_NAME); //makes it easy to recognize language server processes - and set this as early as possible @@ -368,37 +361,4 @@ public class BootLanguageServerBootApp { recipeRepoOpt.orElse(null), projectFinder, server); } - @ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness") - @ConditionalOnProperty(prefix = "languageserver", name = "reconcile-only-opened-docs", havingValue = "false", matchIfMissing = true) - @Bean - ProjectReconcileScheduler bootVersionValidationScheduler(SimpleLanguageServer server, JavaProjectFinder projectFinder, BootJavaConfig config, ProjectObserver projectObserver) { - return new ProjectReconcileScheduler(server, new BootVersionValidationEngine(server, config, projectObserver, projectFinder), projectFinder) { - - @Override - protected void init() { - super.init(); - config.addListener(evt -> scheduleValidationForAllProjects()); - projectObserver.addListener(new ProjectObserver.Listener() { - - @Override - public void deleted(IJavaProject project) { - unscheduleValidation(project); - clear(project, true); - } - - @Override - public void created(IJavaProject project) { - scheduleValidation(project); - } - - @Override - public void changed(IJavaProject project) { - scheduleValidation(project); - } - }); - log.info("Started Boot Version reconciler"); - } - - }; - } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java new file mode 100644 index 000000000..cf3ae4980 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2023 VMware, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.app; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler; +import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; +import org.springframework.ide.vscode.boot.validation.BootVersionValidationEngine; +import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider; +import org.springframework.ide.vscode.boot.validation.generations.UpdateBootVersion; +import org.springframework.ide.vscode.boot.validation.generations.VersionValidator; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; + +@Configuration(proxyBeanMethods = false) +public class BootVersionValidationConfig { + + private static final Logger log = LoggerFactory.getLogger(BootVersionValidationConfig.class); + + @Bean UpdateBootVersion updateBootVersion(SimpleLanguageServer server, Optional bootUpgradeOpt) { + return new UpdateBootVersion(server.getDiagnosticSeverityProvider(), bootUpgradeOpt); + } + + @Bean ProjectVersionDiagnosticProvider projectVersionDiagnosticProvider(List validators) { + return new ProjectVersionDiagnosticProvider(validators); + } + + @ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness") + @ConditionalOnProperty(prefix = "languageserver", name = "reconcile-only-opened-docs", havingValue = "false", matchIfMissing = true) + @Bean + ProjectReconcileScheduler bootVersionValidationScheduler(SimpleLanguageServer server, + JavaProjectFinder projectFinder, BootJavaConfig config, ProjectObserver projectObserver, + ProjectVersionDiagnosticProvider diagnosticProvider) { + return new ProjectReconcileScheduler(server, + new BootVersionValidationEngine(server, config, projectObserver, projectFinder, diagnosticProvider), + projectFinder) { + + @Override + protected void init() { + super.init(); + config.addListener(evt -> scheduleValidationForAllProjects()); + projectObserver.addListener(new ProjectObserver.Listener() { + + @Override + public void deleted(IJavaProject project) { + unscheduleValidation(project); + clear(project, true); + } + + @Override + public void created(IJavaProject project) { + scheduleValidation(project); + } + + @Override + public void changed(IJavaProject project) { + scheduleValidation(project); + } + }); + log.info("Started Boot Version reconciler"); + } + + }; + } + + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java index 574bda774..2eda6f73a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -309,7 +309,7 @@ public class RewriteRecipeRepository implements ApplicationContextAware { for (RecipeCodeActionDescriptor d : descriptors) { TreeVisitor markVisitor = d.getMarkerVisitor(applicationContext); if (markVisitor != null) { - cu = (CompilationUnit) markVisitor.visit(cu, new InMemoryExecutionContext(e -> log.error("", e))); + cu = (CompilationUnit) markVisitor.visit(cu, new InMemoryExecutionContext(e -> log.error("Marker visitor failed!", e))); } } return cu; @@ -424,7 +424,7 @@ public class RewriteRecipeRepository implements ApplicationContextAware { }); List sources = projectParser.parse(absoluteProjectDir, getClasspathEntries(project)); server.getProgressService().progressEvent(progressToken, "Computing changes..."); - RecipeRun reciperun = r.run(sources, new InMemoryExecutionContext(e -> log.error("", e))); + RecipeRun reciperun = r.run(sources, new InMemoryExecutionContext(e -> log.error("Recipe execution failed", e))); List results = reciperun.getResults(); return ORDocUtils.createWorkspaceEdit(absoluteProjectDir, server.getTextDocumentService(), results); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgrade.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgrade.java index 7a341a25d..964af116d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgrade.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgrade.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.UUID; import org.eclipse.lsp4j.TextDocumentIdentifier; @@ -34,8 +36,8 @@ public class SpringBootUpgrade { final public static String CMD_UPGRADE_SPRING_BOOT = "sts/upgrade/spring-boot"; - private static final Map VERSION_TO_RECIPE_ID = Map.of( - "2.0", "org.openrewrite.java.spring.boot2.SpringBoot1To2Migration", + private final Map versionsToRecipeId = Map.of( + "2.0", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_0", "2.1", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_1", "2.2", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2", "2.3", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3", @@ -46,6 +48,11 @@ public class SpringBootUpgrade { "3.0", "org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0" ); +// private static final Map MAJOR_VERSION_TO_RECIPE_ID = Map.of( +// 2, "org.openrewrite.java.spring.boot2.SpringBoot1To2Migration", +// 3, "org.openrewrite.java.spring.boot3.SpringBoot2To3Migration" +// ); + private RewriteRecipeRepository recipeRepo; public SpringBootUpgrade(SimpleLanguageServer server, RewriteRecipeRepository recipeRepo, JavaProjectFinder projectFinder) { @@ -70,7 +77,7 @@ public class SpringBootUpgrade { + version.toMajorMinorVersionStr() + "' is newer or same as the target version '" + targetVersion.toMajorMinorVersionStr() + "'"); - return recipeRepo.loaded.thenComposeAsync(loade -> recipeRepo.apply( + return recipeRepo.loaded.thenComposeAsync(load -> recipeRepo.apply( createUpgradeRecipe(version, targetVersion), uri, UUID.randomUUID().toString() @@ -78,10 +85,10 @@ public class SpringBootUpgrade { }); } - static List createRecipeIdsChain(int major, int minor, int targetMajor, int targetMinor) { + static List createRecipeIdsChain(int major, int minor, int targetMajor, int targetMinor, Map versionToRecipeId) { List ids = new ArrayList<>(); for (int currentMajor = major, currentMinor = minor; targetMajor > currentMajor || (targetMajor == currentMajor && currentMinor <= targetMinor);) { - String recipeId = VERSION_TO_RECIPE_ID.get(createVersionString(currentMajor, currentMinor)); + String recipeId = versionToRecipeId.get(createVersionString(currentMajor, currentMinor)); if (recipeId == null) { currentMajor++; currentMinor = 0; @@ -101,11 +108,15 @@ public class SpringBootUpgrade { // patch version upgrade - treat as pom versions only upgrade recipe.doNext(new UpgradeDependencyVersion("org.springframework.boot", "*", version.getMajor() + "." + version.getMinor() + ".x", null, null, null)); recipe.doNext(new UpgradeParentVersion("org.springframework.boot", "spring-boot-starter-parent", version.getMajor() + "." + version.getMinor() + ".x", null, null)); - } else { - createRecipeIdsChain(version.getMajor(), version.getMinor(), targetVersion.getMajor(), targetVersion.getMinor()).stream() - .map(recipeRepo::getRecipe) - .filter(o -> o.isPresent()) - .forEach(o -> recipe.doNext(o.get())); + } else /*if (version.getMajor() == targetVersion.getMajor())*/ { + List recipedIds = createRecipeIdsChain(version.getMajor(), version.getMinor(), targetVersion.getMajor(), targetVersion.getMinor(), versionsToRecipeId); + if (!recipedIds.isEmpty()) { + String recipeId = recipedIds.get(recipedIds.size() - 1); + recipeRepo.getRecipe(recipeId).ifPresent(recipe::doNext); + } +// } else { +// String recipeId = MAJOR_VERSION_TO_RECIPE_ID.get(targetVersion.getMajor()); +// recipeRepo.getRecipe(recipeId).ifPresent(recipe::doNext); } if (recipe.getRecipeList().isEmpty()) { @@ -125,5 +136,18 @@ public class SpringBootUpgrade { return sb.toString(); } - + static String nearestAvailableMinorVersion(Version v, Set availableVersions) { + for (int major = v.getMajor(), minor = v.getMinor(); minor >= 0; minor--) { + String versionStr = createVersionString(major, minor); + if (availableVersions.contains(versionStr)) { + return versionStr; + } + } + return null; + } + + public Optional getNearestAvailableMinorVersion(Version v) { + return Optional.ofNullable(nearestAvailableMinorVersion(v, versionsToRecipeId.keySet())); + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/BootVersionValidationEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/BootVersionValidationEngine.java index cedfec750..3ae72e794 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/BootVersionValidationEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/BootVersionValidationEngine.java @@ -19,11 +19,6 @@ import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine; import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider; import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider.DiagnosticResult; -import org.springframework.ide.vscode.boot.validation.generations.SpringIoProjectsProvider; -import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsClient; -import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider; -import org.springframework.ide.vscode.boot.validation.generations.VersionValidators; -import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationPreferences; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; @@ -33,12 +28,15 @@ public class BootVersionValidationEngine implements IJavaProjectReconcileEngine private static final Logger log = LoggerFactory.getLogger(BootVersionValidationEngine.class); - private SimpleLanguageServer server; - private BootJavaConfig config; + private final SimpleLanguageServer server; + private final BootJavaConfig config; + private final ProjectVersionDiagnosticProvider diagnosticProvider; - public BootVersionValidationEngine(SimpleLanguageServer server, BootJavaConfig config, ProjectObserver projectObserver, JavaProjectFinder projectFinder) { + public BootVersionValidationEngine(SimpleLanguageServer server, BootJavaConfig config, ProjectObserver projectObserver, JavaProjectFinder projectFinder, + ProjectVersionDiagnosticProvider diagnosticProvider) { this.server = server; this.config = config; + this.diagnosticProvider = diagnosticProvider; } public void reconcile(IJavaProject project) { @@ -46,15 +44,6 @@ public class BootVersionValidationEngine implements IJavaProjectReconcileEngine log.debug("validating Spring Boot version on project: " + project.getElementName()); long start = System.currentTimeMillis(); - VersionValidationPreferences preferences = new VersionValidationPreferences(); - - String url = getSpringProjectsUrl(preferences); - SpringProjectsClient client = new SpringProjectsClient(url); - SpringProjectsProvider provider = new SpringIoProjectsProvider(client); - VersionValidators validators = new VersionValidators(server.getDiagnosticSeverityProvider(), provider); - - ProjectVersionDiagnosticProvider diagnosticProvider = new ProjectVersionDiagnosticProvider(validators); - try { DiagnosticResult result = diagnosticProvider.getDiagnostics(project); if (result != null && !result.getDiagnostics().isEmpty()) { @@ -72,10 +61,6 @@ public class BootVersionValidationEngine implements IJavaProjectReconcileEngine } } - private String getSpringProjectsUrl(VersionValidationPreferences preferences) { - return preferences.getSpringProjectsUrl(); - } - @Override public void clear(IJavaProject project) { // Build file diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java index 69a7d082f..773c19bf2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -23,7 +23,8 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.Diagnosti abstract public class AbstractDiagnosticValidator implements VersionValidator { - + private static final String BOOT_VERSION_VALIDATION_CODE = "BOOT_VERSION_VALIDATION_CODE"; + private final DiagnosticSeverityProvider diagnosticSeverityProvider; public AbstractDiagnosticValidator(DiagnosticSeverityProvider diagnosticSeverityProvider) { @@ -39,7 +40,7 @@ abstract public class AbstractDiagnosticValidator implements VersionValidator { } Diagnostic diagnostic = new Diagnostic(); - diagnostic.setCode(VersionValidators.BOOT_VERSION_VALIDATION_CODE); + diagnostic.setCode(BOOT_VERSION_VALIDATION_CODE); diagnostic.setMessage(diagnosticMessage.toString()); Range range = new Range(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java index e68b24194..bc618dea2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java @@ -28,9 +28,9 @@ public class ProjectVersionDiagnosticProvider { private static final Logger log = LoggerFactory.getLogger(ProjectVersionDiagnosticProvider.class); - private final VersionValidators validators; + private final List validators; - public ProjectVersionDiagnosticProvider(VersionValidators validators) { + public ProjectVersionDiagnosticProvider(List validators) { this.validators = validators; } @@ -51,7 +51,7 @@ public class ProjectVersionDiagnosticProvider { List diagnostics = new ArrayList(); - for (VersionValidator validator : validators.getValidators()) { + for (VersionValidator validator : validators) { try { Collection batch = validator.validate(javaProject, javaProjectVersion); if (batch != null) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java new file mode 100644 index 000000000..aefb81cc6 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2023 VMware, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.validation.generations; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.Diagnostic; +import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; +import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.Version; +import org.springframework.ide.vscode.commons.languageserver.reconcile.DiagnosticSeverityProvider; + +import com.google.common.collect.ImmutableList; + +public class UpdateBootVersion extends AbstractDiagnosticValidator { + + private Optional bootUpgradeOpt; + + public UpdateBootVersion(DiagnosticSeverityProvider diagnosticSeverityProvider, Optional bootUpgradeOpt) { + super(diagnosticSeverityProvider); + this.bootUpgradeOpt = bootUpgradeOpt; + } + + @Override + public Collection validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception { + List versions = CachedBootVersionsFromMavenCentral.getBootVersions(); + ImmutableList.Builder builder = ImmutableList.builder(); + validateMajorVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); + validateMinorVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); + validatePatchVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); + return builder.build(); + } + + private Optional validateMajorVersion(IJavaProject javaProject, Version javaProjectVersion, List sortedBootVersions) { + Version latest = VersionValidationUtils.getNewerLatestMajorRelease(sortedBootVersions, javaProjectVersion); + + if (latest != null) { + VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_MAJOR_VERSION; + + StringBuffer message = new StringBuffer(); + message.append("Newer major version of Spring Boot available: "); + message.append(latest.toString()); + + CodeAction ca = bootUpgradeOpt.flatMap(bu -> bu.getNearestAvailableMinorVersion(latest)).map(targetVersion -> { + CodeAction c = new CodeAction(); + c.setKind(CodeActionKind.QuickFix); + c.setTitle("Upgrade to Spring Boot " + targetVersion + " (executes the full project conversion recipe from OpenRewrite)"); + String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; + c.setCommand(new Command("Upgrade to Version " + targetVersion, commandId, + ImmutableList.of(javaProject.getLocationUri().toASCIIString(), targetVersion))); + return c; + }).orElse(null); + return Optional.ofNullable(createDiagnostic(ca, problemType, message.toString())); + } + return Optional.empty(); + } + + private Optional validateMinorVersion(IJavaProject javaProject, Version javaProjectVersion, List sortedBootVersions) { + Version latest = VersionValidationUtils.getNewerLatestMinorRelease(sortedBootVersions, javaProjectVersion); + + if (latest != null) { + VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_MINOR_VERSION; + + StringBuffer message = new StringBuffer(); + message.append("Newer minor version of Spring Boot available: "); + message.append(latest.toString()); + + CodeAction ca = bootUpgradeOpt.flatMap(bu -> bu.getNearestAvailableMinorVersion(latest)).map(targetVersion -> { + CodeAction c = new CodeAction(); + c.setKind(CodeActionKind.QuickFix); + c.setTitle("Upgrade to Spring Boot " + targetVersion + " (executes the full project conversion recipe from OpenRewrite)"); + String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; + c.setCommand(new Command("Upgrade to Version " + targetVersion, commandId, + ImmutableList.of(javaProject.getLocationUri().toASCIIString(), targetVersion))); + return c; + }).orElse(null); + + return Optional.ofNullable(createDiagnostic(ca, problemType, message.toString())); + } + return Optional.empty(); + } + + private Optional validatePatchVersion(IJavaProject javaProject, Version javaProjectVersion, List sortedBootVersions) { + Version latest = VersionValidationUtils.getNewerLatestPatchRelease(sortedBootVersions, javaProjectVersion); + + if (latest != null) { + VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_PATCH_VERSION; + + StringBuffer message = new StringBuffer(); + message.append("Newer patch version of Spring Boot available: "); + message.append(latest.toString()); + + CodeAction ca = bootUpgradeOpt.map(bu -> { + CodeAction c = new CodeAction(); + c.setKind(CodeActionKind.QuickFix); + c.setTitle("Upgrade to Spring Boot " + latest.toString() + " (Maven dependency version changes only)"); + String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; + c.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, + ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); + return c; + }).orElse(null); + + return Optional.ofNullable(createDiagnostic(ca, problemType, message.toString())); + } + return Optional.empty(); + } +} \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java index 143a39630..c548c7153 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java @@ -11,26 +11,17 @@ package org.springframework.ide.vscode.boot.validation.generations; import java.util.Arrays; -import java.util.Collection; import java.util.List; -import java.util.Optional; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.CodeActionKind; -import org.eclipse.lsp4j.Command; -import org.eclipse.lsp4j.Diagnostic; -import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; -import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; -import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.java.Version; import org.springframework.ide.vscode.commons.languageserver.reconcile.DiagnosticSeverityProvider; -import com.google.common.collect.ImmutableList; - +/* + * TODO: Once uncommented to bring in Spring.io generations API support: + * 1. Remaining validators must become beans. + * 2. I'd merge all 4 into one single validator + */ public class VersionValidators { - public static final String BOOT_VERSION_VALIDATION_CODE = "BOOT_VERSION_VALIDATION_CODE"; - private final VersionValidator[] validators; public VersionValidators(DiagnosticSeverityProvider diagnosticSeverityProvider, SpringProjectsProvider provider) { @@ -39,7 +30,6 @@ public class VersionValidators { // new UnsupportedCommercialValidator(diagnosticSeverityProvider, provider), // new UnsupportedOssValidator(diagnosticSeverityProvider, provider), // new SupportedCommercialValidator(diagnosticSeverityProvider, provider), - new UpdateBootVersion(diagnosticSeverityProvider) }; } @@ -177,91 +167,5 @@ public class VersionValidators { // } // } - - private static class UpdateBootVersion extends AbstractDiagnosticValidator { - - public UpdateBootVersion(DiagnosticSeverityProvider diagnosticSeverityProvider) { - super(diagnosticSeverityProvider); - } - - @Override - public Collection validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception { - List versions = CachedBootVersionsFromMavenCentral.getBootVersions(); - ImmutableList.Builder builder = ImmutableList.builder(); - validateMajorVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); - validateMinorVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); - validatePatchVersion(javaProject, javaProjectVersion, versions).ifPresent(builder::add); - return builder.build(); - } - - private Optional validateMajorVersion(IJavaProject javaProject, Version javaProjectVersion, List sortedBootVersions) { - Version latest = VersionValidationUtils.getNewerLatestMajorRelease(sortedBootVersions, javaProjectVersion); - - if (latest != null) { - VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_MAJOR_VERSION; - - StringBuffer message = new StringBuffer(); - message.append("Newer major version of Spring Boot available: "); - message.append(latest.toString()); - - CodeAction ca = new CodeAction(); - ca.setKind(CodeActionKind.QuickFix); - ca.setTitle("Upgrade to Spring Boot " + latest.toString() + " (executes the full project conversion recipe from OpenRewrite)"); - String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; - ca.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, - ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); - - - return Optional.of(createDiagnostic(ca, problemType, message.toString())); - } - return Optional.empty(); - } - - private Optional validateMinorVersion(IJavaProject javaProject, Version javaProjectVersion, List sortedBootVersions) { - Version latest = VersionValidationUtils.getNewerLatestMinorRelease(sortedBootVersions, javaProjectVersion); - - if (latest != null) { - VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_MINOR_VERSION; - - StringBuffer message = new StringBuffer(); - message.append("Newer minor version of Spring Boot available: "); - message.append(latest.toString()); - - CodeAction ca = new CodeAction(); - ca.setKind(CodeActionKind.QuickFix); - ca.setTitle("Upgrade to Spring Boot " + latest.toString() + " (executes the full project conversion recipe from OpenRewrite)"); - String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; - ca.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, - ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); - - - return Optional.of(createDiagnostic(ca, problemType, message.toString())); - } - return Optional.empty(); - } - - private Optional validatePatchVersion(IJavaProject javaProject, Version javaProjectVersion, List sortedBootVersions) { - Version latest = VersionValidationUtils.getNewerLatestPatchRelease(sortedBootVersions, javaProjectVersion); - - if (latest != null) { - VersionValidationProblemType problemType = VersionValidationProblemType.UPDATE_LATEST_PATCH_VERSION; - - StringBuffer message = new StringBuffer(); - message.append("Newer patch version of Spring Boot available: "); - message.append(latest.toString()); - - CodeAction ca = new CodeAction(); - ca.setKind(CodeActionKind.QuickFix); - ca.setTitle("Upgrade to Spring Boot " + latest.toString() + " (Maven dependency version changes only)"); - String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; - ca.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, - ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); - - - return Optional.of(createDiagnostic(ca, problemType, message.toString())); - } - return Optional.empty(); - } - } } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgradeTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgradeTest.java index 742c7977c..676005fcd 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgradeTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/rewrite/SpringBootUpgradeTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2023 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,27 +11,43 @@ package org.springframework.ide.vscode.boot.java.rewrite; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import java.util.Collections; import java.util.List; +import java.util.Map; import org.junit.jupiter.api.Test; +import org.springframework.ide.vscode.commons.java.Version; public class SpringBootUpgradeTest { + private static final Map MINOR_VERSION_TO_RECIPE_ID = Map.of( + "2.0", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_0", + "2.1", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_1", + "2.2", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2", + "2.3", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3", + "2.4", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_4", + "2.5", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5", + "2.6", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6", + "2.7", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7", + "3.0", "org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0" + ); + @Test - void recipeIdChain1() throws Exception { + void recipeIdChain_1() throws Exception { assertEquals(List.of( - "org.openrewrite.java.spring.boot2.SpringBoot1To2Migration", + "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_0", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_1", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_4", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5" - ), SpringBootUpgrade.createRecipeIdsChain(1, 3, 2, 5)); + ), SpringBootUpgrade.createRecipeIdsChain(1, 3, 2, 5, MINOR_VERSION_TO_RECIPE_ID)); } @Test - void recipeIdChain2() throws Exception { + void recipeIdChain_2() throws Exception { assertEquals(List.of( "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3", @@ -39,13 +55,13 @@ public class SpringBootUpgradeTest { "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7" - ), SpringBootUpgrade.createRecipeIdsChain(2, 2, 2, 7)); + ), SpringBootUpgrade.createRecipeIdsChain(2, 2, 2, 7, MINOR_VERSION_TO_RECIPE_ID)); } @Test - void recipeIdChain3() throws Exception { + void recipeIdChain_3() throws Exception { assertEquals(List.of( - "org.openrewrite.java.spring.boot2.SpringBoot1To2Migration", + "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_0", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_1", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3", @@ -54,20 +70,29 @@ public class SpringBootUpgradeTest { "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6", "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7", "org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0" - ), SpringBootUpgrade.createRecipeIdsChain(1, 3, 3, 0)); + ), SpringBootUpgrade.createRecipeIdsChain(1, 3, 3, 0, MINOR_VERSION_TO_RECIPE_ID)); } @Test - void recipeIdChain4() throws Exception { + void recipeIdChain_4() throws Exception { assertEquals(List.of( "org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2" - ), SpringBootUpgrade.createRecipeIdsChain(2, 2, 2, 2)); + ), SpringBootUpgrade.createRecipeIdsChain(2, 2, 2, 2, MINOR_VERSION_TO_RECIPE_ID)); } @Test - void recipeIdChain5() throws Exception { + void recipeIdChain_5() throws Exception { assertEquals(List.of( - ), SpringBootUpgrade.createRecipeIdsChain(2, 7, 2, 2)); + ), SpringBootUpgrade.createRecipeIdsChain(2, 7, 2, 2, MINOR_VERSION_TO_RECIPE_ID)); + } + + @Test + void nearestMinorVersion() throws Exception { + assertEquals("3.0", SpringBootUpgrade.nearestAvailableMinorVersion(new Version(3, 0, 2, null), MINOR_VERSION_TO_RECIPE_ID.keySet())); + assertEquals("3.0", SpringBootUpgrade.nearestAvailableMinorVersion(new Version(3, 3, 1, null), MINOR_VERSION_TO_RECIPE_ID.keySet())); + assertNull(SpringBootUpgrade.nearestAvailableMinorVersion(new Version(4, 3, 1, null), MINOR_VERSION_TO_RECIPE_ID.keySet())); + assertNull(SpringBootUpgrade.nearestAvailableMinorVersion(new Version(1, 5, 0, null), MINOR_VERSION_TO_RECIPE_ID.keySet())); + assertNull(SpringBootUpgrade.nearestAvailableMinorVersion(new Version(3, 0, 2, null), Collections.emptySet())); } }