Skip to content

Commit 3370ea0

Browse files
Spring boot migrator displays only applied recipe (#321)
Co-authored-by: Andrei Shakirin <[email protected]>
1 parent aa1384a commit 3370ea0

File tree

8 files changed

+191
-45
lines changed

8 files changed

+191
-45
lines changed

applications/spring-shell/src/main/java/org/springframework/sbm/shell/ApplyCommandRenderer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ public class ApplyCommandRenderer {
6565
@Setter
6666
private int delay = 250;
6767

68-
public AttributedString render(Recipe recipe) {
68+
public AttributedString render(String recipeName, List<Action> actions) {
6969
AttributedStringBuilder builder = new AttributedStringBuilder();
7070
builder.append("\n");
7171
builder.style(AttributedStyle.DEFAULT.italicDefault());
72-
builder.append(recipe.getName());
72+
builder.append(recipeName);
7373
builder.append(" successfully applied the following actions:\n")
74-
.append(recipe.getActions().stream()
74+
.append(actions.stream()
7575
.map(a -> " (x) " + getDescriptionWithIndent(a))
7676
.collect(Collectors.joining("\n")))
7777
.append("\n");

applications/spring-shell/src/main/java/org/springframework/sbm/shell/ApplyShellCommand.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.springframework.sbm.engine.commands.ApplyCommand;
2121
import org.springframework.sbm.engine.context.ProjectContext;
2222
import org.springframework.sbm.engine.context.ProjectContextHolder;
23+
import org.springframework.sbm.engine.recipe.Action;
2324
import org.springframework.sbm.engine.recipe.Recipe;
2425
import lombok.RequiredArgsConstructor;
2526
import org.jetbrains.annotations.NotNull;
@@ -57,8 +58,8 @@ public AttributedString apply(@ShellOption(arity = 1, valueProvider = ApplyRecip
5758
System.out.println(header.toAnsi());
5859

5960
ProjectContext projectContext = projectContextHolder.getProjectContext();
60-
Recipe recipe = applyCommand.execute(projectContext, recipeName);
61-
AttributedString applyCommandOutput = applyCommandRenderer.render(recipe);
61+
List<Action> appliedActions = applyCommand.execute(projectContext, recipeName);
62+
AttributedString applyCommandOutput = applyCommandRenderer.render(recipeName, appliedActions);
6263

6364
List<Recipe> applicableRecipes = applicableRecipeListCommand.execute(projectContext);
6465
AttributedString applicableRecipesOutput = applicableRecipeListRenderer.render(applicableRecipes);

applications/spring-shell/src/test/java/org/springframework/sbm/shell/ApplyShellCommandTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ class ApplyShellCommandTest {
5858
@Test
5959
void testApply() {
6060
String recipeName = "recipe-1";
61-
Recipe recipe = new Recipe();
6261
AttributedString applyOutput = new AttributedString("applyOutput");
6362
List<Recipe> applicableRecipes = List.of();
6463
AttributedString applicableRecipesOutput = new AttributedString("applicableRecipesOutput");
@@ -67,8 +66,8 @@ void testApply() {
6766

6867
ProjectContext projectContext = mock(ProjectContext.class);
6968
when(projectContextHolder.getProjectContext()).thenReturn(projectContext);
70-
when(applyCommand.execute(projectContext, recipeName)).thenReturn(recipe);
71-
when(applyCommandRenderer.render(recipe)).thenReturn(applyOutput);
69+
when(applyCommand.execute(projectContext, recipeName)).thenReturn(List.of());
70+
when(applyCommandRenderer.render(recipeName, List.of())).thenReturn(applyOutput);
7271
when(applicableRecipeListCommand.execute(projectContext)).thenReturn(applicableRecipes);
7372
when(applicableRecipeListRenderer.render(applicableRecipes)).thenReturn(applicableRecipesOutput);
7473

components/sbm-core/src/main/java/org/springframework/sbm/engine/commands/ApplyCommand.java

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,18 @@
1919
import org.springframework.sbm.common.filter.ModifiedResourcePathStringFilter;
2020
import org.springframework.sbm.engine.context.ProjectContext;
2121
import org.springframework.sbm.engine.context.ProjectContextSerializer;
22-
import org.springframework.sbm.engine.context.ProjectRootPathResolver;
2322
import org.springframework.sbm.engine.git.GitSupport;
2423
import org.springframework.sbm.engine.git.ProjectSyncVerifier;
24+
import org.springframework.sbm.engine.recipe.Action;
2525
import org.springframework.sbm.engine.recipe.Recipe;
2626
import org.springframework.sbm.engine.recipe.RecipesBuilder;
27-
import org.springframework.sbm.project.parser.ProjectContextInitializer;
2827
import org.springframework.stereotype.Component;
2928

3029
import java.util.List;
3130

3231
@Component
3332
public class ApplyCommand extends AbstractCommand<Recipe> {
3433

35-
private final ProjectContextInitializer projectContextBuilder;
36-
37-
private final ApplyCommandHelper applyCommandHelper;
38-
39-
private final ProjectRootPathResolver projectRootPathResolver;
40-
4134
private final RecipesBuilder recipesBuilder;
4235

4336
private final ProjectContextSerializer contextSerializer;
@@ -47,27 +40,25 @@ public class ApplyCommand extends AbstractCommand<Recipe> {
4740
private final GitSupport gitSupport;
4841

4942
public ApplyCommand(
50-
ProjectContextInitializer projectContextBuilder,
51-
ApplyCommandHelper applyCommandHelper,
52-
ProjectRootPathResolver projectRootPathResolver, RecipesBuilder recipesBuilder, ProjectContextSerializer contextSerializer, ProjectSyncVerifier projectSyncVerifier, GitSupport gitSupport) {
43+
RecipesBuilder recipesBuilder,
44+
ProjectContextSerializer contextSerializer,
45+
ProjectSyncVerifier projectSyncVerifier,
46+
GitSupport gitSupport) {
5347
super("apply");
54-
this.projectContextBuilder = projectContextBuilder;
55-
this.applyCommandHelper = applyCommandHelper;
56-
this.projectRootPathResolver = projectRootPathResolver;
5748
this.recipesBuilder = recipesBuilder;
5849
this.contextSerializer = contextSerializer;
5950
this.projectSyncVerifier = projectSyncVerifier;
6051
this.gitSupport = gitSupport;
6152
}
6253

63-
public Recipe execute(ProjectContext projectContext, String recipeName) {
54+
public List<Action> execute(ProjectContext projectContext, String recipeName) {
6455
Recipe recipe = recipesBuilder.buildRecipes().getRecipeByName(recipeName)
6556
.orElseThrow(() -> new IllegalArgumentException("Recipe with name '" + recipeName + "' could not be found"));
6657

6758
// verify that project sources are in sync with in memory representation
6859
projectSyncVerifier.rescanWhenProjectIsOutOfSyncAndGitAvailable(projectContext);
6960

70-
recipe.apply(projectContext);
61+
List<Action> appliedActions = recipe.apply(projectContext);
7162

7263
// verify that project sources didn't change while running recipe
7364
projectSyncVerifier.verifyProjectIsInSyncWhenGitAvailable(projectContext);
@@ -80,21 +71,12 @@ public Recipe execute(ProjectContext projectContext, String recipeName) {
8071

8172
gitSupport.commitWhenGitAvailable(projectContext, recipeName, modifiedResources, deletedResources);
8273

83-
return recipe;
74+
return appliedActions;
8475
}
8576

8677
@Override
8778
@Deprecated
8879
public Recipe execute(String... arguments) {
89-
// if (arguments == null || arguments.length < 2) {
90-
// throw new IllegalArgumentException("Apply command needs project path (as first) and recipe name (as second) to be provided");
91-
// } else {
92-
// Path projectRoot = projectRootPathResolver.getProjectRootOrDefault(arguments[0]);
93-
// // FIXME: This triggers a new scan which shouldn't be required. Retrieve current ProjectContext from...? and use it here.
94-
// ProjectContext context = projectContextBuilder.initProjectContext(projectRoot, new RewriteExecutionContext());
95-
// return applyCommandHelper.applyRecipe(context, arguments[1]);
96-
// }
9780
return null;
9881
}
99-
10082
}

components/sbm-core/src/main/java/org/springframework/sbm/engine/recipe/Recipe.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import javax.validation.Valid;
2424
import javax.validation.constraints.NotEmpty;
25+
import java.util.ArrayList;
2526
import java.util.List;
2627
import java.util.stream.Collectors;
2728

@@ -50,8 +51,8 @@ public class Recipe {
5051
public Recipe(
5152
@JsonProperty(value = "name", required = true) String name,
5253
@JsonProperty(value = "actions", required = true) List<Action> actions,
53-
@JsonProperty(value = "condition", required = false) Condition condition,
54-
@JsonProperty(value = "order", required = false) Integer order
54+
@JsonProperty(value = "condition") Condition condition,
55+
@JsonProperty(value = "order") Integer order
5556
) {
5657
this.name = name;
5758
this.actions = actions;
@@ -91,10 +92,18 @@ public boolean isApplicable(ProjectContext context) {
9192
return condition.evaluate(context) && actions.stream().anyMatch(a -> a.isApplicable(context));
9293
}
9394

94-
public void apply(ProjectContext context) {
95-
actions.stream()
96-
.filter(a -> a.isApplicable(context))
97-
.forEach(a -> a.applyWithStatusEvent(context));
95+
public List<Action> apply(ProjectContext context) {
96+
97+
List<Action> appliedActions = new ArrayList<>();
98+
for (Action action : actions) {
99+
100+
if (action.isApplicable(context)) {
101+
action.applyWithStatusEvent(context);
102+
appliedActions.add(action);
103+
}
104+
}
105+
106+
return appliedActions;
98107
}
99108

100109
public String getDetails() {

components/sbm-core/src/main/java/org/springframework/sbm/engine/recipe/RecipeLoader.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@
1515
*/
1616
package org.springframework.sbm.engine.recipe;
1717

18-
import org.springframework.sbm.project.resource.ResourceHelper;
19-
import lombok.RequiredArgsConstructor;
20-
import org.springframework.core.io.Resource;
21-
import org.springframework.stereotype.Component;
22-
2318
import java.util.List;
2419

2520
public interface RecipeLoader {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2021 - 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.sbm.engine.commands;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.api.extension.ExtendWith;
20+
import org.mockito.InjectMocks;
21+
import org.mockito.Mock;
22+
import org.mockito.junit.jupiter.MockitoExtension;
23+
import org.springframework.sbm.engine.context.ProjectContext;
24+
import org.springframework.sbm.engine.context.ProjectContextSerializer;
25+
import org.springframework.sbm.engine.git.GitSupport;
26+
import org.springframework.sbm.engine.git.ProjectSyncVerifier;
27+
import org.springframework.sbm.engine.recipe.Action;
28+
import org.springframework.sbm.engine.recipe.Recipe;
29+
import org.springframework.sbm.engine.recipe.Recipes;
30+
import org.springframework.sbm.engine.recipe.RecipesBuilder;
31+
32+
import java.util.List;
33+
import java.util.Optional;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
import static org.mockito.Mockito.when;
37+
38+
@ExtendWith(MockitoExtension.class)
39+
public class ApplyCommandTest {
40+
@Mock
41+
private RecipesBuilder recipesBuilder;
42+
@Mock
43+
private ProjectContextSerializer contextSerializer;
44+
@Mock
45+
private ProjectSyncVerifier projectSyncVerifier;
46+
@Mock
47+
private GitSupport gitSupport;
48+
@Mock
49+
Recipes recipes;
50+
@Mock
51+
private Recipe recipe;
52+
@Mock
53+
ProjectContext projectContext;
54+
@Mock
55+
Action action1;
56+
@Mock
57+
Action action2;
58+
59+
@InjectMocks
60+
ApplyCommand applyCommand;
61+
62+
@Test
63+
void shouldReturnActionList() {
64+
when(recipesBuilder.buildRecipes()).thenReturn(recipes);
65+
when(recipes.getRecipeByName("testRecipe")).thenReturn(Optional.of(recipe));
66+
when(recipe.apply(projectContext)).thenReturn(List.of(action1, action2));
67+
List<Action> actions = applyCommand.execute(projectContext, "testRecipe");
68+
69+
assertThat(actions).hasSize(2);
70+
assertThat(actions).contains(action1).contains(action2);
71+
}
72+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2021 - 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.sbm.engine.recipe;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.api.extension.ExtendWith;
20+
import org.mockito.Mock;
21+
import org.mockito.junit.jupiter.MockitoExtension;
22+
import org.springframework.context.ApplicationEventPublisher;
23+
import org.springframework.sbm.engine.context.ProjectContext;
24+
25+
import java.util.List;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.assertj.core.api.Assertions.fail;
29+
import static org.junit.jupiter.api.Assertions.assertThrows;
30+
import static org.mockito.ArgumentMatchers.any;
31+
import static org.mockito.Mockito.doThrow;
32+
import static org.mockito.Mockito.mock;
33+
import static org.mockito.Mockito.when;
34+
35+
@ExtendWith(MockitoExtension.class)
36+
public class RecipeTest {
37+
@Mock
38+
Action applicableAction;
39+
@Mock
40+
Action notApplicableAction;
41+
@Mock
42+
ProjectContext projectContext;
43+
44+
@Test
45+
void shouldReturnOnlyApplicableActions() {
46+
when(applicableAction.isApplicable(any(ProjectContext.class))).thenReturn(true);
47+
when(notApplicableAction.isApplicable(any(ProjectContext.class))).thenReturn(false);
48+
Recipe testRecipe = new Recipe(
49+
"test recipe",
50+
List.of(applicableAction, notApplicableAction),
51+
Condition.TRUE,
52+
0);
53+
54+
List<Action> actionList = testRecipe.apply(projectContext);
55+
56+
assertThat(actionList).hasSize(1);
57+
assertThat(actionList).contains(applicableAction);
58+
}
59+
60+
@Test
61+
void shouldReturnOnlyActionsWhichHaveSuccessfullyBeingApplied() {
62+
63+
when(applicableAction.isApplicable(any(ProjectContext.class))).thenReturn(true);
64+
when(notApplicableAction.isApplicable(any(ProjectContext.class))).thenReturn(false);
65+
66+
final Action successAction = mock(Action.class);
67+
final Action failAction = mock(Action.class);
68+
69+
when(successAction.isApplicable(any(ProjectContext.class))).thenReturn(true);
70+
when(failAction.isApplicable(any(ProjectContext.class))).thenReturn(true);
71+
72+
Recipe testRecipe = new Recipe(
73+
"test recipe",
74+
List.of(
75+
applicableAction,
76+
notApplicableAction,
77+
successAction,
78+
failAction
79+
),
80+
Condition.TRUE,
81+
0);
82+
83+
84+
doThrow(new RuntimeException("")).when(failAction).applyWithStatusEvent(any());
85+
86+
assertThrows(RuntimeException.class, () -> testRecipe.apply(projectContext));
87+
}
88+
}

0 commit comments

Comments
 (0)