diff --git a/README.md b/README.md index 224b2174..1a9198d9 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ To use the plugin you need Gradle version 5 or later, to start add the following ```groovy plugins { - id "co.com.bancolombia.cleanArchitecture" version "1.6.1" + id "co.com.bancolombia.cleanArchitecture" version "1.6.2" } ``` @@ -90,6 +90,12 @@ gradle gpl --type=[pipelineType] |AZURE |Azure Pipeline| +8 The ```deleteModule | dm``` task will delete a sub project, this task have one required parameter ```module```. +```sh +gradle deleteModule --module=[name] +gradle dm --module=[name] +```` + How I can help? ============= Review the issues, we hear new ideas. diff --git a/gradle.properties b/gradle.properties index dd295f6c..8631ae41 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ package=co.com.bancolombia -systemProp.version=1.6.1 +systemProp.version=1.6.2 diff --git a/src/functionalTest/java/co/com/bancolombia/PluginCleanFunctionalTest.java b/src/functionalTest/java/co/com/bancolombia/PluginCleanFunctionalTest.java index 1f941cfb..63a97ec9 100644 --- a/src/functionalTest/java/co/com/bancolombia/PluginCleanFunctionalTest.java +++ b/src/functionalTest/java/co/com/bancolombia/PluginCleanFunctionalTest.java @@ -3,8 +3,6 @@ */ package co.com.bancolombia; -import org.gradle.api.Project; -import org.gradle.testfixtures.ProjectBuilder; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; import org.gradle.testkit.runner.TaskOutcome; @@ -17,8 +15,7 @@ import java.io.Writer; import java.nio.file.Files; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * A simple functional test for the 'co.com.bancolombia.greeting' plugin. @@ -249,6 +246,19 @@ public void createTasks() { assertEquals(result.task(":tasks").getOutcome(), TaskOutcome.SUCCESS); } + @Test + public void canDeleteModule() { + // Arrange + canRunTaskGenerateDrivenAdapterWithParameters(); + // Act + runner.withArguments("deleteModule", "--module=jpa-repository"); + runner.withProjectDir(projectDir); + BuildResult result = runner.build(); + // Assert + assertFalse(new File("build/functionalTest/infrastructure/driven-adapters/jpa-repository").exists()); + assertEquals(result.task(":deleteModule").getOutcome(), TaskOutcome.SUCCESS); + } + private void writeString(File file, String string) throws IOException { try (Writer writer = new FileWriter(file)) { writer.write(string); diff --git a/src/main/java/co/com/bancolombia/Constants.java b/src/main/java/co/com/bancolombia/Constants.java index dd008e2c..9f076859 100644 --- a/src/main/java/co/com/bancolombia/Constants.java +++ b/src/main/java/co/com/bancolombia/Constants.java @@ -10,7 +10,7 @@ public class Constants { public static final String SPRING_CLOUD_VERSION = "Greenwich.M1"; public static final String SONAR_VERSION = "2.7"; public static final String JACOCO_VERSION = "0.8.5"; - public static final String PLUGIN_VERSION = "1.6.1"; + public static final String PLUGIN_VERSION = "1.6.2"; public static final String SECRETS_VERSION = "2.1.0"; public enum BooleanOption { diff --git a/src/main/java/co/com/bancolombia/PluginClean.java b/src/main/java/co/com/bancolombia/PluginClean.java index 51f357a2..f9dee32d 100644 --- a/src/main/java/co/com/bancolombia/PluginClean.java +++ b/src/main/java/co/com/bancolombia/PluginClean.java @@ -51,6 +51,10 @@ private List initTasks() { .description("Generate CI pipeline as a code in deployment layer").group(Constants.PLUGIN_TASK_GROUP) .taskAction(GeneratePipelineTask.class).build()); + tasksModels.add(TaskModel.builder().name("deleteModule").shortcut("dm") + .description("Delete gradle module").group(Constants.PLUGIN_TASK_GROUP) + .taskAction(DeleteModuleTask.class).build()); + return tasksModels; } diff --git a/src/main/java/co/com/bancolombia/factory/ModuleBuilder.java b/src/main/java/co/com/bancolombia/factory/ModuleBuilder.java index 1dca0a9a..4b2dc915 100644 --- a/src/main/java/co/com/bancolombia/factory/ModuleBuilder.java +++ b/src/main/java/co/com/bancolombia/factory/ModuleBuilder.java @@ -4,7 +4,7 @@ import co.com.bancolombia.exceptions.ParamNotFoundException; import co.com.bancolombia.models.FileModel; import co.com.bancolombia.models.TemplateDefinition; -import co.com.bancolombia.utils.FileAppender; +import co.com.bancolombia.utils.FileUpdater; import co.com.bancolombia.utils.FileUtils; import co.com.bancolombia.utils.Utils; import com.fasterxml.jackson.databind.ObjectMapper; @@ -31,6 +31,7 @@ public class ModuleBuilder { private final MustacheFactory mustacheFactory = new DefaultMustacheFactory(); private final Map files = new ConcurrentHashMap<>(); private final List dirs = new ArrayList<>(); + private final List dirsToDelete = new ArrayList<>(); private final Map params = new HashMap<>(); private final ObjectMapper mapper = new ObjectMapper(); private final Logger logger; @@ -51,11 +52,13 @@ public ModuleBuilder(Project project) { } public void persist() throws IOException { - logger.lifecycle("Generating dirs"); - dirs.forEach(getProject()::mkdir); - logger.lifecycle("Dirs generated"); - logger.lifecycle("Generating files"); + logger.lifecycle("Applying changes"); + dirs.forEach(dir -> { + getProject().mkdir(dir); + logger.debug("creating dir {}", dir); + }); if (properties != null) { + logger.lifecycle("Updating application properties"); addFile(APPLICATION_PROPERTIES, FileUtils.parseToYaml(properties)); } for (Map.Entry fileEntry : files.entrySet()) { @@ -63,7 +66,11 @@ public void persist() throws IOException { FileUtils.writeString(getProject(), file.getPath(), file.getContent()); logger.debug("file {} written", file.getPath()); } - logger.lifecycle("Files written"); + dirsToDelete.forEach(dir -> { + getProject().delete(dir); + logger.debug("deleting dir {}", dir); + }); + logger.lifecycle("Changes successfully applied"); } public void setupFromTemplate(String resourceGroup) throws IOException, ParamNotFoundException { @@ -80,19 +87,35 @@ public void setupFromTemplate(String resourceGroup) throws IOException, ParamNot } public void appendToSettings(String module, String baseDir) throws IOException { - appendToFile("settings.gradle", settings -> { - String toAppend = "\ninclude ':" + module + "'\nproject(':" + module + "').projectDir = file('./" + baseDir - + "/" + module + "')"; - if (settings.contains(toAppend)) { - return settings; - } - return settings + toAppend; + logger.lifecycle("adding module {} to settings.gradle", module); + updateFile("settings.gradle", settings -> Utils.addModule(settings, module, baseDir)); + } + + public void removeFromSettings(String module) throws IOException { + logger.lifecycle("removing {} from settings.gradle", module); + updateFile("settings.gradle", settings -> { + String moduleKey = "':" + module + "'"; + return Utils.removeLinesIncludes(settings, moduleKey); }); } public void appendDependencyToModule(String module, String dependency) throws IOException { + logger.lifecycle("adding dependency {} to module {}", dependency, module); + String buildFilePath = project.getChildProjects().get(module).getBuildFile().getPath(); + updateFile(buildFilePath, current -> Utils.addDependency(current, dependency)); + } + + public void removeDependencyFromModule(String module, String dependency) throws IOException { + logger.lifecycle("removing dependency {} from module {}", dependency, module); String buildFilePath = project.getChildProjects().get(module).getBuildFile().getPath(); - appendToFile(buildFilePath, current -> Utils.addDependency(current, dependency)); + updateFile(buildFilePath, current -> Utils.removeLinesIncludes(current, dependency)); + } + + public void deleteModule(String module) { + String projectDir = project.getChildProjects().get(module).getProjectDir().getPath(); + logger.lifecycle("deleting module {} from dir {}", module, + projectDir.replace(project.getProjectDir().getPath(), "")); + removeDir(projectDir); } public ObjectNode appendToProperties(String path) throws IOException { @@ -129,6 +152,12 @@ public void addDir(String path) { } } + public void removeDir(String path) { + if (path != null) { + this.dirsToDelete.add(path); + } + } + public String getStringParam(String key) { return (String) params.get(key); } @@ -137,7 +166,7 @@ public Boolean getBooleanParam(String key) { return (Boolean) params.get(key); } - private void appendToFile(String path, FileAppender appender) throws IOException { + private void updateFile(String path, FileUpdater updater) throws IOException { FileModel current = files.get(path); String content; if (current == null) { @@ -146,7 +175,7 @@ private void appendToFile(String path, FileAppender appender) throws IOException } else { content = current.getContent(); } - addFile(path, appender.append(content)); + addFile(path, updater.update(content)); } private ObjectNode getNode(ObjectNode node, List attributes) { diff --git a/src/main/java/co/com/bancolombia/task/DeleteModuleTask.java b/src/main/java/co/com/bancolombia/task/DeleteModuleTask.java new file mode 100644 index 00000000..51e43b84 --- /dev/null +++ b/src/main/java/co/com/bancolombia/task/DeleteModuleTask.java @@ -0,0 +1,40 @@ +package co.com.bancolombia.task; + +import co.com.bancolombia.factory.ModuleBuilder; +import co.com.bancolombia.utils.Utils; +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.options.Option; +import org.gradle.api.tasks.options.OptionValues; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class DeleteModuleTask extends DefaultTask { + private final ModuleBuilder builder = new ModuleBuilder(getProject()); + + private String module; + + @Option(option = "module", description = "Set module name to delete") + public void setModule(String module) { + this.module = module; + } + + @OptionValues("module") + public List getModules() { + return new ArrayList<>(getProject().getChildProjects().keySet()); + } + + @TaskAction + public void deleteModule() throws IOException { + if (module == null || !getProject().getChildProjects().containsKey(module)) { + throw new IllegalArgumentException("No valid module name is set, usage: gradle deleteModule --module " + + Utils.formatTaskOptions(getModules())); + } + builder.deleteModule(module); + builder.removeFromSettings(module); + builder.removeDependencyFromModule("app-service", "implementation project(':" + module + "')"); + builder.persist(); + } +} diff --git a/src/main/java/co/com/bancolombia/utils/FileAppender.java b/src/main/java/co/com/bancolombia/utils/FileAppender.java deleted file mode 100644 index f5bd143f..00000000 --- a/src/main/java/co/com/bancolombia/utils/FileAppender.java +++ /dev/null @@ -1,5 +0,0 @@ -package co.com.bancolombia.utils; - -public interface FileAppender { - String append(String content); -} diff --git a/src/main/java/co/com/bancolombia/utils/FileUpdater.java b/src/main/java/co/com/bancolombia/utils/FileUpdater.java new file mode 100644 index 00000000..510bd88f --- /dev/null +++ b/src/main/java/co/com/bancolombia/utils/FileUpdater.java @@ -0,0 +1,5 @@ +package co.com.bancolombia.utils; + +public interface FileUpdater { + String update(String content); +} diff --git a/src/main/java/co/com/bancolombia/utils/Utils.java b/src/main/java/co/com/bancolombia/utils/Utils.java index 60c00c57..27fb11f1 100644 --- a/src/main/java/co/com/bancolombia/utils/Utils.java +++ b/src/main/java/co/com/bancolombia/utils/Utils.java @@ -6,10 +6,12 @@ import lombok.NoArgsConstructor; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class Utils { @@ -88,4 +90,18 @@ public static String toDashName(String name) { return res; } + public static String addModule(String settings, String module, String baseDir) { + String toAppend = "\ninclude ':" + module + "'\nproject(':" + module + "').projectDir = file('./" + baseDir + + "/" + module + "')"; + if (settings.contains(toAppend)) { + return settings; + } + return settings + toAppend; + } + + public static String removeLinesIncludes(String content, String key) { + return Arrays.stream(content.split("\\n")) + .filter(line -> !line.contains(key)) + .collect(Collectors.joining("\n")); + } } diff --git a/src/main/resources/structure/root/settings.gradle.mustache b/src/main/resources/structure/root/settings.gradle.mustache index 41de9285..ca9b9b4a 100644 --- a/src/main/resources/structure/root/settings.gradle.mustache +++ b/src/main/resources/structure/root/settings.gradle.mustache @@ -1,8 +1,8 @@ rootProject.name = '{{projectName}}' -include ":app-service" -include ":model" -include ":usecase" +include ':app-service' +include ':model' +include ':usecase' project(':app-service').projectDir = file('./applications/app-service') project(':model').projectDir = file('./domain/model') project(':usecase').projectDir = file('./domain/usecase') diff --git a/src/test/java/co/com/bancolombia/factory/ModuleBuilderTest.java b/src/test/java/co/com/bancolombia/factory/ModuleBuilderTest.java index 4137f5fd..3199bd28 100644 --- a/src/test/java/co/com/bancolombia/factory/ModuleBuilderTest.java +++ b/src/test/java/co/com/bancolombia/factory/ModuleBuilderTest.java @@ -40,6 +40,7 @@ public void shouldAppendProperties() throws IOException { builder.appendToProperties("spring.datasource").put("url", "mydburl"); builder.appendToProperties("").put("test", "myUnitTes"); builder.appendToProperties("server").put("port", 8000); + builder.removeDir(null); builder.persist(); } diff --git a/src/test/java/co/com/bancolombia/task/DeleteModuleTaskTest.java b/src/test/java/co/com/bancolombia/task/DeleteModuleTaskTest.java new file mode 100644 index 00000000..791f12b5 --- /dev/null +++ b/src/test/java/co/com/bancolombia/task/DeleteModuleTaskTest.java @@ -0,0 +1,79 @@ +package co.com.bancolombia.task; + +import co.com.bancolombia.exceptions.CleanException; +import co.com.bancolombia.factory.adapters.ModuleFactoryDrivenAdapter; +import org.gradle.api.Project; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class DeleteModuleTaskTest { + private DeleteModuleTask task; + + @Before + public void setup() throws IOException, CleanException { + Project project = ProjectBuilder.builder() + .withName("cleanArchitecture") + .withProjectDir(new File("build/unitTest")) + .build(); + + project.getTasks().create("ca", GenerateStructureTask.class); + GenerateStructureTask generateStructureTask = (GenerateStructureTask) project.getTasks().getByName("ca"); + generateStructureTask.generateStructureTask(); + + ProjectBuilder.builder() + .withName("app-service") + .withProjectDir(new File("build/unitTest/applications/app-service")) + .withParent(project) + .build(); + + project.getTasks().create("gda", GenerateDrivenAdapterTask.class); + GenerateDrivenAdapterTask generateDriven = (GenerateDrivenAdapterTask) project.getTasks().getByName("gda"); + generateDriven.setType(ModuleFactoryDrivenAdapter.DrivenAdapterType.MONGODB); + generateDriven.generateDrivenAdapterTask(); + + ProjectBuilder.builder() + .withName("mongo-repository") + .withProjectDir(new File("build/unitTest/infrastructure/driven-adapters/mongo-repository")) + .withParent(project) + .build(); + + assertTrue(new File("build/unitTest/infrastructure/driven-adapters/mongo-repository/build.gradle").exists()); + + project.getTasks().create("test", DeleteModuleTask.class); + task = (DeleteModuleTask) project.getTasks().getByName("test"); + } + + // Assert + @Test(expected = IllegalArgumentException.class) + public void deleteNullModule() throws IOException { + // Arrange + // Act + task.deleteModule(); + } + + // Assert + @Test(expected = IllegalArgumentException.class) + public void deleteNonExistentModule() throws IOException { + // Arrange + task.setModule("non-existent"); + // Act + task.deleteModule(); + } + + @Test + public void generateEntryPoint() throws IOException { + // Arrange + task.setModule("mongo-repository"); + // Act + task.deleteModule(); + // Assert + assertFalse(new File("build/unitTest/infrastructure/driven-adapters/mongo-repository/build.gradle").exists()); + } +} diff --git a/src/test/java/co/com/bancolombia/utils/UtilsTest.java b/src/test/java/co/com/bancolombia/utils/UtilsTest.java index abcabef1..eaf37f55 100644 --- a/src/test/java/co/com/bancolombia/utils/UtilsTest.java +++ b/src/test/java/co/com/bancolombia/utils/UtilsTest.java @@ -169,6 +169,89 @@ public void shouldGenerateDashNameNonStartsWithUpper() { assertEquals("my-camel-case", res); } + @Test + public void shouldAddModule() { + // Arrange + String settings = "rootProject.name = 'cleanArchitecture'\n" + + "\n" + + "include ':app-service'\n" + + "include ':model'\n" + + "include ':usecase'\n" + + "project(':app-service').projectDir = file('./applications/app-service')\n" + + "project(':model').projectDir = file('./domain/model')\n" + + "project(':usecase').projectDir = file('./domain/usecase')\n" + + "include ':api-rest'\n" + + "project(':api-rest').projectDir = file('./infrastructure/entry-points/api-rest')"; + String settingsNew = "rootProject.name = 'cleanArchitecture'\n" + + "\n" + + "include ':app-service'\n" + + "include ':model'\n" + + "include ':usecase'\n" + + "project(':app-service').projectDir = file('./applications/app-service')\n" + + "project(':model').projectDir = file('./domain/model')\n" + + "project(':usecase').projectDir = file('./domain/usecase')\n" + + "include ':api-rest'\n" + + "project(':api-rest').projectDir = file('./infrastructure/entry-points/api-rest')\n" + + "include ':my-module'\n" + + "project(':my-module').projectDir = file('./infrastructure/entry-points/my-module')"; + // Act + String result = Utils.addModule(settings, "my-module", "infrastructure/entry-points"); + // Assert + assertEquals(settingsNew, result); + } + + @Test + public void shouldNotAddRepeatedModule() { + // Arrange + String settings = "rootProject.name = 'cleanArchitecture'\n" + + "\n" + + "include ':app-service'\n" + + "include ':model'\n" + + "include ':usecase'\n" + + "project(':app-service').projectDir = file('./applications/app-service')\n" + + "project(':model').projectDir = file('./domain/model')\n" + + "project(':usecase').projectDir = file('./domain/usecase')\n" + + "include ':api-rest'\n" + + "project(':api-rest').projectDir = file('./infrastructure/entry-points/api-rest')\n" + + "include ':my-module'\n" + + "project(':my-module').projectDir = file('./infrastructure/entry-points/my-module')"; + // Act + String result = Utils.addModule(settings, "my-module", "infrastructure/entry-points"); + // Assert + assertEquals(settings, result); + } + + @Test + public void shouldRemoveModule() { + // Arrange + String settings = "rootProject.name = 'cleanArchitecture'\n" + + "\n" + + "include ':app-service'\n" + + "include ':model'\n" + + "include ':usecase'\n" + + "project(':app-service').projectDir = file('./applications/app-service')\n" + + "project(':model').projectDir = file('./domain/model')\n" + + "project(':usecase').projectDir = file('./domain/usecase')\n" + + "include ':api-rest'\n" + + "project(':api-rest').projectDir = file('./infrastructure/entry-points/api-rest')\n" + + "include ':my-module'\n" + + "project(':my-module').projectDir = file('./infrastructure/entry-points/my-module')"; + String settingsExpected = "rootProject.name = 'cleanArchitecture'\n" + + "\n" + + "include ':app-service'\n" + + "include ':model'\n" + + "include ':usecase'\n" + + "project(':app-service').projectDir = file('./applications/app-service')\n" + + "project(':model').projectDir = file('./domain/model')\n" + + "project(':usecase').projectDir = file('./domain/usecase')\n" + + "include ':my-module'\n" + + "project(':my-module').projectDir = file('./infrastructure/entry-points/my-module')"; + // Act + String result = Utils.removeLinesIncludes(settings, "api-rest"); + // Assert + assertEquals(settingsExpected, result); + } + private enum Options { A, BC, D }