diff --git a/initializr-docs/src/main/asciidoc/configuration-guide.adoc b/initializr-docs/src/main/asciidoc/configuration-guide.adoc index 5fb3d235..57b46da5 100644 --- a/initializr-docs/src/main/asciidoc/configuration-guide.adoc +++ b/initializr-docs/src/main/asciidoc/configuration-guide.adoc @@ -129,7 +129,9 @@ include::{code-examples}/doc/generator/project/SampleContributor.java[tag=code] ==== Project Generation Lifecycle When a `ProjectGenerator` is instructed to generate a project, the specified `ProjectDescription` can be customized using available `ProjectDescriptionCustomizer` -beans and can be ordered using Spring's `Ordered` interface. +beans and can be ordered using Spring's `Ordered` interface. A `ProjectDescriptionDiff` +bean is available for extensions that wish to know if an attribute of the original +`ProjectDescription` was modified. Once the description has been customized based on the available ``ProjectDescriptionCustomizer``s, the generator uses a `ProjectAssetGenerator` to diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/DefaultProjectDescriptionDiffFactory.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/DefaultProjectDescriptionDiffFactory.java new file mode 100644 index 00000000..8a304b8a --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/DefaultProjectDescriptionDiffFactory.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.initializr.generator.project; + +/** + * A default {@link ProjectDescriptionDiffFactory} implementation that creates default + * {@link ProjectDescriptionDiff} instances. + * + * @author Chris Bono + */ +public class DefaultProjectDescriptionDiffFactory implements ProjectDescriptionDiffFactory { + + @Override + public ProjectDescriptionDiff create(ProjectDescription description) { + return new ProjectDescriptionDiff(description); + } + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/MutableProjectDescription.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/MutableProjectDescription.java index fa37ec95..87b4399c 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/project/MutableProjectDescription.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/MutableProjectDescription.java @@ -61,6 +61,35 @@ public class MutableProjectDescription implements ProjectDescription { private String baseDirectory; + public MutableProjectDescription() { + } + + /** + * Create a new instance with the state of the specified {@code source}. + * @param source the source description to initialize this instance with + */ + protected MutableProjectDescription(MutableProjectDescription source) { + this.platformVersion = source.getPlatformVersion(); + this.buildSystem = source.getBuildSystem(); + this.packaging = source.getPackaging(); + this.language = source.getLanguage(); + this.requestedDependencies.putAll(source.getRequestedDependencies()); + this.groupId = source.getGroupId(); + this.artifactId = source.getArtifactId(); + this.version = source.getVersion(); + this.name = source.getName(); + this.description = source.getDescription(); + this.applicationName = source.getApplicationName(); + this.packageName = source.getPackageName(); + this.baseDirectory = source.getBaseDirectory(); + } + + @Override + public MutableProjectDescription createCopy() { + return new MutableProjectDescription(this); + } + + @Override public Version getPlatformVersion() { return this.platformVersion; } @@ -69,6 +98,7 @@ public class MutableProjectDescription implements ProjectDescription { this.platformVersion = platformVersion; } + @Override public BuildSystem getBuildSystem() { return this.buildSystem; } @@ -77,6 +107,7 @@ public class MutableProjectDescription implements ProjectDescription { this.buildSystem = buildSystem; } + @Override public Packaging getPackaging() { return this.packaging; } @@ -85,6 +116,7 @@ public class MutableProjectDescription implements ProjectDescription { this.packaging = packaging; } + @Override public Language getLanguage() { return this.language; } @@ -101,10 +133,12 @@ public class MutableProjectDescription implements ProjectDescription { return addDependency(id, builder.build()); } + @Override public Map getRequestedDependencies() { return Collections.unmodifiableMap(this.requestedDependencies); } + @Override public String getGroupId() { return this.groupId; } @@ -113,6 +147,7 @@ public class MutableProjectDescription implements ProjectDescription { this.groupId = groupId; } + @Override public String getArtifactId() { return this.artifactId; } @@ -121,6 +156,7 @@ public class MutableProjectDescription implements ProjectDescription { this.artifactId = artifactId; } + @Override public String getVersion() { return this.version; } @@ -129,6 +165,7 @@ public class MutableProjectDescription implements ProjectDescription { this.version = version; } + @Override public String getName() { return this.name; } @@ -137,6 +174,7 @@ public class MutableProjectDescription implements ProjectDescription { this.name = name; } + @Override public String getDescription() { return this.description; } @@ -145,6 +183,7 @@ public class MutableProjectDescription implements ProjectDescription { this.description = description; } + @Override public String getApplicationName() { return this.applicationName; } @@ -153,6 +192,7 @@ public class MutableProjectDescription implements ProjectDescription { this.applicationName = applicationName; } + @Override public String getPackageName() { if (StringUtils.hasText(this.packageName)) { return this.packageName; @@ -167,6 +207,7 @@ public class MutableProjectDescription implements ProjectDescription { this.packageName = packageName; } + @Override public String getBaseDirectory() { return this.baseDirectory; } diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescription.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescription.java index fca2bdbe..be89723e 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescription.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescription.java @@ -32,6 +32,15 @@ import io.spring.initializr.generator.version.Version; */ public interface ProjectDescription { + /** + * Create a full copy of this description so that any additional changes made on this + * instance are not reflected on the returned copy. + * @return a clone of this instance + */ + default ProjectDescription createCopy() { + throw new UnsupportedOperationException(); + } + /** * Return a immutable mapping of requested {@link Dependency dependencies}. * @return the requested dependencies diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescriptionDiff.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescriptionDiff.java new file mode 100644 index 00000000..712e9b28 --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescriptionDiff.java @@ -0,0 +1,199 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.initializr.generator.project; + +import java.util.Objects; +import java.util.function.BiConsumer; + +import io.spring.initializr.generator.buildsystem.BuildSystem; +import io.spring.initializr.generator.language.Language; +import io.spring.initializr.generator.packaging.Packaging; +import io.spring.initializr.generator.version.Version; + +/** + * Provides a convenient API for determining if certain fields on a + * {@link ProjectDescription} were modified. + * + * @author Chris Bono + * @author Stephane Nicoll + */ +public class ProjectDescriptionDiff { + + private final ProjectDescription original; + + /** + * Create a {@link ProjectDescriptionDiff} that uses a copy of the specified + * description as its source. + * @param original the description to copy as the source + */ + public ProjectDescriptionDiff(ProjectDescription original) { + this.original = original.createCopy(); + } + + /** + * Return the original {@link ProjectDescription} that is being tracked. + * @return the original description + */ + public ProjectDescription getOriginal() { + return this.original; + } + + /** + * Calls the specified consumer if the {@code platformVersion} is different on the + * original source project description than the specified project description. + * @param current the project description to test against + * @param consumer to call if the property has changed + */ + public void ifPlatformVersionChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getPlatformVersion(), current.getPlatformVersion())) { + consumer.accept(this.original.getPlatformVersion(), current.getPlatformVersion()); + } + } + + /** + * Calls the specified consumer if the {@code buildSystem} is different on the + * original source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifBuildSystemChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getBuildSystem(), current.getBuildSystem())) { + consumer.accept(this.original.getBuildSystem(), current.getBuildSystem()); + } + } + + /** + * Calls the specified consumer if the {@code packaging} is different on the original + * source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifPackagingChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getPackaging(), current.getPackaging())) { + consumer.accept(this.original.getPackaging(), current.getPackaging()); + } + } + + /** + * Calls the specified consumer if the {@code language} is different on the original + * source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifLanguageChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getLanguage(), current.getLanguage())) { + consumer.accept(this.original.getLanguage(), current.getLanguage()); + } + } + + /** + * Calls the specified consumer if the {@code groupId} is different on the original + * source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifGroupIdChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getGroupId(), current.getGroupId())) { + consumer.accept(this.original.getGroupId(), current.getGroupId()); + } + } + + /** + * Calls the specified consumer if the {@code artifactId} is different on the original + * source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifArtifactIdChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getArtifactId(), current.getArtifactId())) { + consumer.accept(this.original.getArtifactId(), current.getArtifactId()); + } + } + + /** + * Calls the specified consumer if the {@code version} is different on the original + * source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifVersionChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getVersion(), current.getVersion())) { + consumer.accept(this.original.getVersion(), current.getVersion()); + } + } + + /** + * Calls the specified consumer if the {@code name} is different on the original + * source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifNameChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getName(), current.getName())) { + consumer.accept(this.original.getName(), current.getName()); + } + } + + /** + * Calls the specified consumer if the {@code description} is different on the + * original source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifDescriptionChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getDescription(), current.getDescription())) { + consumer.accept(this.original.getDescription(), current.getDescription()); + } + } + + /** + * Calls the specified consumer if the {@code applicationName} is different on the + * original source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifApplicationNameChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getApplicationName(), current.getApplicationName())) { + consumer.accept(this.original.getApplicationName(), current.getApplicationName()); + } + } + + /** + * Calls the specified consumer if the {@code packageName} is different on the + * original source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifPackageNameChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getPackageName(), current.getPackageName())) { + consumer.accept(this.original.getPackageName(), current.getPackageName()); + } + } + + /** + * Calls the specified consumer if the {@code baseDirectory} is different on the + * original source project description than the specified project description. + * @param current the description to test against + * @param consumer to call if the property has changed + */ + public void ifBaseDirectoryChanged(ProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.getBaseDirectory(), current.getBaseDirectory())) { + consumer.accept(this.original.getBaseDirectory(), current.getBaseDirectory()); + } + } + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescriptionDiffFactory.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescriptionDiffFactory.java new file mode 100644 index 00000000..d1fecc6f --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectDescriptionDiffFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.initializr.generator.project; + +/** + * A factory for {@link ProjectDescriptionDiff}. + * + * @author Chris Bono + */ +public interface ProjectDescriptionDiffFactory { + + /** + * Create a {@link ProjectDescriptionDiff} for the specified + * {@link ProjectDescription}. Any change on the specified {@code description} is + * tracked by the returned instance. + * @param description the project description to use as the source of the diff + * @return a diff instance using the current state of the specified description as its + * source + */ + ProjectDescriptionDiff create(ProjectDescription description); + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java index 85d97c49..c4bce087 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java @@ -120,8 +120,12 @@ public class ProjectGenerator { private Supplier resolve(ProjectDescription description, ProjectGenerationContext context) { return () -> { if (description instanceof MutableProjectDescription) { + MutableProjectDescription mutableDescription = (MutableProjectDescription) description; + ProjectDescriptionDiffFactory diffFactory = context.getBeanProvider(ProjectDescriptionDiffFactory.class) + .getIfAvailable(DefaultProjectDescriptionDiffFactory::new); + context.registerBean(ProjectDescriptionDiff.class, () -> diffFactory.create(mutableDescription)); context.getBeanProvider(ProjectDescriptionCustomizer.class).orderedStream() - .forEach((customizer) -> customizer.customize((MutableProjectDescription) description)); + .forEach((customizer) -> customizer.customize(mutableDescription)); } return description; }; diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectDescriptionDiffTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectDescriptionDiffTests.java new file mode 100644 index 00000000..27537691 --- /dev/null +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectDescriptionDiffTests.java @@ -0,0 +1,226 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.initializr.generator.project; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import io.spring.initializr.generator.buildsystem.BuildSystem; +import io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystem; +import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem; +import io.spring.initializr.generator.language.Language; +import io.spring.initializr.generator.language.java.JavaLanguage; +import io.spring.initializr.generator.packaging.Packaging; +import io.spring.initializr.generator.packaging.jar.JarPackaging; +import io.spring.initializr.generator.version.Version; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link ProjectDescriptionDiff}. + * + * @author Chris Bono + * @author Stephane Nicoll + */ +class ProjectDescriptionDiffTests { + + @Test + void projectDescriptionDiffCopySource() { + ProjectDescription original = createFullProjectDescription(); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(original); + assertThat(diff.getOriginal()).usingRecursiveComparison().isEqualTo(original); + assertThat(diff.getOriginal()).isNotSameAs(original); + } + + @Test + void projectDescriptionDiffWithUnmodifiedDescriptionDoesNotInvokeConsumer() { + ProjectDescription description = createFullProjectDescription(); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + diff.ifPlatformVersionChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifBuildSystemChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifPackagingChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifLanguageChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifGroupIdChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifArtifactIdChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifVersionChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifNameChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifDescriptionChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifApplicationNameChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifPackageNameChanged(description, (original, actual) -> fail("Values should not have changed")); + diff.ifBaseDirectoryChanged(description, (original, actual) -> fail("Values should not have changed")); + } + + @Test + void projectDescriptionDiffWithModifiedPlatformVersionInvokesConsumer() { + Version original = Version.parse("2.1.0.RELEASE"); + Version actual = Version.parse("2.2.0.RELEASE"); + MutableProjectDescription description = new MutableProjectDescription(); + description.setPlatformVersion(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setPlatformVersion(actual); + validateConsumer(original, actual, (consumer) -> diff.ifPlatformVersionChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedBuildSystemInvokesConsumer() { + BuildSystem original = BuildSystem.forId(MavenBuildSystem.ID); + BuildSystem actual = BuildSystem.forId(GradleBuildSystem.ID); + MutableProjectDescription description = new MutableProjectDescription(); + description.setBuildSystem(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setBuildSystem(actual); + validateConsumer(original, actual, (consumer) -> diff.ifBuildSystemChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedPackagingInvokesConsumer() { + Packaging original = Packaging.forId(JarPackaging.ID); + Packaging actual = Packaging.forId("war"); + MutableProjectDescription description = new MutableProjectDescription(); + description.setPackaging(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setPackaging(actual); + validateConsumer(original, actual, (consumer) -> diff.ifPackagingChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedLanguageInvokesConsumer() { + Language original = Language.forId(JavaLanguage.ID, "11"); + Language actual = Language.forId(JavaLanguage.ID, "13"); + MutableProjectDescription description = new MutableProjectDescription(); + description.setLanguage(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setLanguage(actual); + validateConsumer(original, actual, (consumer) -> diff.ifLanguageChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedGroupIdInvokesConsumer() { + String original = "com.example"; + String actual = "com.example.app"; + MutableProjectDescription description = new MutableProjectDescription(); + description.setGroupId(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setGroupId(actual); + validateConsumer(original, actual, (consumer) -> diff.ifGroupIdChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedArtifactIdInvokesConsumer() { + String original = "demo"; + String actual = "app"; + MutableProjectDescription description = new MutableProjectDescription(); + description.setArtifactId(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setArtifactId(actual); + validateConsumer(original, actual, (consumer) -> diff.ifArtifactIdChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedVersionInvokesConsumer() { + String original = "1.0.0"; + String actual = "1.1.0"; + MutableProjectDescription description = new MutableProjectDescription(); + description.setVersion(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setVersion(actual); + validateConsumer(original, actual, (consumer) -> diff.ifVersionChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedNameInvokesConsumer() { + String original = "demo"; + String actual = "application"; + MutableProjectDescription description = new MutableProjectDescription(); + description.setName(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setName(actual); + validateConsumer(original, actual, (consumer) -> diff.ifNameChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedDescriptionInvokesConsumer() { + String original = "Demo Application"; + String actual = "Application"; + MutableProjectDescription description = new MutableProjectDescription(); + description.setDescription(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setDescription(actual); + validateConsumer(original, actual, (consumer) -> diff.ifDescriptionChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedApplicationNameInvokesConsumer() { + String original = "DemoApplication"; + String actual = "Application"; + MutableProjectDescription description = new MutableProjectDescription(); + description.setApplicationName(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setApplicationName(actual); + validateConsumer(original, actual, (consumer) -> diff.ifApplicationNameChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedPackageNameInvokesConsumer() { + String original = "com.example"; + String actual = "com.example.app"; + MutableProjectDescription description = new MutableProjectDescription(); + description.setPackageName(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setPackageName(actual); + validateConsumer(original, actual, (consumer) -> diff.ifPackageNameChanged(description, consumer)); + } + + @Test + void projectDescriptionDiffWithModifiedBaseDirectoryInvokesConsumer() { + String original = null; + String actual = "demo-app"; + MutableProjectDescription description = new MutableProjectDescription(); + description.setBaseDirectory(original); + ProjectDescriptionDiff diff = new ProjectDescriptionDiff(description); + description.setBaseDirectory(actual); + validateConsumer(original, actual, (consumer) -> diff.ifBaseDirectoryChanged(description, consumer)); + } + + private MutableProjectDescription createFullProjectDescription() { + MutableProjectDescription description = new MutableProjectDescription(); + description.setBuildSystem(BuildSystem.forId(MavenBuildSystem.ID)); + description.setLanguage(Language.forId(JavaLanguage.ID, "11")); + description.setPlatformVersion(Version.parse("2.2.0.RELEASE")); + description.setGroupId("com.example"); + description.setArtifactId("demo"); + description.setName("demo"); + description.setVersion("0.0.8"); + description.setApplicationName("DemoApplication"); + description.setPackageName("com.example.demo"); + description.setPackaging(Packaging.forId("jar")); + description.setBaseDirectory("."); + return description; + } + + @SuppressWarnings("unchecked") + private void validateConsumer(T original, T actual, Consumer> test) { + BiConsumer consumer = mock(BiConsumer.class); + test.accept(consumer); + verify(consumer).accept(original, actual); + } + +} diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java index 8b68ae32..d2cec6ec 100644 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java @@ -32,6 +32,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; /** @@ -53,6 +54,33 @@ public class ProjectGeneratorTests { assertThat(description).isSameAs(beanDescription); } + @Test + void generateProvideDefaultProjectDescriptionDiff() { + ProjectGenerator generator = new ProjectGenerator(mockContextInitializr()); + MutableProjectDescription description = new MutableProjectDescription(); + ProjectDescriptionDiff diff = generator.generate(description, (context) -> { + assertThat(context.getBeansOfType(ProjectDescriptionDiff.class)).hasSize(1); + return context.getBean(ProjectDescriptionDiff.class); + }); + assertThat(diff).isInstanceOf(ProjectDescriptionDiff.class); + } + + @Test + void generateUseAvailableProjectDescriptionDiffFactory() { + ProjectDescriptionDiff diff = mock(ProjectDescriptionDiff.class); + ProjectDescriptionDiffFactory diffFactory = mock(ProjectDescriptionDiffFactory.class); + MutableProjectDescription description = new MutableProjectDescription(); + given(diffFactory.create(description)).willReturn(diff); + ProjectGenerator generator = new ProjectGenerator( + (context) -> context.registerBean(ProjectDescriptionDiffFactory.class, () -> diffFactory)); + ProjectDescriptionDiff actualDiff = generator.generate(description, (context) -> { + assertThat(context.getBeansOfType(ProjectDescriptionDiff.class)).hasSize(1); + return context.getBean(ProjectDescriptionDiff.class); + }); + assertThat(actualDiff).isSameAs(diff); + verify(diffFactory).create(description); + } + @Test void generateInvokeContextInitializerBeforeContextIsRefreshed() { ProjectGenerator generator = new ProjectGenerator((context) -> { diff --git a/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectDescription.java b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectDescription.java index dc7fbaa2..668078f9 100644 --- a/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectDescription.java +++ b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectDescription.java @@ -28,6 +28,19 @@ class CustomProjectDescription extends MutableProjectDescription { private boolean customFlag; + CustomProjectDescription() { + } + + CustomProjectDescription(CustomProjectDescription source) { + super(source); + this.customFlag = source.isCustomFlag(); + } + + @Override + public CustomProjectDescription createCopy() { + return new CustomProjectDescription(this); + } + boolean isCustomFlag() { return this.customFlag; } diff --git a/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectDescriptionDiff.java b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectDescriptionDiff.java new file mode 100644 index 00000000..556d485c --- /dev/null +++ b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectDescriptionDiff.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.initializr.web.controller.custom; + +import java.util.Objects; +import java.util.function.BiConsumer; + +import io.spring.initializr.generator.project.ProjectDescriptionDiff; + +/** + * Extends the base {@link ProjectDescriptionDiff} to provide convenient diff methods on + * {@link CustomProjectDescription}. + * + * @author Chris Bono + * @author Stephane Nicoll + */ +class CustomProjectDescriptionDiff extends ProjectDescriptionDiff { + + private final CustomProjectDescription original; + + CustomProjectDescriptionDiff(CustomProjectDescription original) { + super(original); + this.original = original; + } + + /** + * Calls the specified consumer if the {@code customFlag} is different on the original + * source project description than the specified project description. + * @param current the project description to test against + * @param consumer to call if the property has changed + */ + void ifCustomFlagChanged(CustomProjectDescription current, BiConsumer consumer) { + if (!Objects.equals(this.original.isCustomFlag(), current.isCustomFlag())) { + consumer.accept(this.original.isCustomFlag(), current.isCustomFlag()); + } + } + +} diff --git a/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/ProjectGenerationControllerCustomRequestIntegrationTests.java b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/ProjectGenerationControllerCustomRequestIntegrationTests.java index 68c9b2c3..1ea03f50 100644 --- a/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/ProjectGenerationControllerCustomRequestIntegrationTests.java +++ b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/ProjectGenerationControllerCustomRequestIntegrationTests.java @@ -16,7 +16,10 @@ package io.spring.initializr.web.controller.custom; +import io.spring.initializr.generator.project.MutableProjectDescription; import io.spring.initializr.generator.project.ProjectDescription; +import io.spring.initializr.generator.project.ProjectDescriptionCustomizer; +import io.spring.initializr.generator.project.ProjectDescriptionDiffFactory; import io.spring.initializr.generator.test.project.ProjectStructure; import io.spring.initializr.metadata.InitializrMetadata; import io.spring.initializr.metadata.InitializrMetadataProvider; @@ -33,6 +36,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; +import org.springframework.util.Assert; import static org.assertj.core.api.Assertions.assertThat; @@ -44,8 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat; */ @ActiveProfiles("test-default") @Import(CustomProjectGenerationConfiguration.class) -public class ProjectGenerationControllerCustomRequestIntegrationTests - extends AbstractInitializrControllerIntegrationTests { +class ProjectGenerationControllerCustomRequestIntegrationTests extends AbstractInitializrControllerIntegrationTests { @Test void createProjectWithCustomFlagEnabled() { @@ -79,6 +82,16 @@ public class ProjectGenerationControllerCustomRequestIntegrationTests return new CustomProjectGenerationController(metadataProvider, projectGenerationInvoker); } + @Bean + CustomProjectDescriptionCustomizer customProjectDescriptionCustomizer() { + return new CustomProjectDescriptionCustomizer(); + } + + @Bean + CustomProjectDescriptionDiffFactory customProjectDescriptionDiffFactory() { + return new CustomProjectDescriptionDiffFactory(); + } + } static class CustomProjectRequestToDescriptionConverter @@ -91,10 +104,28 @@ public class ProjectGenerationControllerCustomRequestIntegrationTests description.setCustomFlag(request.isCustomFlag()); // Override attributes for test purposes description.setPackageName("org.example.custom"); - description.setApplicationName("CustomApp"); return description; } } + static class CustomProjectDescriptionCustomizer implements ProjectDescriptionCustomizer { + + @Override + public void customize(MutableProjectDescription description) { + description.setApplicationName("CustomApp"); + } + + } + + static class CustomProjectDescriptionDiffFactory implements ProjectDescriptionDiffFactory { + + @Override + public CustomProjectDescriptionDiff create(ProjectDescription description) { + Assert.isInstanceOf(CustomProjectDescription.class, description); + return new CustomProjectDescriptionDiff((CustomProjectDescription) description); + } + + } + }