diff --git a/docs/src/main/asciidoc/gradle-project.adoc b/docs/src/main/asciidoc/gradle-project.adoc index 8fe268c6a2..6c3f929a5b 100644 --- a/docs/src/main/asciidoc/gradle-project.adoc +++ b/docs/src/main/asciidoc/gradle-project.adoc @@ -228,22 +228,13 @@ pseudocode): contracts { testFramework ='JUNIT' testMode = 'MockMvc' - generatedTestSourcesDir = project.file("${project.buildDir}/generated-test-sources/contracts") + generatedTestJavaSourcesDir = project.file("${project.buildDir}/generated-test-sources/contractTest/java") + generatedTestGroovySourcesDir = project.file("${project.buildDir}/generated-test-sources/contractTest/groovy") generatedTestResourcesDir = project.file("${project.buildDir}/generated-test-resources/contracts") - contractsDslDir = project.file("${project.rootDir}/src/test/resources/contracts") + contractsDslDir = project.file("${project.projectDir}/src/test/resources/contracts") basePackageForTests = 'org.springframework.cloud.verifier.tests' stubsOutputDir = project.file("${project.buildDir}/stubs") sourceSet = null - - // the following properties are used when you want to provide where the JAR with contract lays - contractDependency { - stringNotation = '' - } - contractsPath = '' - contractsWorkOffline = false - contractRepository { - cacheDownloadedContracts(true) - } } tasks.create(type: Jar, name: 'verifierStubsJar', dependsOn: 'generateClientStubs') { @@ -278,6 +269,52 @@ contracts { ---- ==== +To download contracts from a remote source, you can use the following snippets as needed: + +==== +[source,groovy,indent=0] +---- +contracts { + // If your contracts exist in a JAR archive published to a Maven repository + contractDependency { + stringNotation = '' + // OR + groupId = '' + artifactId = '' + version = '' + classifier = '' + } + + // If your contracts exist in a Git SCM repository + contractRepository { + repositoryUrl = '' + // username = '' + // password = '' + } + + // controls the nested location to find the contracts in either the JAR or Git SCM source + contractsPath = '' +} +---- +==== + +Since we are using Gradle's Jar packaging task, there are several options and capabilities that you may wish to utilize to further extend what is created by the `verifierStubsJar`. In order to do this, you would use the native mechanisms provided directly by Gradle for customizing an existing task like so: + +NOTE: for the sake of the example, we desire to add a `git.properties` file to the `verifierStubsJar`. + +==== +[source,groovy,inden=0] +---- +verifierStubsJar { + from("${buildDir}/resources/main/") { + include("git.properties") + } +} +---- +==== + +It should also be noted that as of 3.0.0, the default publication has been disabled. As a result this means, that you are able to create any named jar and publish it as you would normally have done via Gradle configuration options. This means that you can build a jar file customized just the way you would like and publish that for absolute full control over the jar's layout and contents. + [[gradle-configuration-options]] == Configuration Options @@ -299,25 +336,24 @@ use Spock classes, the class is `spock.lang.Specification`. setting takes precedence over `baseClassForTests`. * `baseClassMappings`: Explicitly maps a contract package to a FQN of a base class. This setting takes precedence over `packageWithBaseClasses` and `baseClassForTests`. -* `ruleClassForTests`: Specifies a rule that should be added to the generated test -classes. * `ignoredFiles`: Uses an `Antmatcher` to allow defining stub files for which processing should be skipped. By default, it is an empty array. * `contractsDslDir`: Specifies the directory that contains contracts written by using the -GroovyDSL. By default, its value is `$rootDir/src/test/resources/contracts`. +GroovyDSL. By default, its value is `$projectDir/src/test/resources/contracts`. * `generatedTestSourcesDir`: Specifies the test source directory where tests generated -from the Groovy DSL should be placed. By default, its value is -`$buildDir/generated-test-sources/contracts`. +from the Groovy DSL should be placed. (Deprecrated) +* `generatedTestJavaSourcesDir`: Specifies the test source directory where Java/JUnit tests generated from the Groovy DSL should be placed. By default, it's value is `$buildDir/generated-tes-sources/contractTest/java`. +* `generatedTestGroovySourcesDir`: Specifies the test source directory where Groovy/Spock tests generated from the Groovy DSL should be placed. By default, it's value is `$buildDir/generated-test-sources/contractTest/groovy`. * `generatedTestResourcesDir`: Specifies the test resource directory where resources used by the tests generated from the Groovy DSL should be placed. By default, its value is -`$buildDir/generated-test-resources/contracts`. +`$buildDir/generated-test-resources/contractTest`. * `stubsOutputDir`: Specifies the directory where the generated WireMock stubs from the Groovy DSL should be placed. * `testFramework`: Specifies the target test framework to be used. Currently, Spock, JUnit 4 (`TestFramework.JUNIT`) and JUnit 5 are supported, with JUnit 4 being the default framework. * `contractsProperties`: A map that contains properties to be passed to Spring Cloud Contract components. Those properties might be used by (for example) built-in or custom Stub Downloaders. -* `sourceSet`: Source set where the contracts are stored. If not provided will assume `test` (for example, `project.sourceSets.test.java` for JUnit or `project.sourceSets.test.groovy` for Spock). +* `sourceSet`: Source set where the contracts are stored. If not provided will assume `contractTest` (for example, `project.sourceSets.contractTest.java` for JUnit or `project.sourceSets.contractTest.groovy` for Spock). You can use the following properties when you want to specify the location of the JAR that contains the contracts: diff --git a/samples/standalone/dsl/http-server/build.gradle b/samples/standalone/dsl/http-server/build.gradle index e81eaf1341..0295955156 100644 --- a/samples/standalone/dsl/http-server/build.gradle +++ b/samples/standalone/dsl/http-server/build.gradle @@ -81,7 +81,7 @@ dependencies { } } -test { +contractTest { useJUnitPlatform() systemProperty 'spring.profiles.active', 'gradle' testLogging { @@ -97,6 +97,25 @@ test { } } +publishing { + publications { + maven(MavenPublication) { + artifact bootJar + artifact verifierStubsJar + + // https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/273 + versionMapping { + usage("java-api") { + fromResolutionOf("runtimeClasspath") + } + usage("java-runtime") { + fromResolutionResult() + } + } + } + } +} + clean.doFirst { delete "~/.m2/repository/com/example/http-server-dsl-gradle" } diff --git a/samples/standalone/webclient/http-server/build.gradle b/samples/standalone/webclient/http-server/build.gradle index 639363f42e..38dd9b1911 100644 --- a/samples/standalone/webclient/http-server/build.gradle +++ b/samples/standalone/webclient/http-server/build.gradle @@ -4,7 +4,6 @@ plugins { id "io.spring.dependency-management" id "maven-publish" id "maven" - id "org.springframework.cloud.contract" } group = 'com.example' @@ -36,10 +35,6 @@ dependencies { testImplementation 'org.springframework.cloud:spring-cloud-contract-wiremock' } -contracts { - failOnNoContracts = false -} - test { useJUnitPlatform() systemProperty 'spring.profiles.active', 'gradle' @@ -83,6 +78,25 @@ task copyClasses(type: Copy) { into "${project.buildDir}/stubs/" } +publishing { + publications { + maven(MavenPublication) { + artifact bootJar + artifact stubsJar + + // https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/273 + versionMapping { + usage("java-api") { + fromResolutionOf("runtimeClasspath") + } + usage("java-runtime") { + fromResolutionResult() + } + } + } + } +} + clean.doFirst { delete 'target/snippets/stubs' delete "~/.m2/repository/com/example/http-server-restdocs-gradle" diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle index d4d589f5cc..ae06a1520f 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle @@ -63,7 +63,6 @@ configurations { dependencies { compile gradleApi() - compile localGroovy() compile "org.eclipse.aether:aether-api:${aetherVersion}" compile("org.springframework.cloud:spring-cloud-contract-converters:${project.version}") { @@ -79,6 +78,7 @@ dependencies { exclude(group: 'org.codehaus.groovy') } testCompile 'info.solidsoft.spock:spock-global-unroll:0.5.0' + testCompile localGroovy() testCompile gradleTestKit() } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.groovy deleted file mode 100644 index 8c5f91ceeb..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.groovy +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin - -import java.time.Instant - -import groovy.transform.CompileStatic -import groovy.transform.Immutable -import groovy.transform.ImmutableOptions -import groovy.transform.PackageScope -import org.gradle.api.Action -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.Project -import org.gradle.api.file.CopySpec -import org.gradle.api.file.Directory -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.logging.Logger -import org.gradle.api.provider.MapProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.WorkResult - -import org.springframework.cloud.contract.stubrunner.ContractDownloader -import org.springframework.cloud.contract.stubrunner.StubConfiguration -import org.springframework.cloud.contract.stubrunner.StubDownloader -import org.springframework.cloud.contract.stubrunner.StubDownloaderBuilderProvider -import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties -import org.springframework.cloud.contract.verifier.converter.ToYamlConverter -import org.springframework.util.StringUtils - -// TODO: Convert to incremental task: https://docs.gradle.org/current/userguide/custom_tasks.html#incremental_tasks -/** - * Task that copies the contracts in order for the jar task to - * generate the jar. It takes into consideration the inclusion - * patterns when working with repo with shared contracts. - * - * @author Marcin Grzejszczak - * @author Anatoliy Balakirev - * @since 1.0.2 - */ -@PackageScope -@CompileStatic -class ContractsCopyTask extends DefaultTask { - - static final String TASK_NAME = 'copyContracts' - static final String CONTRACTS = "contracts" - static final String BACKUP = "original" - @Nested - Config config - - static class Config { - @Input - Provider convertToYaml - @Input - Provider excludeBuildFolders - @Input - Provider failOnNoContracts - String contractsDirectoryPath - Provider contractsDirectory - // All fields inside `@Nested` one are properly marked as an `@Input` to work with incremental build: - @Nested - @Optional - ContractVerifierExtension.Dependency contractDependency - @Nested - @Optional - ContractVerifierExtension.ContractRepository contractRepository - @Input - @Optional - Property contractsMode - @Input - Property deleteStubsAfterTest - @Input - MapProperty contractsProperties - @Input - @Optional - Property contractsPath - - @Input - @Optional - Instant getForceDownloadOfTheLatestContracts() { - // If we have `dynamic` version (`+` or `SNAPSHOT`) - we should mark this task as out of date for every run: - if (shouldDownloadContracts() && getStubConfiguration().isVersionChanging()) { - return Instant.now() // This will trigger re-download of contracts - } - else { - return null // This will not trigger re-download of contracts - } - } - - @Optional - @InputDirectory - Provider getContractsDirectory() { - contractsDirectoryPath = contractsDirectory.get().asFile.absolutePath - if (shouldDownloadContracts() || contractFolderMissing()) { - return null - } - else { - return contractsDirectory - } - } - - private boolean contractFolderMissing() { - return contractsDirectory.isPresent() && !contractsDirectory.get().asFile.exists() - } - - @Internal - boolean shouldDownloadContracts() { - return StringUtils.hasText(contractDependency.getArtifactId().getOrNull()) || - StringUtils.hasText(contractDependency.getStringNotation().getOrNull()) || - StringUtils.hasText(contractRepository.repositoryUrl.getOrNull()) - } - - @Internal - StubConfiguration getStubConfiguration() { - return GradleContractsDownloaderHelper.stubConfiguration(contractDependency) - } - - @OutputDirectory - DirectoryProperty copiedContractsFolder - @OutputDirectory - DirectoryProperty stubsOutputDir - @Optional - @OutputDirectory - DirectoryProperty backupContractsFolder - } - - @TaskAction - void sync() { - final DownloadedData downloadedData = downloadContractsIfNeeded() - final File contractsDirectory - String antPattern = "" - if (downloadedData) { - contractsDirectory = downloadedData.downloadedContracts - antPattern = "${downloadedData.inclusionProperties.includedRootFolderAntPattern}*.*" - logger.info("Contracts got downloaded to [" + contractsDirectory + "]") - } - else if (config.contractsDirectory != null && config.contractsDirectory.isPresent()) { - contractsDirectory = config.contractsDirectory.get().asFile - antPattern = "**/" - } - else { - contractsDirectory = null - } - logger.info("For project [{}] will use contracts provided in the folder [{}]", project.name, contractsDirectory) - final String contractsRepository = config.contractRepository.repositoryUrl.isPresent() ? config.contractRepository.repositoryUrl.get() : "" - throwExceptionWhenFailOnNoContracts(contractsDirectory, contractsRepository) - if (contractsDirectory == null) { - logger.info("Contracts directory not set and contracts weren't downloaded. There's nothing to copy") - return - } - final String slashSeparatedGroupId = project.group.toString().replace(".", File.separator) - final String slashSeparatedAntPattern = antPattern.replace(slashSeparatedGroupId, project.group.toString()) - File output = config.copiedContractsFolder.get().getAsFile() - logger.info("Downloading and unpacking files from [${contractsDirectory}] to [$output]. The inclusion ant patterns are [${antPattern}] and [${slashSeparatedAntPattern}]") - sync(contractsDirectory, antPattern, slashSeparatedAntPattern, config.excludeBuildFolders.get(), output) - if (config.convertToYaml.get()) { - convertBackedUpDslsToYaml(contractsDirectory, antPattern, slashSeparatedAntPattern, output, config.excludeBuildFolders.get()) - } - } - - static Config fromExtension(ContractVerifierExtension extension, String root, Project project) { - return new Config( - convertToYaml: extension.convertToYaml, - excludeBuildFolders: extension.excludeBuildFolders, - failOnNoContracts: extension.failOnNoContracts, - contractsDirectory: extension.contractsDslDir, - stubsOutputDir: extension.stubsOutputDir, - copiedContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.CONTRACTS, project), - backupContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.BACKUP, project), - contractDependency: extension.contractDependency, - contractRepository: extension.contractRepository, - contractsMode: extension.contractsMode, - deleteStubsAfterTest: extension.deleteStubsAfterTest, - contractsProperties: extension.contractsProperties, - contractsPath: extension.contractsPath - ) - } - - private void convertBackedUpDslsToYaml(File file, String antPattern, String slashSeparatedAntPattern, File outputContractsFolder, boolean excludeBuildFolders) { - sync(file, antPattern, slashSeparatedAntPattern, excludeBuildFolders, config.backupContractsFolder.get().asFile) - ToYamlConverter.replaceContractWithYaml(outputContractsFolder) - logger.info("Replaced DSL files with their YAML representation at [" + outputContractsFolder + "]") - } - - protected WorkResult sync(File file, String antPattern, String slashSeparatedAntPattern, boolean excludeBuildFolders, File outputContractsFolder) { - return project.sync(new Action() { - @Override - void execute(final CopySpec spec) { - spec.with { - from(file) - // by default group id is slash separated... - include(antPattern) - // ...we also want to allow dot separation - include(slashSeparatedAntPattern) - if (excludeBuildFolders) { - exclude "**/target/**", "**/build/**", "**/.mvn/**", "**/.gradle/**" - } - into(outputContractsFolder) - } - } - }) - } - - private DownloadedData downloadContractsIfNeeded() { - if (config.shouldDownloadContracts()) { - logger.info("Project has group id [{}], artifact id [{}]", project.group, project.name) - logger.info("For project [${project.name}] Download dependency is provided - will download contract jars") - logger.info("Contract dependency [{}]", config.contractDependency) - StubConfiguration configuration = config.getStubConfiguration() - logger.info("Got the following contract dependency to download [{}]", configuration) - logger.info("The contract dependency is a changing one [{}]", configuration.isVersionChanging()) - - final StubDownloader downloader = new StubDownloaderBuilderProvider().get( - StubRunnerOptionsFactory.createStubRunnerOptions(config.contractRepository, - config.contractsMode.getOrNull(), config.deleteStubsAfterTest.get(), - config.contractsProperties.get(), config.failOnNoContracts.get())) - final ContractDownloader contractDownloader = new ContractDownloader(downloader, configuration, - config.contractsPath.getOrNull(), project.group as String, project.name, project.version as String) - final File downloadedContracts = contractDownloader.unpackAndDownloadContracts(); - final ContractDownloader.InclusionProperties inclusionProperties = - contractDownloader.createNewInclusionProperties(downloadedContracts) - - // TODO: inclusionProperties.includedContracts is never used eventually. Review this: - return new DownloadedData( - downloadedContracts: contractsSubDirIfPresent(downloadedContracts, logger), - inclusionProperties: inclusionProperties - ) - } - else { - return null - } - } - - private static DirectoryProperty createTaskOutput(String root, DirectoryProperty stubsOutputDir, String suffix, Project project) { - Provider provider = stubsOutputDir.flatMap { - Directory dir = it - File output = project.file("${dir.asFile}/${root}/${suffix}") - - DirectoryProperty property = project.objects.directoryProperty() - property.set(output) - return property - } - DirectoryProperty property = project.objects.directoryProperty(); - property.set(provider) - return property - } - - private void throwExceptionWhenFailOnNoContracts(File file, String contractsRepository) { - if (StringUtils.hasText(contractsRepository)) { - if (logger.isDebugEnabled()) { - logger.debug("Contracts repository is set, will not throw an exception that the contracts are not found") - } - return - } - if (config.failOnNoContracts.get() && (file == null || !file.exists() || file.listFiles().length == 0)) { - String path = file != null ? file.getAbsolutePath() : config.contractsDirectoryPath - throw new GradleException("Contracts could not be found: [" + path + "]\nPlease make sure that the contracts were defined, or set the [failOnNoContracts] flag to [false]") - } - } - - private static File contractsSubDirIfPresent(File contractsDirectory, Logger logger) { - File contracts = new File(contractsDirectory, "contracts") - if (contracts.exists()) { - if (logger.isDebugEnabled()) { - logger.debug("Contracts folder found [" + contracts + "]") - } - contractsDirectory = contracts - } - return contractsDirectory - } - - @ImmutableOptions(knownImmutableClasses = [File, ContractDownloader.InclusionProperties]) - @Immutable - private static class DownloadedData { - final File downloadedContracts - final ContractDownloader.InclusionProperties inclusionProperties - } -} \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.groovy deleted file mode 100644 index 04499c01b3..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.groovy +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin - -import groovy.transform.CompileStatic -import org.gradle.api.DefaultTask -import org.gradle.api.Project -import org.gradle.api.file.Directory -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.TaskProvider -import org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverter - -//TODO: Implement as an incremental task: https://gradle.org/docs/current/userguide/custom_tasks.html#incremental_tasks ? -/** - * Generates stubs from the contracts. - * - * @author Marcin Grzejszczak - * @author Anatoliy Balakirev - * @since 2.0.0 - */ -@CompileStatic -class GenerateClientStubsFromDslTask extends DefaultTask { - - static final String TASK_NAME = 'generateClientStubs' - private static final String DEFAULT_MAPPINGS_FOLDER = 'mappings' - @Nested - Config config - - static class Config { - @InputDirectory - Provider contractsDslDir - @Input - ListProperty excludedFiles - @Input - Provider excludeBuildFolders - - @OutputDirectory - Provider stubsOutputDir - } - - @TaskAction - void generate() { - File output = config.stubsOutputDir.get().asFile - logger.info("Stubs output dir [${output}") - logger.info("Spring Cloud Contract Verifier Plugin: Invoking DSL to client stubs conversion") - logger.info("Contracts dir is [${config.contractsDslDir.get().asFile}] output stubs dir is [${output}]") - List excludedFiles = config.excludedFiles.get() - RecursiveFilesConverter converter = new RecursiveFilesConverter(output, - config.contractsDslDir.get().asFile, excludedFiles, ".*", config.excludeBuildFolders.get()) - converter.processFiles() - } - - static Config fromExtension(ContractVerifierExtension extension, TaskProvider copyContracts, - String root, Project project) { - return new Config( - contractsDslDir: copyContracts.flatMap { it.config.copiedContractsFolder }, - excludedFiles: extension.excludedFiles, - excludeBuildFolders: extension.excludeBuildFolders, - - stubsOutputDir: createTaskOutput(root, extension.stubsOutputDir, project) - ) - } - - private static DirectoryProperty createTaskOutput(String root, DirectoryProperty stubsOutputDir, Project project) { - Provider provider = stubsOutputDir.flatMap { - Directory dir = it - File output = new File(dir.asFile, "${root}/${DEFAULT_MAPPINGS_FOLDER}") - - DirectoryProperty property = project.objects.directoryProperty(); - property.set(output) - return property - } - DirectoryProperty property = project.objects.directoryProperty(); - property.set(provider) - return property - } -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy deleted file mode 100644 index 6ca601b0d6..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin - -import groovy.transform.CompileStatic -import groovy.transform.builder.Builder -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.file.Directory -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.MapProperty -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.TaskProvider -import org.springframework.cloud.contract.spec.ContractVerifierException -import org.springframework.cloud.contract.verifier.TestGenerator -import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties -import org.springframework.cloud.contract.verifier.config.TestFramework -import org.springframework.cloud.contract.verifier.config.TestMode - -/** - * Task used to generate server side tests - * - * @author Marcin Grzejszczak - * @author Anatoliy Balakirev - * @since 1.0.0 - */ -@CompileStatic -class GenerateServerTestsTask extends DefaultTask { - static final String TASK_NAME = 'generateContractTests' - @Nested - Config config - - @CompileStatic - @Builder - static class Config { - - final Provider contractsDslDir - final Provider nameSuffixForTests - final Provider basePackageForTests - final Provider baseClassForTests - final Provider packageWithBaseClasses - final ListProperty excludedFiles - final ListProperty ignoredFiles - final ListProperty includedFiles - final ListProperty imports - final ListProperty staticImports - final Provider testMode - final Provider testFramework - final MapProperty baseClassMappings - final Provider assertJsonSize - final Provider failOnInProgress - final DirectoryProperty generatedTestSourcesDir - final DirectoryProperty generatedTestResourcesDir - - Config(Provider contractsDslDir, Provider nameSuffixForTests, Provider basePackageForTests, Provider baseClassForTests, Provider packageWithBaseClasses, ListProperty excludedFiles, ListProperty ignoredFiles, ListProperty includedFiles, ListProperty imports, ListProperty staticImports, Provider testMode, Provider testFramework, MapProperty baseClassMappings, Provider assertJsonSize, Provider failOnInProgress, DirectoryProperty generatedTestSourcesDir, DirectoryProperty generatedTestResourcesDir) { - this.contractsDslDir = contractsDslDir - this.nameSuffixForTests = nameSuffixForTests - this.basePackageForTests = basePackageForTests - this.baseClassForTests = baseClassForTests - this.packageWithBaseClasses = packageWithBaseClasses - this.excludedFiles = excludedFiles - this.ignoredFiles = ignoredFiles - this.includedFiles = includedFiles - this.imports = imports - this.staticImports = staticImports - this.testMode = testMode - this.testFramework = testFramework - this.baseClassMappings = baseClassMappings - this.assertJsonSize = assertJsonSize - this.failOnInProgress = failOnInProgress - this.generatedTestSourcesDir = generatedTestSourcesDir - this.generatedTestResourcesDir = generatedTestResourcesDir - } - - @InputDirectory - Provider getContractsDslDir() { - return contractsDslDir - } - - @Input - @Optional - Provider getNameSuffixForTests() { - return nameSuffixForTests - } - - @Input - @Optional - Provider getBasePackageForTests() { - return basePackageForTests - } - - @Input - @Optional - Provider getBaseClassForTests() { - return baseClassForTests - } - - @Input - @Optional - Provider getPackageWithBaseClasses() { - return packageWithBaseClasses - } - - @Input - ListProperty getExcludedFiles() { - return excludedFiles - } - - @Input - ListProperty getIgnoredFiles() { - return ignoredFiles - } - - @Input - ListProperty getIncludedFiles() { - return includedFiles - } - - @Input - ListProperty getImports() { - return imports - } - - @Input - ListProperty getStaticImports() { - return staticImports - } - - @Input - Provider getTestMode() { - return testMode - } - - @Input - Provider getTestFramework() { - return testFramework - } - - @Input - MapProperty getBaseClassMappings() { - return baseClassMappings - } - - @Input - Provider getAssertJsonSize() { - return assertJsonSize - } - - @Input - Provider getFailOnInProgress() { - return failOnInProgress - } - - @OutputDirectory - DirectoryProperty getGeneratedTestSourcesDir() { - return generatedTestSourcesDir - } - - @OutputDirectory - DirectoryProperty getGeneratedTestResourcesDir() { - return generatedTestResourcesDir - } - } - - @TaskAction - void generate() { - File generatedTestSources = config.generatedTestSourcesDir.get().asFile - File generatedTestResources = config.generatedTestResourcesDir.get().asFile - logger.info("Generated test sources dir [${generatedTestSources}]") - logger.info("Generated test resources dir [${generatedTestResources}]") - File contractsDslDir = config.contractsDslDir.get().asFile - String includedContracts = ".*" - project.logger.info("Spring Cloud Contract Verifier Plugin: Invoking test sources generation") - project.logger.info("Contracts are unpacked to [${contractsDslDir}]") - project.logger.info("Included contracts are [${includedContracts}]") - try { - List excludedFiles = config.excludedFiles.get() - List ignoredFiles = config.ignoredFiles.get() - List includedFiles = config.includedFiles.get() - String[] imports = config.imports.get().toArray(new String[0]) - String[] staticImports = config.staticImports.get().toArray(new String[0]) - TestGenerator generator = new TestGenerator(new ContractVerifierConfigProperties( - includedContracts: includedContracts, - contractsDslDir: contractsDslDir, - nameSuffixForTests: config.nameSuffixForTests.getOrNull(), - generatedTestSourcesDir: generatedTestSources, - generatedTestResourcesDir: generatedTestResources, - basePackageForTests: config.basePackageForTests.getOrNull(), - baseClassForTests: config.baseClassForTests.getOrNull(), - packageWithBaseClasses: config.packageWithBaseClasses.getOrNull(), - excludedFiles: excludedFiles, - ignoredFiles: ignoredFiles, - includedFiles: includedFiles, - imports: imports, - staticImports: staticImports, - testMode: config.testMode.get(), - testFramework: config.testFramework.get(), - baseClassMappings: config.baseClassMappings.get(), - assertJsonSize: config.assertJsonSize.get(), - failOnInProgress: config.failOnInProgress.get() - )) - int generatedClasses = generator.generate() - project.logger.info("Generated {} test classes", generatedClasses) - } - catch (ContractVerifierException e) { - throw new GradleException("Spring Cloud Contract Verifier Plugin exception: ${e.message}", e) - } - } - - static Config fromExtension(ContractVerifierExtension extension, TaskProvider copyContractsTask) { - return new Config( - copyContractsTask.flatMap { it.config.copiedContractsFolder }, - extension.nameSuffixForTests, - extension.basePackageForTests, - extension.baseClassForTests, - extension.packageWithBaseClasses, - extension.excludedFiles, - extension.ignoredFiles, - extension.includedFiles, - extension.imports, - extension.staticImports, - extension.testMode, - extension.testFramework, - extension.baseClassMappings.getBaseClassMappings(), - extension.assertJsonSize, - extension.failOnInProgress, - extension.generatedTestSourcesDir, - extension.generatedTestResourcesDir) - } -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelper.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelper.groovy deleted file mode 100644 index 45d3855a0f..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelper.groovy +++ /dev/null @@ -1,33 +0,0 @@ -package org.springframework.cloud.contract.verifier.plugin - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.springframework.cloud.contract.stubrunner.StubConfiguration -import org.springframework.util.StringUtils - -/** - * @author Anatoliy Balakirev - */ -@PackageScope -@CompileStatic -class GradleContractsDownloaderHelper { - - private static final String LATEST_VERSION = '+' - - @PackageScope - static StubConfiguration stubConfiguration(ContractVerifierExtension.Dependency contractDependency) { - String groupId = contractDependency.groupId.getOrNull() - String artifactId = contractDependency.artifactId.getOrNull() - String version = StringUtils.hasText(contractDependency.version.getOrNull()) ? - contractDependency.version.getOrNull() : LATEST_VERSION - String classifier = contractDependency.classifier.getOrNull() - String stringNotation = contractDependency.stringNotation.getOrNull() - if (StringUtils.hasText(stringNotation)) { - StubConfiguration stubConfiguration = new StubConfiguration(stringNotation) - return new StubConfiguration(stubConfiguration.groupId, stubConfiguration.artifactId, - stubConfiguration.version, stubConfiguration.classifier) - } - return new StubConfiguration(groupId, artifactId, version, classifier) - } - -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.groovy deleted file mode 100644 index 34c6b8bcad..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.groovy +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin - -import groovy.transform.CompileStatic -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.MapProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction -import org.springframework.cloud.contract.stubrunner.ContractProjectUpdater -import org.springframework.cloud.contract.stubrunner.ScmStubDownloaderBuilder -import org.springframework.cloud.contract.stubrunner.StubRunnerOptions -import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties - -/** - * For SCM based repositories will copy the generated stubs - * to the cloned repo with contracts and stubs. Will also - * commit the changes and push them to origin. - * - * NOTE: starting with 2.3.0.RELEASE the customize{} closure previously used for - * {@link PublishStubsToScmTask} customisation is no longer available. The settings should be applied directly - * within the publishStubsToScm closure as in the example above. - * - * @author Marcin Grzejszczak - * @author Anatoliy Balakirev - * @since 2.0.0 - */ -@CompileStatic -class PublishStubsToScmTask extends DefaultTask { - - static final String TASK_NAME = 'publishStubsToScm' - @Nested - Config config - - @CompileStatic - static class Config { - @Nested - final Property contractRepository - @Nested - final Property publishStubsToScm - @Input - final Property contractsMode - @Input - final Property deleteStubsAfterTest - @Input - final Property failOnNoContracts - @Input - final MapProperty contractsProperties - @OutputDirectory - final DirectoryProperty stubsOutputDir - @Internal - final ObjectFactory objects - - Config(ObjectFactory objects, ContractVerifierExtension extension) { - this.objects = objects - this.contractRepository = objects.property(ContractVerifierExtension.ContractRepository) - this.contractRepository.set(extension.contractRepository) - this.publishStubsToScm = objects.property(ContractVerifierExtension.PublishStubsToScm) - this.publishStubsToScm.set(extension.publishStubsToScm) - this.contractsMode = objects.property(StubRunnerProperties.StubsMode) - this.contractsMode.set(extension.contractsMode) - this.deleteStubsAfterTest = objects.property(Boolean) - this.deleteStubsAfterTest.set(extension.failOnNoContracts) - this.failOnNoContracts = objects.property(Boolean) - this.failOnNoContracts.set(extension.deleteStubsAfterTest) - this.contractsProperties = objects.mapProperty(String, String) - this.contractsProperties.set(extension.contractsProperties) - this.stubsOutputDir = objects.directoryProperty() - this.stubsOutputDir.set(extension.stubsOutputDir) - } - } - - @TaskAction - void publishStubsToScm() { - ContractVerifierExtension.ContractRepository repository = merged() - if (!shouldRun(repository)) { - return - } - String projectName = project.group.toString() + ":" + project.name.toString() + ":" + this.project.version.toString() - project.logger.info("Pushing Stubs to SCM for project [" + projectName + "]") - StubRunnerOptions stubRunnerOptions = StubRunnerOptionsFactory.createStubRunnerOptions( - repository, config.contractsMode.getOrElse(StubRunnerProperties.StubsMode.REMOTE), config.deleteStubsAfterTest.get(), - config.contractsProperties.get(), config.failOnNoContracts.get()) - new ContractProjectUpdater(stubRunnerOptions).updateContractProject(projectName, config.stubsOutputDir.get().asFile.toPath()) - } - - static Config fromExtension(ContractVerifierExtension extension, ObjectFactory objects) { - return new Config(objects, extension) - } - - private boolean shouldRun(ContractVerifierExtension.ContractRepository repository) { - String contractRepoUrl = repository.repositoryUrl.getOrNull() ?: "" - if (!contractRepoUrl || !ScmStubDownloaderBuilder.isProtocolAccepted(contractRepoUrl)) { - project.logger.warn("Skipping pushing stubs to scm since your contracts repository URL [${contractRepoUrl}] doesn't match any of the accepted protocols for SCM stub downloader") - return false - } - return true - } - - private ContractVerifierExtension.ContractRepository merged() { - ContractVerifierExtension.ContractRepository original = config.contractRepository.get() - ContractVerifierExtension.ContractRepository stubs = config.publishStubsToScm.get().getContractRepository() - ContractVerifierExtension.ContractRepository copied = new ContractVerifierExtension.ContractRepository(config.objects) - copied.setRepositoryUrl(stubs.getRepositoryUrl().getOrElse(original.getRepositoryUrl().getOrNull())) - copied.setUsername(stubs.getUsername().getOrElse(original.getUsername().getOrNull())) - copied.setPassword(stubs.getPassword().getOrElse(original.getPassword().getOrNull())) - Integer port = stubs.getProxyPort().getOrElse(original.getProxyPort().getOrNull()) - if (port != null ) { - copied.setProxyPort(port) - } - copied.setProxyHost(stubs.getProxyHost().getOrElse(original.getProxyHost().getOrNull())) - return copied - } -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy deleted file mode 100644 index 6c3d643ec4..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin - -import groovy.transform.CompileDynamic -import groovy.transform.CompileStatic -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.plugins.GroovyPlugin -import org.gradle.api.publish.maven.MavenPublication -import org.gradle.api.publish.maven.plugins.MavenPublishPlugin -import org.gradle.api.tasks.TaskProvider -import org.gradle.api.tasks.bundling.Jar - -import org.springframework.cloud.contract.verifier.config.TestFramework - -/** - * Gradle plugin for Spring Cloud Contract Verifier that from the DSL contract can - *
    - *
  • generate tests
  • - *
  • generate stubs
  • - *
- * - * @author Jakub Kubrynski, codearte.io - * @author Marcin Grzejszczak - * @author Anatoliy Balakirev - * - * @since 1.0.0 - */ -@CompileStatic -class SpringCloudContractVerifierGradlePlugin implements Plugin { - - private static final String VERIFIER_STUBS_JAR_TASK_NAME = 'verifierStubsJar' - private static final String GROUP_NAME = "Verification" - private static final String EXTENSION_NAME = 'contracts' - - private Project project - - @Override - void apply(Project project) { - this.project = project - project.plugins.apply(GroovyPlugin) - ContractVerifierExtension extension = project.extensions.create(EXTENSION_NAME, ContractVerifierExtension) - - TaskProvider copyContracts = createAndConfigureCopyContractsTask(extension) - TaskProvider generateClientStubs = createAndConfigureGenerateClientStubs(extension, copyContracts) - - createAndConfigureStubsJarTasks(extension, copyContracts, generateClientStubs) - createGenerateTestsTask(extension, copyContracts) - createAndConfigurePublishStubsToScmTask(extension, generateClientStubs) - project.afterEvaluate { - addIdeaTestSources(project, extension) - applyDefaultSourceSets(extension) - } - } - - // This must be called within afterEvaluate due to getting data from extension, which must be initialised first: - @CompileDynamic - private void applyDefaultSourceSets(ContractVerifierExtension extension) { - boolean sourceSetPresent = extension.getSourceSet().isPresent() - String sourceSet = sourceSet(sourceSetPresent, extension) - String sourceSetType = extension.testFramework.get() == TestFramework.SPOCK ? "groovy" : "java" - project.sourceSets."${sourceSet}"."${sourceSetType}" { - project.logger. - info("Registering ${extension.generatedTestSourcesDir.get().asFile} as test source directory") - srcDir extension.generatedTestSourcesDir.get().asFile - } - project.sourceSets.test.resources { - project.logger. - info("Registering ${extension.generatedTestResourcesDir.get().asFile} as test resource directory") - srcDir extension.generatedTestResourcesDir.get().asFile - } - } - - private String sourceSet(boolean sourceSetPresent, ContractVerifierExtension extension) { - if (sourceSetPresent) { - return extension.getSourceSet().get() - } - return "test" - } - - // This must be called within afterEvaluate due to getting data from extension, which must be initialised first: - @CompileDynamic - private addIdeaTestSources(Project project, ContractVerifierExtension extension) { - boolean hasIdea = new File(project.rootDir, ".idea").exists() - if (hasIdea) { - project.apply(plugin: 'idea') - project.idea { - module { - testSourceDirs += extension.generatedTestSourcesDir.get().asFile - testSourceDirs += extension.generatedTestResourcesDir.get().asFile - testSourceDirs += extension.contractsDslDir.get().asFile - } - } - } - } - - private void createGenerateTestsTask(ContractVerifierExtension extension, TaskProvider copyContracts) { - TaskProvider task = project.tasks.register(GenerateServerTestsTask.TASK_NAME, GenerateServerTestsTask) - task.configure { - it.description = "Generate server tests from the contracts" - it.group = GROUP_NAME - it.enabled = !project.gradle.startParameter.excludedTaskNames.contains("test") - it.config = GenerateServerTestsTask.fromExtension(extension, copyContracts) - - it.dependsOn copyContracts - } - project.tasks.findByName("compileTestJava").dependsOn(task) - project.tasks.findByName("check").dependsOn(task) - } - - private void createAndConfigurePublishStubsToScmTask(ContractVerifierExtension extension, - TaskProvider generateClientStubs) { - TaskProvider task = project.tasks.register(PublishStubsToScmTask.TASK_NAME, PublishStubsToScmTask) - task.configure { - it.description = "The generated stubs get committed to the SCM repo and pushed to origin" - it.group = GROUP_NAME - it.config = PublishStubsToScmTask.fromExtension(extension, project.objects) - - it.dependsOn generateClientStubs - } - } - - private TaskProvider createAndConfigureGenerateClientStubs(ContractVerifierExtension extension, - TaskProvider copyContracts) { - TaskProvider task = project.tasks.register(GenerateClientStubsFromDslTask.TASK_NAME, GenerateClientStubsFromDslTask) - task.configure { - it.description = "Generate client stubs from the contracts" - it.group = GROUP_NAME - it.config = GenerateClientStubsFromDslTask.fromExtension(extension, copyContracts, buildRootPath(), project) - - it.dependsOn copyContracts - } - return task - } - - private TaskProvider createAndConfigureStubsJarTasks(ContractVerifierExtension extension, - TaskProvider copyContracts, - TaskProvider generateClientStubs) { - TaskProvider task = stubsTask() - if (task) { - // How is this possible? Where can it come from? - project.logger.info("Spring Cloud Contract Verifier Plugin: Stubs jar task was present - won't create one. Remember about adding it to artifacts as an archive!") - } - else { - task = createStubsJarTask(extension, generateClientStubs) - } - task.configure { - it.dependsOn copyContracts - } - createAndConfigureMavenPublishPlugin(task, extension) - return task - } - - @Deprecated - private void createAndConfigureMavenPublishPlugin(TaskProvider stubsTask, ContractVerifierExtension extension) { - if (!classIsOnClasspath("org.gradle.api.publish.maven.plugins.MavenPublishPlugin")) { - project.logger.debug("Maven Publish Plugin is not present - won't add default publication") - return - } - // This must be called within afterEvaluate due to getting data from extension, which must be initialised first: - project.afterEvaluate { - project.logger.debug("Spring Cloud Contract Verifier Plugin: Generating default publication") - if (extension.disableStubPublication.get()) { - project.logger.info("You've switched off the stub publication - won't add default publication") - return - } - project.plugins.withType(MavenPublishPlugin) { def publishingPlugin -> - def publishingExtension = project.extensions.findByName('publishing') - if (hasStubsPublication(publishingExtension)) { - project.logger.info("Spring Cloud Contract Verifier Plugin: Stubs publication was present - won't create a new one. Remember about passing stubs as artifact") - } - else { - project.logger.debug("Spring Cloud Contract Verifier Plugin: Stubs publication is not present - will create one") - setPublications(publishingExtension, stubsTask) - } - } - } - } - - @CompileDynamic - @Deprecated - private void setPublications(def publishingExtension, TaskProvider stubsTask) { - project.logger.warn("Spring Cloud Contract Verifier Plugin: Creating stubs publication is deprecated") - publishingExtension.publications { - stubs(MavenPublication) { - artifactId "${project.name}" - artifact stubsTask.get() // TODO: How to make it lazily initialised? - } - } - } - - private TaskProvider stubsTask() { - try { - return project.tasks.named(VERIFIER_STUBS_JAR_TASK_NAME) - } - catch (Exception e) { - return null - } - } - - @CompileDynamic - @Deprecated - private boolean hasStubsPublication(def publishingExtension) { - try { - return publishingExtension.publications.getByName('stubs') - } - catch (Exception e) { - return false - } - } - - // TODO: Can we define inputs / outputs and make it incremental? - @CompileDynamic - private TaskProvider createStubsJarTask(ContractVerifierExtension extension, - TaskProvider generateClientStubs) { - TaskProvider task = project.tasks.register(VERIFIER_STUBS_JAR_TASK_NAME, Jar) - task.configure { - it.description = "Creates the stubs JAR task" - it.group = GROUP_NAME - it.getArchiveBaseName().set(project.name) - it.getArchiveClassifier().set(extension.stubsSuffix) - it.from { extension.stubsOutputDir } - - it.dependsOn generateClientStubs - } - project.artifacts { - archives task - } - return task - } - - private TaskProvider createAndConfigureCopyContractsTask(ContractVerifierExtension extension) { - TaskProvider task = project.tasks.register(ContractsCopyTask.TASK_NAME, ContractsCopyTask) - task.configure { - it.description = "Copies contracts to the output folder" - it.group = GROUP_NAME - it.config = ContractsCopyTask.fromExtension(extension, buildRootPath(), project) - } - return task - } - - @Deprecated - private boolean classIsOnClasspath(String className) { - try { - Class.forName(className) - return true - } - catch (Exception e) { - project.logger.debug("Maven Publish Plugin is not available") - } - return false - } - - private String buildRootPath() { - String groupId = project.group as String - String artifactId = project.name - String version = project.version - return "META-INF/${groupId}/${artifactId}/${version}" - } -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/StubRunnerOptionsFactory.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/StubRunnerOptionsFactory.groovy deleted file mode 100644 index 9ad7e22e60..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/StubRunnerOptionsFactory.groovy +++ /dev/null @@ -1,35 +0,0 @@ -package org.springframework.cloud.contract.verifier.plugin - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.springframework.cloud.contract.stubrunner.StubRunnerOptions -import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder -import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties - -/** - * Helper class to create StubRunnerOptions. - * - * @author Anatoliy Balakirev - */ -@CompileStatic -@PackageScope -class StubRunnerOptionsFactory { - - static StubRunnerOptions createStubRunnerOptions(ContractVerifierExtension.ContractRepository contractRepository, - StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest, - Map contractsProperties, boolean failOnNoContracts) { - StubRunnerOptionsBuilder options = new StubRunnerOptionsBuilder() - .withOptions(StubRunnerOptions.fromSystemProps()) - .withStubRepositoryRoot(contractRepository.repositoryUrl.getOrNull()) - .withStubsMode(contractsMode) - .withUsername(contractRepository.username.getOrNull()) - .withPassword(contractRepository.password.getOrNull()) - .withDeleteStubsAfterTest(deleteStubsAfterTest) - .withProperties(contractsProperties) - .withFailOnNoStubs(failOnNoContracts) - if (contractRepository.proxyPort.getOrNull()) { - options = options.withProxy(contractRepository.proxyHost.getOrNull(), contractRepository.proxyPort.getOrNull()) - } - return options.build() - } -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.java similarity index 77% rename from spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.java rename to spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.java index c2791fc036..e36fdd9bf5 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.java @@ -23,14 +23,9 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Consumer; import javax.inject.Inject; -import groovy.lang.Closure; -import groovy.lang.DelegatesTo; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.gradle.api.Action; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.ProjectLayout; @@ -38,11 +33,6 @@ import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.Nested; -import org.gradle.api.tasks.Optional; - import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; import org.springframework.cloud.contract.verifier.config.TestFramework; import org.springframework.cloud.contract.verifier.config.TestMode; @@ -55,100 +45,104 @@ import org.springframework.util.Assert; */ public class ContractVerifierExtension implements Serializable { - private static final Log log = LogFactory.getLog(ContractVerifierExtension.class); - /** * For which unit test library tests should be generated */ - private Property testFramework; + private final Property testFramework; /** * Which mechanism should be used to invoke REST calls during tests */ - private Property testMode; + private final Property testMode; /** * Base package for generated tests */ - private Property basePackageForTests; + private final Property basePackageForTests; /** * Class which all generated tests should extend */ - private Property baseClassForTests; + private final Property baseClassForTests; /** * Suffix for generated test classes, like Spec or Test */ - private Property nameSuffixForTests; - - /** - * Rule class that should be added to generated tests - */ - private Property ruleClassForTests; + private final Property nameSuffixForTests; /** * Patterns that should not be taken into account for processing */ - private ListProperty excludedFiles; + private final ListProperty excludedFiles; /** * Patterns that should be taken into account for processing */ - private ListProperty includedFiles; + private final ListProperty includedFiles; /** * Patterns for which generated tests should be @Ignored */ - private ListProperty ignoredFiles; + private final ListProperty ignoredFiles; /** * Imports that should be added to generated tests */ - private ListProperty imports; + private final ListProperty imports; /** * Static imports that should be added to generated tests */ - private ListProperty staticImports; + private final ListProperty staticImports; /** * Directory containing contracts written using the GroovyDSL */ - private DirectoryProperty contractsDslDir; + private final DirectoryProperty contractsDslDir; /** * Test source directory where tests generated from Groovy DSL should be placed */ - private DirectoryProperty generatedTestSourcesDir; + @Deprecated + private final DirectoryProperty generatedTestSourcesDir; + + /** + * Java test source directory where tests generated from Contract DSL should be placed + */ + private final DirectoryProperty generatedTestJavaSourcesDir; + + /** + * Groovy test source directory where tests generated from Contract DSL should be placed + */ + private final DirectoryProperty generatedTestGroovySourcesDir; /** * Test resource directory where tests generated from Groovy DSL should be referenced */ - private DirectoryProperty generatedTestResourcesDir; + private final DirectoryProperty generatedTestResourcesDir; /** * Dir where the generated stubs from Groovy DSL should be placed. * You can then mention them in your packaging task to create jar with stubs */ - private DirectoryProperty stubsOutputDir; + private final DirectoryProperty stubsOutputDir; /** * Suffix for the generated Stubs Jar task */ - private Property stubsSuffix; + private final Property stubsSuffix; /** * Incubating feature. You can check the size of JSON arrays. If not turned on * explicitly will be disabled. */ - private Property assertJsonSize; + private final Property assertJsonSize; /** * When enabled, this flag will tell stub runner to throw an exception when no stubs / * contracts were found. */ - private Property failOnNoContracts; + private final Property failOnNoContracts; /** * If set to true then if any contracts that are in progress are found, will break the @@ -156,16 +150,16 @@ public class ContractVerifierExtension implements Serializable { * contracts in progress and take into consideration that you might be causing false * positive test execution results on the consumer side. */ - private Property failOnInProgress; + private final Property failOnInProgress; - private ContractRepository contractRepository; + private final ContractRepository contractRepository; - private PublishStubsToScm publishStubsToScm; + private final PublishStubsToScm publishStubsToScm; /** * Dependency that contains packaged contracts */ - private Dependency contractDependency; + private final Dependency contractDependency; /** * The path in the JAR with all the contracts where contracts for this particular service lay. @@ -173,12 +167,12 @@ public class ContractVerifierExtension implements Serializable { * If {@code groupid} is {@code com.example} and {@code artifactid} is {@code service} then the resolved path will be * {@code /com/example/artifactid} */ - private Property contractsPath; + private final Property contractsPath; /** * Picks the mode in which stubs will be found and registered */ - private Property contractsMode; + private final Property contractsMode; /** * A package that contains all the base clases for generated tests. If your contract resides in a location @@ -187,7 +181,7 @@ public class ContractVerifierExtension implements Serializable { * have the package {@code com.example.contracts.base} and name {@code ExampleV1Base}. As you can see * it will take the two last folders to and attach {@code Base} to its name. */ - private Property packageWithBaseClasses; + private final Property packageWithBaseClasses; /** * A way to override any base class mappings. The keys are regular expressions on the package name @@ -197,39 +191,30 @@ public class ContractVerifierExtension implements Serializable { * When a contract's package matches the provided regular expression then extending class will be the one * provided in the map - in this case {@code com.example.SomeBaseClass} */ - private BaseClassMapping baseClassMappings; + private final BaseClassMapping baseClassMappings; /** * If set to true then the {@code target} or {@code build} folders are getting * excluded from any operations. This is used out of the box when working with * common repo with contracts. */ - private Property excludeBuildFolders; - - /** - * If set to {@code true} will not assert whether the downloaded stubs / contract - * JAR was downloaded from a remote location or a local one(only applicable to Maven repos, not Git or Pact) - * - * @deprecated - with 2.1.0 this option is redundant - */ - @Deprecated - private Property contractsSnapshotCheckSkip; + private final Property excludeBuildFolders; /** * If set to {@code false} will NOT delete stubs from a temporary * folder after running tests */ - private Property deleteStubsAfterTest; + private final Property deleteStubsAfterTest; /** * If {@code true} then will convert contracts to a YAML representation */ - private Property convertToYaml; + private final Property convertToYaml; /** * Map of properties that can be passed to custom {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder} */ - private MapProperty contractsProperties; + private final MapProperty contractsProperties; /** * Is set to true will not provide the default publication task @@ -237,14 +222,12 @@ public class ContractVerifierExtension implements Serializable { * @deprecated - with 3.0.0, the user should include stubs with their own publication(s) */ @Deprecated - private Property disableStubPublication; + private final Property disableStubPublication; /** * Source set where the contracts are stored. If not provided will assume {@code test}. */ - private Property sourceSet; - - private final ObjectFactory objects; + private final Property sourceSet; @Inject public ContractVerifierExtension(ProjectLayout layout, ObjectFactory objects) { @@ -253,15 +236,16 @@ public class ContractVerifierExtension implements Serializable { this.basePackageForTests = objects.property(String.class); this.baseClassForTests = objects.property(String.class); this.nameSuffixForTests = objects.property(String.class); - this.ruleClassForTests = objects.property(String.class); this.excludedFiles = objects.listProperty(String.class).convention(new ArrayList<>()); this.includedFiles = objects.listProperty(String.class).convention(new ArrayList<>()); this.ignoredFiles = objects.listProperty(String.class).convention(new ArrayList<>()); this.imports = objects.listProperty(String.class).convention(new ArrayList<>()); this.staticImports = objects.listProperty(String.class).convention(new ArrayList<>()); this.contractsDslDir = objects.directoryProperty().convention(layout.getProjectDirectory().dir("src/test/resources/contracts")); - this.generatedTestSourcesDir = objects.directoryProperty().convention(layout.getBuildDirectory().dir("generated-test-sources/contracts")); - this.generatedTestResourcesDir = objects.directoryProperty().convention(layout.getBuildDirectory().dir("generated-test-resources/contracts")); + this.generatedTestSourcesDir = objects.directoryProperty(); + this.generatedTestJavaSourcesDir = objects.directoryProperty().convention(layout.getBuildDirectory().dir("generated-test-sources/contractTest/java")); + this.generatedTestGroovySourcesDir = objects.directoryProperty().convention(layout.getBuildDirectory().dir("generated-test-sources/contractTest/groovy")); + this.generatedTestResourcesDir = objects.directoryProperty().convention(layout.getBuildDirectory().dir("generated-test-resources/contractTest")); this.stubsOutputDir = objects.directoryProperty().convention(layout.getBuildDirectory().dir("stubs")); this.stubsSuffix = objects.property(String.class).convention("stubs"); this.assertJsonSize = objects.property(Boolean.class).convention(false); @@ -275,24 +259,11 @@ public class ContractVerifierExtension implements Serializable { this.packageWithBaseClasses = objects.property(String.class); this.baseClassMappings = objects.newInstance(BaseClassMapping.class); this.excludeBuildFolders = objects.property(Boolean.class).convention(false); - this.contractsSnapshotCheckSkip = objects.property(Boolean.class).convention(false); this.deleteStubsAfterTest = objects.property(Boolean.class).convention(true); this.convertToYaml = objects.property(Boolean.class).convention(false); this.contractsProperties = objects.mapProperty(String.class, String.class).convention(new HashMap<>()); this.disableStubPublication = objects.property(Boolean.class).convention(true); this.sourceSet = objects.property(String.class); - this.objects = objects; - } - - @Deprecated - public void setTargetFramework(TestFramework targetFramework) { - log.warn("Please use the [testFramework] field. This one is deprecated"); - this.testFramework.set(targetFramework); - } - - @Deprecated - public TestFramework getTargetFramework() { - return getTestFramework().get(); } public Property getTestFramework() { @@ -304,9 +275,7 @@ public class ContractVerifierExtension implements Serializable { } public void setTestFramework(String testFramework) { - if (testFramework != null) { - this.testFramework.set(TestFramework.valueOf(testFramework.toUpperCase())); - } + this.testFramework.set(TestFramework.valueOf(testFramework.toUpperCase())); } public Property getTestMode() { @@ -318,9 +287,7 @@ public class ContractVerifierExtension implements Serializable { } public void setTestMode(String testMode) { - if (testMode != null) { - this.testMode.set(TestMode.valueOf(testMode.toUpperCase())); - } + this.testMode.set(TestMode.valueOf(testMode.toUpperCase())); } public Property getBasePackageForTests() { @@ -347,14 +314,6 @@ public class ContractVerifierExtension implements Serializable { this.nameSuffixForTests.set(nameSuffixForTests); } - public Property getRuleClassForTests() { - return ruleClassForTests; - } - - public void setRuleClassForTests(String ruleClassForTests) { - this.ruleClassForTests.set(ruleClassForTests); - } - public ListProperty getExcludedFiles() { return excludedFiles; } @@ -411,14 +370,32 @@ public class ContractVerifierExtension implements Serializable { this.contractsDslDir.set(contractsDslDir); } + @Deprecated public DirectoryProperty getGeneratedTestSourcesDir() { return generatedTestSourcesDir; } + @Deprecated public void setGeneratedTestSourcesDir(File generatedTestSourcesDir) { this.generatedTestSourcesDir.set(generatedTestSourcesDir); } + public DirectoryProperty getGeneratedTestJavaSourcesDir() { + return generatedTestJavaSourcesDir; + } + + public void setGeneratedTestJavaSourcesDir(File generatedTestJavaSourcesDir) { + this.generatedTestJavaSourcesDir.set(generatedTestJavaSourcesDir); + } + + public DirectoryProperty getGeneratedTestGroovySourcesDir() { + return generatedTestGroovySourcesDir; + } + + public void setGeneratedTestGroovySourcesDir(File generatedTestGroovySourcesDir) { + this.generatedTestGroovySourcesDir.set(generatedTestGroovySourcesDir); + } + public DirectoryProperty getGeneratedTestResourcesDir() { return generatedTestResourcesDir; } @@ -467,7 +444,6 @@ public class ContractVerifierExtension implements Serializable { this.failOnInProgress.set(failOnInProgress); } - @Nested public ContractRepository getContractRepository() { return contractRepository; } @@ -476,7 +452,6 @@ public class ContractVerifierExtension implements Serializable { action.execute(contractRepository); } - @Nested public PublishStubsToScm getPublishStubsToScm() { return publishStubsToScm; } @@ -510,9 +485,7 @@ public class ContractVerifierExtension implements Serializable { } public void setContractsMode(String contractsMode) { - if (contractsMode != null) { - this.contractsMode.set(StubRunnerProperties.StubsMode.valueOf(contractsMode.toUpperCase())); - } + this.contractsMode.set(StubRunnerProperties.StubsMode.valueOf(contractsMode.toUpperCase())); } public Property getPackageWithBaseClasses() { @@ -543,14 +516,6 @@ public class ContractVerifierExtension implements Serializable { this.excludeBuildFolders.set(excludeBuildFolders); } - public Property getContractsSnapshotCheckSkip() { - return contractsSnapshotCheckSkip; - } - - public void setContractsSnapshotCheckSkip(boolean contractsSnapshotCheckSkip) { - this.contractsSnapshotCheckSkip.set(contractsSnapshotCheckSkip); - } - public Property getDeleteStubsAfterTest() { return deleteStubsAfterTest; } @@ -575,12 +540,6 @@ public class ContractVerifierExtension implements Serializable { this.contractsProperties.set(contractsProperties); } - // use standard setter instead - @Deprecated - public void contractsProperties(Map map) { - contractsProperties.set(map); - } - @Deprecated public Property getDisableStubPublication() { return disableStubPublication; @@ -600,11 +559,11 @@ public class ContractVerifierExtension implements Serializable { } public static class Dependency implements Serializable { - private Property groupId; - private Property artifactId; - private Property version; - private Property classifier; - private Property stringNotation; + private final Property groupId; + private final Property artifactId; + private final Property version; + private final Property classifier; + private final Property stringNotation; @Inject public Dependency(ObjectFactory objects) { @@ -615,8 +574,6 @@ public class ContractVerifierExtension implements Serializable { stringNotation = objects.property(String.class); } - @Input - @Optional public Property getGroupId() { return groupId; } @@ -625,8 +582,6 @@ public class ContractVerifierExtension implements Serializable { this.groupId.set(groupId); } - @Input - @Optional public Property getArtifactId() { return artifactId; } @@ -635,8 +590,6 @@ public class ContractVerifierExtension implements Serializable { this.artifactId.set(artifactId); } - @Input - @Optional public Property getVersion() { return version; } @@ -645,8 +598,6 @@ public class ContractVerifierExtension implements Serializable { this.version.set(version); } - @Input - @Optional public Property getClassifier() { return classifier; } @@ -655,8 +606,6 @@ public class ContractVerifierExtension implements Serializable { this.classifier.set(classifier); } - @Input - @Optional public Property getStringNotation() { return stringNotation; } @@ -700,17 +649,11 @@ public class ContractVerifierExtension implements Serializable { // This class is used as an input to the tasks, so all fields are marked as `@Input` to allow incremental build public static class ContractRepository implements Serializable { - private Property repositoryUrl; - private Property username; - private Property password; - private Property proxyPort; - private Property proxyHost; - /** - * If set to true then will cache the folder where non snapshot contract artifacts got downloaded. - * - * Not used any more, as we switched to Gradle's incremental build. - */ - private Property cacheDownloadedContracts; + private final Property repositoryUrl; + private final Property username; + private final Property password; + private final Property proxyPort; + private final Property proxyHost; @Inject public ContractRepository(ObjectFactory objects) { @@ -719,11 +662,8 @@ public class ContractVerifierExtension implements Serializable { this.password = objects.property(String.class); this.proxyHost = objects.property(String.class); this.proxyPort = objects.property(Integer.class); - this.cacheDownloadedContracts = objects.property(Boolean.class).convention(true); } - @Input - @Optional public Property getRepositoryUrl() { return repositoryUrl; } @@ -732,14 +672,6 @@ public class ContractVerifierExtension implements Serializable { this.repositoryUrl.set(repositoryUrl); } - // favor property assignment - @Deprecated - public void repositoryUrl(String repositoryUrl) { - this.repositoryUrl.set(repositoryUrl); - } - - @Input - @Optional public Property getUsername() { return username; } @@ -748,14 +680,6 @@ public class ContractVerifierExtension implements Serializable { this.username.set(username); } - // favor property assignment - @Deprecated - public void username(String username) { - this.username.set(username); - } - - @Input - @Optional public Property getPassword() { return password; } @@ -764,14 +688,6 @@ public class ContractVerifierExtension implements Serializable { this.password.set(password); } - // favor property assignment - @Deprecated - public void password(String password) { - this.password.set(password); - } - - @Input - @Optional public Property getProxyHost() { return proxyHost; } @@ -780,14 +696,6 @@ public class ContractVerifierExtension implements Serializable { this.proxyHost.set(proxyHost); } - // favor property assignment - @Deprecated - public void proxyHost(String proxyHost) { - this.proxyHost.set(proxyHost); - } - - @Input - @Optional public Property getProxyPort() { return proxyPort; } @@ -797,17 +705,6 @@ public class ContractVerifierExtension implements Serializable { this.proxyPort.set(proxyPort); } - @Internal - @Deprecated - public Property getCacheDownloadedContracts() { - return cacheDownloadedContracts; - } - - @Deprecated - public void setCacheDownloadedContracts(boolean cacheDownloadedContracts) { - this.cacheDownloadedContracts.set(cacheDownloadedContracts); - } - @Override public String toString() { return "ContractRepository{" + @@ -816,7 +713,6 @@ public class ContractVerifierExtension implements Serializable { ", password=" + password.getOrNull() + ", proxyPort=" + proxyPort.getOrNull() + ", proxyHost=" + proxyHost.getOrNull() + - ", cacheDownloadedContracts=" + cacheDownloadedContracts.get() + '}'; } } @@ -826,9 +722,9 @@ public class ContractVerifierExtension implements Serializable { /** * Dependency that contains packaged contracts */ - private Dependency contractDependency; + private final Dependency contractDependency; - private ContractRepository contractRepository; + private final ContractRepository contractRepository; @Inject public PublishStubsToScm(ObjectFactory objects) { @@ -836,7 +732,6 @@ public class ContractVerifierExtension implements Serializable { contractRepository = objects.newInstance(ContractRepository.class); } - @Nested public Dependency getContractDependency() { return contractDependency; } @@ -845,7 +740,6 @@ public class ContractVerifierExtension implements Serializable { action.execute(contractDependency); } - @Nested public ContractRepository getContractRepository() { return contractRepository; } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.java new file mode 100644 index 0000000000..eb0d530d94 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/ContractsCopyTask.java @@ -0,0 +1,455 @@ +/* + * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin; + +import java.io.File; +import java.util.Collection; + +import javax.inject.Inject; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.gradle.api.Action; +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.Task; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; +import org.springframework.cloud.contract.stubrunner.ContractDownloader; +import org.springframework.cloud.contract.stubrunner.ScmStubDownloaderBuilder; +import org.springframework.cloud.contract.stubrunner.StubConfiguration; +import org.springframework.cloud.contract.stubrunner.StubDownloader; +import org.springframework.cloud.contract.stubrunner.StubDownloaderBuilderProvider; +import org.springframework.cloud.contract.stubrunner.StubRunnerOptions; +import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder; +import org.springframework.cloud.contract.stubrunner.StubRunnerPropertyUtils; +import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; +import org.springframework.cloud.contract.verifier.converter.ToYamlConverter; +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; + +// TODO: Convert to incremental task: https://docs.gradle.org/current/userguide/custom_tasks.html#incremental_tasks +/** + * Task that copies the contracts in order for the jar task to + * generate the jar. It takes into consideration the inclusion + * patterns when working with repo with shared contracts. + * + * @author Marcin Grzejszczak + * @author Anatoliy Balakirev + * @author Shannon Pamperl + * @since 1.0.2 + */ +@CacheableTask +class ContractsCopyTask extends DefaultTask { + + static final String TASK_NAME = "copyContracts"; + static final String CONTRACTS = "contracts"; + static final String BACKUP = "original"; + + // inputs + private final Property convertToYaml; + + private final Property failOnNoContracts; + + private final DirectoryProperty contractsDirectory; + + private final Dependency contractDependency; + + private final Repository contractRepository; + + private final Property contractsMode; + + private final MapProperty contractsProperties; + + private final Property contractsPath; + + private final Property excludeBuildFolders; + + /** + * @see ContractVerifierExtension#deleteStubsAfterTest + * + * This property will delete the temporary dependency or Git repository from + * which stubs were copied to this task's output directory. + */ + private final Property deleteStubsAfterTest; + + // outputs + private final DirectoryProperty copiedContractsFolder; + + private final DirectoryProperty backupContractsFolder; + + @Inject + public ContractsCopyTask(ObjectFactory objects) { + convertToYaml = objects.property(Boolean.class); + failOnNoContracts = objects.property(Boolean.class); + contractsDirectory = objects.directoryProperty(); + contractDependency = objects.newInstance(Dependency.class); + contractRepository = objects.newInstance(Repository.class); + contractsMode = objects.property(StubRunnerProperties.StubsMode.class); + contractsProperties = objects.mapProperty(String.class, String.class); + contractsPath = objects.property(String.class); + excludeBuildFolders = objects.property(Boolean.class); + deleteStubsAfterTest = objects.property(Boolean.class); + + copiedContractsFolder = objects.directoryProperty(); + backupContractsFolder = objects.directoryProperty(); + + this.getOutputs().upToDateWhen(task -> !(this.shouldDownloadContracts() && this.getContractDependency().toStubConfiguration().isVersionChanging())); + // Lambdas break build caching support + this.doFirst(new Action() { + @Override + public void execute(Task inner) { + String repositoryUrl = contractRepository.getRepositoryUrl().getOrNull(); + if (repositoryUrl != null && ScmStubDownloaderBuilder.isProtocolAccepted(repositoryUrl)) { + String branch = StubRunnerPropertyUtils.getProperty(contractsProperties.get(), "git.branch"); + branch = StringUtils.hasText(branch) ? branch : "master"; + UsernamePasswordCredentialsProvider provider = null; + if (StringUtils.hasText(contractRepository.getUsername().get())) { + provider = new UsernamePasswordCredentialsProvider(contractRepository.getUsername().get(), contractRepository.getPassword().get()); + } + try { + Collection refs = Git.lsRemoteRepository() + .setRemote(repositoryUrl) + .setCredentialsProvider(provider) + .call(); + for (Ref ref : refs) { + if (ref.getName().equals(branch) || ref.getName().equals("refs/heads/" + branch) || ref.getName().equals("refs/tags/" + branch)) { + contractsProperties.put("git.commit", ref.getObjectId().name()); + } + } + } catch (GitAPIException e) { + ContractsCopyTask.this.getLogger().warn("Unable to determine git repository commit id"); + } + } + } + }); + } + + @TaskAction + void sync() { + final File contractsDirectory; + final String antPattern; + if (shouldDownloadContracts()) { + DownloadedData downloadedData = downloadContracts(); + contractsDirectory = downloadedData.downloadedContracts; + antPattern = downloadedData.inclusionProperties.getIncludedRootFolderAntPattern() + "*.*"; + getLogger().info("Contracts got downloaded to [{}]", contractsDirectory); + } + else { + contractsDirectory = this.contractsDirectory.getAsFile().getOrNull(); + antPattern = "**/"; + } + getLogger().info("For project [{}] will use contracts provided in the folder [{}]", getProject().getName(), contractsDirectory); + final String contractsRepository = this.contractRepository.getRepositoryUrl().getOrElse(""); + throwExceptionWhenFailOnNoContracts(contractsDirectory, contractsRepository); + + final String slashSeparatedGroupId = getProject().getGroup().toString().replace(".", File.separator); + final String dotSeparatedAntPattern = antPattern.replace(slashSeparatedGroupId, getProject().getGroup().toString()); + File output = copiedContractsFolder.get().getAsFile(); + getLogger().info("Downloading and unpacking files from [{}] to [{}]. The inclusion ant patterns are [{}] and [{}]", contractsDirectory, output, antPattern, dotSeparatedAntPattern); + sync(contractsDirectory, antPattern, dotSeparatedAntPattern, excludeBuildFolders.get(), output); + if (convertToYaml.get()) { + convertContractsToYaml(contractsDirectory, antPattern, dotSeparatedAntPattern, output, excludeBuildFolders.get()); + } + } + + private void convertContractsToYaml(File file, String antPattern, String slashSeparatedAntPattern, File outputContractsFolder, boolean excludeBuildFolders) { + sync(file, antPattern, slashSeparatedAntPattern, excludeBuildFolders, backupContractsFolder.get().getAsFile()); + ToYamlConverter.replaceContractWithYaml(outputContractsFolder); + getLogger().info("Replaced DSL files with their YAML representation at [{}]", outputContractsFolder); + } + + private void sync(File file, String antPattern, String dotSeparatedAntPattern, boolean excludeBuildFolders, File outputContractsFolder) { + getProject().sync(spec -> { + spec.from(file); + // by default group id is slash separated... + spec.include(antPattern); + // ...we also want to allow dot separation + spec.include(dotSeparatedAntPattern); + if (excludeBuildFolders) { + spec.exclude("**/target/**", "**/build/**", "**/.mvn/**", "**/.gradle/**"); + } + spec.into(outputContractsFolder); + }); + } + + private DownloadedData downloadContracts() { + String groupId = getProject().getGroup().toString(); + String artifactId = getProject().getName(); + getLogger().info("Project has group id [{}], artifact id [{}]", groupId, artifactId); + getLogger().info("For project [{}] Download dependency is provided - will download contract jars", artifactId); + getLogger().info("Contract dependency [{}]", contractDependency); + StubConfiguration configuration = contractDependency.toStubConfiguration(); + getLogger().info("Got the following contract dependency to download [{}]", configuration); + getLogger().info("The contract dependency is a changing one [{}]", configuration.isVersionChanging()); + + final StubDownloader downloader = new StubDownloaderBuilderProvider().get(createStubRunnerOptions()); + final ContractDownloader contractDownloader = new ContractDownloader(downloader, configuration, + contractsPath.getOrNull(), groupId, artifactId, getProject().getVersion().toString()); + final File downloadedContracts = contractDownloader.unpackAndDownloadContracts(); + final ContractDownloader.InclusionProperties inclusionProperties = + contractDownloader.createNewInclusionProperties(downloadedContracts); + + // TODO: inclusionProperties.includedContracts is never used eventually. Review this: + return new DownloadedData(contractsSubDirIfPresent(downloadedContracts), inclusionProperties); + } + + private void throwExceptionWhenFailOnNoContracts(@Nullable File file, String contractsRepository) { + if (StringUtils.hasText(contractsRepository)) { + if (getLogger().isDebugEnabled()) { + getLogger().debug("Contracts repository is set, will not throw an exception that the contracts are not found"); + } + return; + } + if (failOnNoContracts.get() && (!file.exists() || file.listFiles().length == 0)) { + String path = file.getAbsolutePath(); + throw new GradleException("Contracts could not be found: [" + path + "]\nPlease make sure that the contracts were defined, or set the [failOnNoContracts] flag to [false]"); + } + } + + private File contractsSubDirIfPresent(File contractsDirectory) { + File contracts = new File(contractsDirectory, "contracts"); + if (contracts.exists()) { + if (getLogger().isDebugEnabled()) { + getLogger().debug("Contracts folder found [{}]", contracts); + } + contractsDirectory = contracts; + } + return contractsDirectory; + } + + private class DownloadedData { + final File downloadedContracts; + final ContractDownloader.InclusionProperties inclusionProperties; + + private DownloadedData(File downloadedContracts, ContractDownloader.InclusionProperties inclusionProperties) { + this.downloadedContracts = downloadedContracts; + this.inclusionProperties = inclusionProperties; + } + } + + @Input + Property getConvertToYaml() { + return convertToYaml; + } + + @Input + Property getFailOnNoContracts() { + return failOnNoContracts; + } + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + DirectoryProperty getContractsDirectory() { + return contractsDirectory; + } + + @Nested + Dependency getContractDependency() { + return contractDependency; + } + + static class Dependency { + private static final String LATEST_VERSION = "+"; + + private final Property groupId; + private final Property artifactId; + private final Property version; + private final Property classifier; + private final Property stringNotation; + + @Inject + public Dependency(ObjectFactory objects) { + groupId = objects.property(String.class); + artifactId = objects.property(String.class); + version = objects.property(String.class); + classifier = objects.property(String.class); + stringNotation = objects.property(String.class); + } + + @Input + @Optional + Property getGroupId() { + return groupId; + } + + @Input + @Optional + Property getArtifactId() { + return artifactId; + } + + @Input + @Optional + Property getVersion() { + return version; + } + + @Input + @Optional + Property getClassifier() { + return classifier; + } + + @Input + @Optional + Property getStringNotation() { + return stringNotation; + } + + @Internal + StubConfiguration toStubConfiguration() { + String stringNotation = this.stringNotation.getOrNull(); + if (StringUtils.hasText(stringNotation)) { + return new StubConfiguration(stringNotation); + } + + String groupId = this.groupId.getOrNull(); + String artifactId = this.artifactId.getOrNull(); + String version = StringUtils.hasText(this.version.getOrNull()) ? + this.version.getOrNull() : LATEST_VERSION; + String classifier = this.classifier.getOrNull(); + return new StubConfiguration(groupId, artifactId, version, classifier); + } + } + + @Nested + Repository getContractRepository() { + return contractRepository; + } + + static class Repository { + private final Property repositoryUrl; + private final Property username; + private final Property password; + private final Property proxyHost; + private final Property proxyPort; + + @Inject + public Repository(ObjectFactory objects) { + repositoryUrl = objects.property(String.class); + username = objects.property(String.class); + password = objects.property(String.class); + proxyHost = objects.property(String.class); + proxyPort = objects.property(Integer.class); + } + + @Input + @Optional + Property getRepositoryUrl() { + return repositoryUrl; + } + + @Input + @Optional + Property getUsername() { + return username; + } + + @Input + @Optional + Property getPassword() { + return password; + } + + @Input + @Optional + Property getProxyHost() { + return proxyHost; + } + + @Input + @Optional + Property getProxyPort() { + return proxyPort; + } + } + + @Input + @Optional + Property getContractsMode() { + return contractsMode; + } + + @Input + MapProperty getContractsProperties() { + return contractsProperties; + } + + @Input + @Optional + Property getContractsPath() { + return contractsPath; + } + + @Input + Property getExcludeBuildFolders() { + return excludeBuildFolders; + } + + @Input + Property getDeleteStubsAfterTest() { + return deleteStubsAfterTest; + } + + @OutputDirectory + DirectoryProperty getCopiedContractsFolder() { + return copiedContractsFolder; + } + + @Optional + @OutputDirectory + DirectoryProperty getBackupContractsFolder() { + return backupContractsFolder; + } + + private boolean shouldDownloadContracts() { + return StringUtils.hasText(contractDependency.getArtifactId().getOrNull()) || + StringUtils.hasText(contractDependency.getStringNotation().getOrNull()) || + StringUtils.hasText(contractRepository.getRepositoryUrl().getOrNull()); + } + + private StubRunnerOptions createStubRunnerOptions() { + StubRunnerOptionsBuilder options = new StubRunnerOptionsBuilder() + .withOptions(StubRunnerOptions.fromSystemProps()) + .withStubRepositoryRoot(contractRepository.repositoryUrl.getOrNull()) + .withStubsMode(contractsMode.get()) + .withUsername(contractRepository.username.getOrNull()) + .withPassword(contractRepository.password.getOrNull()) + .withDeleteStubsAfterTest(deleteStubsAfterTest.get()) + .withProperties(contractsProperties.getOrNull()) + .withFailOnNoStubs(failOnNoContracts.get()); + if (contractRepository.proxyPort.isPresent()) { + options = options.withProxy(contractRepository.proxyHost.getOrNull(), contractRepository.proxyPort.get()); + } + return options.build(); + } +} \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.java new file mode 100644 index 0000000000..d3a8a631cf --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateClientStubsFromDslTask.java @@ -0,0 +1,103 @@ +/* + * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin; + +import java.io.File; +import java.util.List; + +import javax.inject.Inject; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.Directory; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; +import org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverter; + +//TODO: Implement as an incremental task: https://gradle.org/docs/current/userguide/custom_tasks.html#incremental_tasks ? +/** + * Generates stubs from the contracts. + * + * @author Marcin Grzejszczak + * @author Anatoliy Balakirev + * @author Shannon Pamperl + * @since 2.0.0 + */ +@CacheableTask +class GenerateClientStubsFromDslTask extends DefaultTask { + + static final String TASK_NAME = "generateClientStubs"; + static final String DEFAULT_MAPPINGS_FOLDER = "mappings"; + + private Property contractsDslDir; + + private ListProperty excludedFiles; + + private Property excludeBuildFolders; + + private DirectoryProperty stubsOutputDir; + + @Inject + public GenerateClientStubsFromDslTask(ObjectFactory objects) { + contractsDslDir = objects.directoryProperty(); + excludedFiles = objects.listProperty(String.class); + excludeBuildFolders = objects.property(Boolean.class); + + stubsOutputDir = objects.directoryProperty(); + } + + @TaskAction + void generate() { + File output = stubsOutputDir.get().getAsFile(); + getLogger().info("Stubs output dir [{}]", output); + getLogger().info("Spring Cloud Contract Verifier Plugin: Invoking DSL to client stubs conversion"); + getLogger().info("Contracts dir is [{}] output stubs dir is [{}]", contractsDslDir.get().getAsFile(), output); + List excludedFiles = this.excludedFiles.get(); + RecursiveFilesConverter converter = new RecursiveFilesConverter(output, + contractsDslDir.get().getAsFile(), excludedFiles, ".*", excludeBuildFolders.get()); + converter.processFiles(); + } + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + public Property getContractsDslDir() { + return contractsDslDir; + } + + @Input + public ListProperty getExcludedFiles() { + return excludedFiles; + } + + @Input + public Property getExcludeBuildFolders() { + return excludeBuildFolders; + } + + @OutputDirectory + public Property getStubsOutputDir() { + return stubsOutputDir; + } +} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.java new file mode 100644 index 0000000000..adc6cb44d6 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.java @@ -0,0 +1,236 @@ +/* + * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin; + +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; +import org.springframework.cloud.contract.spec.ContractVerifierException; +import org.springframework.cloud.contract.verifier.TestGenerator; +import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties; +import org.springframework.cloud.contract.verifier.config.TestFramework; +import org.springframework.cloud.contract.verifier.config.TestMode; + +import javax.inject.Inject; +import java.io.File; +import java.util.List; + +/** + * Task used to generate server side tests + * + * @author Marcin Grzejszczak + * @author Anatoliy Balakirev + * @author Shannon Pamperl + * @since 1.0.0 + */ +@CacheableTask +class GenerateServerTestsTask extends DefaultTask { + static final String TASK_NAME = "generateContractTests"; + + private final DirectoryProperty contractsDslDir; + private final Property nameSuffixForTests; + private final Property basePackageForTests; + private final Property baseClassForTests; + private final Property packageWithBaseClasses; + private final ListProperty excludedFiles; + private final ListProperty ignoredFiles; + private final ListProperty includedFiles; + private final ListProperty imports; + private final ListProperty staticImports; + private final Property testMode; + private final Property testFramework; + private final MapProperty baseClassMappings; + private final Property assertJsonSize; + private final Property failOnInProgress; + private final DirectoryProperty generatedTestSourcesDir; + private final DirectoryProperty generatedTestResourcesDir; + + @Inject + public GenerateServerTestsTask(ObjectFactory objects) { + this.contractsDslDir = objects.directoryProperty(); + this.nameSuffixForTests = objects.property(String.class); + this.basePackageForTests = objects.property(String.class); + this.baseClassForTests = objects.property(String.class); + this.packageWithBaseClasses = objects.property(String.class); + this.excludedFiles = objects.listProperty(String.class); + this.ignoredFiles = objects.listProperty(String.class); + this.includedFiles = objects.listProperty(String.class); + this.imports = objects.listProperty(String.class); + this.staticImports = objects.listProperty(String.class); + this.testMode = objects.property(TestMode.class); + this.testFramework = objects.property(TestFramework.class); + this.baseClassMappings = objects.mapProperty(String.class, String.class); + this.assertJsonSize = objects.property(Boolean.class); + this.failOnInProgress = objects.property(Boolean.class); + this.generatedTestSourcesDir = objects.directoryProperty(); + this.generatedTestResourcesDir = objects.directoryProperty(); + } + + @TaskAction + void generate() { + File generatedTestSources = this.generatedTestSourcesDir.get().getAsFile(); + File generatedTestResources = this.generatedTestResourcesDir.get().getAsFile(); + getLogger().info("Generated test sources dir [{}]", generatedTestSources); + getLogger().info("Generated test resources dir [{}]", generatedTestResources); + File contractsDslDir = this.contractsDslDir.get().getAsFile(); + String includedContracts = ".*"; + getLogger().info("Spring Cloud Contract Verifier Plugin: Invoking test sources generation"); + getLogger().info("Contracts are unpacked to [{}]", contractsDslDir); + getLogger().info("Included contracts are [{}]", includedContracts); + try { + TestGenerator generator = new TestGenerator(toConfigProperties(contractsDslDir, includedContracts, generatedTestSources, generatedTestResources)); + int generatedClasses = generator.generate(); + getLogger().info("Generated {} test classes", generatedClasses); + } + catch (ContractVerifierException e) { + throw new GradleException("Spring Cloud Contract Verifier Plugin exception: " + e.getMessage(), e); + } + } + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + DirectoryProperty getContractsDslDir() { + return contractsDslDir; + } + + @Input + @Optional + Property getNameSuffixForTests() { + return nameSuffixForTests; + } + + @Input + @Optional + Property getBasePackageForTests() { + return basePackageForTests; + } + + @Input + @Optional + Property getBaseClassForTests() { + return baseClassForTests; + } + + @Input + @Optional + Property getPackageWithBaseClasses() { + return packageWithBaseClasses; + } + + @Input + ListProperty getExcludedFiles() { + return excludedFiles; + } + + @Input + ListProperty getIgnoredFiles() { + return ignoredFiles; + } + + @Input + ListProperty getIncludedFiles() { + return includedFiles; + } + + @Input + ListProperty getImports() { + return imports; + } + + @Input + ListProperty getStaticImports() { + return staticImports; + } + + @Input + Property getTestMode() { + return testMode; + } + + @Input + Property getTestFramework() { + return testFramework; + } + + @Input + MapProperty getBaseClassMappings() { + return baseClassMappings; + } + + @Input + Property getAssertJsonSize() { + return assertJsonSize; + } + + @Input + Property getFailOnInProgress() { + return failOnInProgress; + } + + @OutputDirectory + DirectoryProperty getGeneratedTestSourcesDir() { + return generatedTestSourcesDir; + } + + @OutputDirectory + DirectoryProperty getGeneratedTestResourcesDir() { + return generatedTestResourcesDir; + } + + private ContractVerifierConfigProperties toConfigProperties(File contractsDslDir, + String includedContracts, File generatedTestSources, + File generatedTestResources) { + List excludedFiles = this.excludedFiles.get(); + List ignoredFiles = this.ignoredFiles.get(); + List includedFiles = this.includedFiles.get(); + String[] imports = this.imports.get().toArray(new String[0]); + String[] staticImports = this.staticImports.get().toArray(new String[0]); + + ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(); + properties.setIncludedContracts(includedContracts); + properties.setContractsDslDir(contractsDslDir); + properties.setNameSuffixForTests(nameSuffixForTests.getOrNull()); + properties.setGeneratedTestSourcesDir(generatedTestSources); + properties.setGeneratedTestResourcesDir(generatedTestResources); + properties.setBasePackageForTests(basePackageForTests.getOrNull()); + properties.setBaseClassForTests(baseClassForTests.getOrNull()); + properties.setPackageWithBaseClasses(packageWithBaseClasses.getOrNull()); + properties.setExcludedFiles(excludedFiles); + properties.setIgnoredFiles(ignoredFiles); + properties.setIncludedFiles(includedFiles); + properties.setImports(imports); + properties.setStaticImports(staticImports); + properties.setTestMode(testMode.get()); + properties.setTestFramework(testFramework.get()); + properties.setBaseClassMappings(baseClassMappings.get()); + properties.setAssertJsonSize(assertJsonSize.get()); + properties.setFailOnInProgress(failOnInProgress.get()); + return properties; + } +} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.java new file mode 100644 index 0000000000..8b7c32896f --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/PublishStubsToScmTask.java @@ -0,0 +1,196 @@ +/* + * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin; + +import javax.inject.Inject; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; +import org.springframework.cloud.contract.stubrunner.ContractProjectUpdater; +import org.springframework.cloud.contract.stubrunner.ScmStubDownloaderBuilder; +import org.springframework.cloud.contract.stubrunner.StubRunnerOptions; +import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder; +import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; +import org.springframework.util.StringUtils; + +/** + * For SCM based repositories will copy the generated stubs + * to the cloned repo with contracts and stubs. Will also + * commit the changes and push them to origin. + * + * NOTE: starting with 2.3.0.RELEASE the customize{} closure previously used for + * {@link PublishStubsToScmTask} customisation is no longer available. The settings should be applied directly + * within the publishStubsToScm closure as in the example above. + * + * @author Marcin Grzejszczak + * @author Anatoliy Balakirev + * @author Shannon Pamperl + * @since 2.0.0 + */ +class PublishStubsToScmTask extends DefaultTask { + + static final String TASK_NAME = "publishStubsToScm"; + + private final Repository contractRepository; + + private final Property contractsMode; + + /** + * @see ContractVerifierExtension#deleteStubsAfterTest + * + * This property will delete the Git repository where the input + * stubs to this task have been committed. + */ + private final Property deleteStubsAfterTest; + + private final Property failOnNoContracts; + + private final MapProperty contractsProperties; + + private final DirectoryProperty stubsDir; + + @Inject + public PublishStubsToScmTask(ObjectFactory objects) { + this.contractRepository = objects.newInstance(Repository.class); + this.contractsMode = objects.property(StubRunnerProperties.StubsMode.class); + this.deleteStubsAfterTest = objects.property(Boolean.class); + this.failOnNoContracts = objects.property(Boolean.class); + this.contractsProperties = objects.mapProperty(String.class, String.class); + this.stubsDir = objects.directoryProperty(); + + this.onlyIf(task -> { + String contractRepoUrl = contractRepository.repositoryUrl.getOrElse(""); + if (StringUtils.isEmpty(contractRepoUrl) || !ScmStubDownloaderBuilder.isProtocolAccepted(contractRepoUrl)) { + getLogger().warn("Skipping pushing stubs to scm since your contracts repository URL [{}] doesn't match any of the accepted protocols for SCM stub downloader", contractRepoUrl); + return false; + } + return true; + }); + } + + @TaskAction + void publishStubsToScm() { + String projectName = getProject().getGroup().toString() + ":" + getProject().getName() + ":" + getProject().getVersion().toString(); + getLogger().info("Pushing Stubs to SCM for project [{}]", projectName); + StubRunnerOptions stubRunnerOptions = createStubRunnerOptions(); + new ContractProjectUpdater(stubRunnerOptions).updateContractProject(projectName, stubsDir.get().getAsFile().toPath()); + } + + @Nested + Repository getContractRepository() { + return contractRepository; + } + + static class Repository { + private final Property repositoryUrl; + private final Property username; + private final Property password; + private final Property proxyPort; + private final Property proxyHost; + + @Inject + public Repository(ObjectFactory objects) { + this.repositoryUrl = objects.property(String.class); + this.username = objects.property(String.class); + this.password = objects.property(String.class); + this.proxyHost = objects.property(String.class); + this.proxyPort = objects.property(Integer.class); + } + + @Input + @Optional + Property getRepositoryUrl() { + return repositoryUrl; + } + + @Input + @Optional + Property getUsername() { + return username; + } + + @Input + @Optional + Property getPassword() { + return password; + } + + @Input + @Optional + Property getProxyPort() { + return proxyPort; + } + + @Input + @Optional + Property getProxyHost() { + return proxyHost; + } + } + + @Input + Property getContractsMode() { + return contractsMode; + } + + @Input + Property getDeleteStubsAfterTest() { + return deleteStubsAfterTest; + } + + @Input + Property getFailOnNoContracts() { + return failOnNoContracts; + } + + @Input + MapProperty getContractsProperties() { + return contractsProperties; + } + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + DirectoryProperty getStubsDir() { + return stubsDir; + } + + private StubRunnerOptions createStubRunnerOptions() { + StubRunnerOptionsBuilder options = new StubRunnerOptionsBuilder() + .withOptions(StubRunnerOptions.fromSystemProps()) + .withStubRepositoryRoot(contractRepository.repositoryUrl.getOrNull()) + .withStubsMode(contractsMode.get()) + .withUsername(contractRepository.username.getOrNull()) + .withPassword(contractRepository.password.getOrNull()) + .withDeleteStubsAfterTest(deleteStubsAfterTest.get()) + .withProperties(contractsProperties.getOrNull()) + .withFailOnNoStubs(failOnNoContracts.get()); + if (contractRepository.proxyPort.isPresent()) { + options = options.withProxy(contractRepository.proxyHost.getOrNull(), contractRepository.proxyPort.get()); + } + return options.build(); + } +} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.java new file mode 100644 index 0000000000..06834bb74f --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.java @@ -0,0 +1,373 @@ +/* + * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin; + +import java.io.File; + +import javax.annotation.Nullable; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.file.Directory; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.FileCollection; +import org.gradle.api.internal.HasConvention; +import org.gradle.api.plugins.GroovyPlugin; +import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; +import org.gradle.api.tasks.GroovySourceSet; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.SourceSetOutput; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.testing.Test; +import org.springframework.cloud.contract.verifier.config.TestFramework; + +/** + * Gradle plugin for Spring Cloud Contract Verifier that from the DSL contract can + *
    + *
  • generate tests
  • + *
  • generate stubs
  • + *
+ * + * @author Jakub Kubrynski, codearte.io + * @author Marcin Grzejszczak + * @author Anatoliy Balakirev + * @author Shannon Pamperl + * + * @since 1.0.0 + */ +public class SpringCloudContractVerifierGradlePlugin implements Plugin { + + private static final String GROUP_NAME = "Verification"; + private static final String EXTENSION_NAME = "contracts"; + private static final String CONTRACT_TEST_SOURCE_SET_NAME = "contractTest"; + private static final String CONTRACT_TEST_COMPILE_ONLY_CONFIGURATION_NAME = "contractTestCompileOnly"; + private static final String CONTRACT_TEST_IMPLEMENTATION_CONFIGURATION_NAME = "contractTestImplementation"; + private static final String CONTRACT_TEST_RUNTIME_ONLY_CONFIGURATION_NAME = "contractTestRuntimeOnly"; + private static final String VERIFIER_STUBS_JAR_TASK_NAME = "verifierStubsJar"; + private static final String CONTRACT_TEST_TASK_NAME = "contractTest"; + + private Project project; + + @Override + public void apply(Project project) { + this.project = project; + project.getPlugins().apply(JavaPlugin.class); + ContractVerifierExtension extension = project.getExtensions().create(EXTENSION_NAME, ContractVerifierExtension.class); + + JavaPluginConvention javaConvention = project.getConvention().getPlugin(JavaPluginConvention.class); + SourceSet contractTestSourceSet = configureSourceSets(extension, javaConvention); + configureConfigurations(); + registerContractTestTask(contractTestSourceSet); + + TaskProvider copyContracts = createAndConfigureCopyContractsTask(extension); + TaskProvider generateClientStubs = createAndConfigureGenerateClientStubs(extension, copyContracts); + + createAndConfigureStubsJarTasks(extension, generateClientStubs); + createGenerateTestsTask(extension, contractTestSourceSet, copyContracts); + createAndConfigurePublishStubsToScmTask(extension, generateClientStubs); + + project.afterEvaluate(inner -> { + DirectoryProperty generatedTestSourcesDir = extension.getGeneratedTestSourcesDir(); + if (generatedTestSourcesDir.isPresent()) { + if (extension.getTestFramework().get() == TestFramework.SPOCK) { + project.getPlugins().withType(GroovyPlugin.class, groovyPlugin -> { + GroovySourceSet groovy = ((HasConvention) contractTestSourceSet).getConvention().getPlugin(GroovySourceSet.class); + groovy.getGroovy().srcDirs(generatedTestSourcesDir); + }); + } else { + contractTestSourceSet.getJava().srcDirs(generatedTestSourcesDir); + } + } + }); + } + + private SourceSet configureSourceSets(ContractVerifierExtension extension, JavaPluginConvention javaConvention) { + SourceSetContainer sourceSets = javaConvention.getSourceSets(); + SourceSet contractTest = sourceSets.create(CONTRACT_TEST_SOURCE_SET_NAME); + contractTest.getJava().srcDirs(extension.getGeneratedTestJavaSourcesDir()); + project.getPlugins().withType(GroovyPlugin.class, groovyPlugin -> { + GroovySourceSet groovy = ((HasConvention) contractTest).getConvention().getPlugin(GroovySourceSet.class); + groovy.getGroovy().srcDirs(extension.getGeneratedTestGroovySourcesDir()); + }); + contractTest.getResources().srcDirs(extension.getGeneratedTestResourcesDir()); + + SourceSetOutput mainOutput = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput(); + SourceSetOutput testOutput = sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME).getOutput(); + + FileCollection compileClasspath = contractTest.getCompileClasspath(); + contractTest.setCompileClasspath(compileClasspath.plus(mainOutput).plus(testOutput)); + + FileCollection runtimeClasspath = contractTest.getRuntimeClasspath(); + contractTest.setRuntimeClasspath(runtimeClasspath.plus(mainOutput).plus(testOutput)); + return contractTest; + } + + private void configureConfigurations() { + ConfigurationContainer configurations = project.getConfigurations(); + + Configuration testCompileOnly = configurations.getByName(JavaPlugin.TEST_COMPILE_ONLY_CONFIGURATION_NAME); + Configuration testImplementation = configurations.getByName(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME); + Configuration testRuntimeOnly = configurations.getByName(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME); + Configuration contractTestCompileOnly = configurations.getByName(CONTRACT_TEST_COMPILE_ONLY_CONFIGURATION_NAME); + Configuration contractTestImplementation = configurations.getByName(CONTRACT_TEST_IMPLEMENTATION_CONFIGURATION_NAME); + Configuration contractTestRuntimeOnly = configurations.getByName(CONTRACT_TEST_RUNTIME_ONLY_CONFIGURATION_NAME); + + contractTestCompileOnly.extendsFrom(testCompileOnly); + contractTestImplementation.extendsFrom(testImplementation); + contractTestRuntimeOnly.extendsFrom(testRuntimeOnly); + } + + private void registerContractTestTask(SourceSet contractTestSourceSet) { + TaskProvider contractTestTask = project.getTasks().register(CONTRACT_TEST_TASK_NAME, Test.class, contractTest -> { + contractTest.setDescription("Runs the contract tests"); + contractTest.setGroup(GROUP_NAME); + contractTest.setTestClassesDirs(contractTestSourceSet.getOutput().getClassesDirs()); + contractTest.setClasspath(contractTestSourceSet.getRuntimeClasspath()); + + contractTest.mustRunAfter(project.getTasks().named(JavaPlugin.TEST_TASK_NAME)); + }); + + project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, check -> check.dependsOn(contractTestTask)); + } + + private void createGenerateTestsTask(ContractVerifierExtension extension, SourceSet contractTestSourceSet, TaskProvider copyContracts) { + TaskProvider task = project.getTasks().register(GenerateServerTestsTask.TASK_NAME, GenerateServerTestsTask.class); + task.configure(generateServerTestsTask -> { + generateServerTestsTask.setDescription("Generate server tests from the contracts"); + generateServerTestsTask.setGroup(GROUP_NAME); + + generateServerTestsTask.getContractsDslDir().convention(copyContracts.flatMap(ContractsCopyTask::getCopiedContractsFolder)); + generateServerTestsTask.getNameSuffixForTests().convention(extension.getNameSuffixForTests()); + generateServerTestsTask.getBasePackageForTests().convention(extension.getBasePackageForTests()); + generateServerTestsTask.getBaseClassForTests().convention(extension.getBaseClassForTests()); + generateServerTestsTask.getPackageWithBaseClasses().convention(extension.getPackageWithBaseClasses()); + generateServerTestsTask.getExcludedFiles().convention(extension.getExcludedFiles()); + generateServerTestsTask.getIgnoredFiles().convention(extension.getIgnoredFiles()); + generateServerTestsTask.getIncludedFiles().convention(extension.getIncludedFiles()); + generateServerTestsTask.getImports().convention(extension.getImports()); + generateServerTestsTask.getStaticImports().convention(extension.getStaticImports()); + generateServerTestsTask.getTestMode().convention(extension.getTestMode()); + generateServerTestsTask.getTestFramework().convention(extension.getTestFramework()); + generateServerTestsTask.getBaseClassMappings().convention(extension.getBaseClassMappings().getBaseClassMappings()); + generateServerTestsTask.getAssertJsonSize().convention(extension.getAssertJsonSize()); + generateServerTestsTask.getFailOnInProgress().convention(extension.getFailOnInProgress()); + generateServerTestsTask.getGeneratedTestSourcesDir().convention(extension.getTestFramework().flatMap(testFramework -> { + Property correctSourceSetDir; + if (testFramework == TestFramework.SPOCK) { + correctSourceSetDir = extension.getGeneratedTestGroovySourcesDir(); + } else { + correctSourceSetDir = extension.getGeneratedTestJavaSourcesDir(); + } + return extension.getGeneratedTestSourcesDir().orElse(correctSourceSetDir); + })); + generateServerTestsTask.getGeneratedTestResourcesDir().convention(extension.getGeneratedTestResourcesDir()); + + generateServerTestsTask.dependsOn(copyContracts); + }); + project.getTasks().named(contractTestSourceSet.getCompileJavaTaskName(), compileContractTestJava -> { + compileContractTestJava.dependsOn(task); + }); + project.getPlugins().withType(GroovyPlugin.class, groovyPlugin -> { + project.getTasks().named(contractTestSourceSet.getCompileTaskName("groovy"), compileContractTestGroovy -> { + compileContractTestGroovy.dependsOn(task); + }); + }); + } + + private void createAndConfigurePublishStubsToScmTask(ContractVerifierExtension extension, + TaskProvider generateClientStubs) { + TaskProvider task = project.getTasks().register(PublishStubsToScmTask.TASK_NAME, PublishStubsToScmTask.class); + task.configure(publishStubsToScmTask -> { + publishStubsToScmTask.setDescription("The generated stubs get committed to the SCM repo and pushed to origin"); + publishStubsToScmTask.setGroup(GROUP_NAME); + + ContractVerifierExtension.ContractRepository stubs = extension.getPublishStubsToScm().getContractRepository(); + ContractVerifierExtension.ContractRepository original = extension.getContractRepository(); + + publishStubsToScmTask.getContractRepository().getRepositoryUrl().convention(stubs.getRepositoryUrl().orElse(original.getRepositoryUrl())); + publishStubsToScmTask.getContractRepository().getUsername().convention(stubs.getUsername().orElse(original.getUsername())); + publishStubsToScmTask.getContractRepository().getPassword().convention(stubs.getPassword().orElse(original.getPassword())); + publishStubsToScmTask.getContractRepository().getProxyHost().convention(stubs.getProxyHost().orElse(original.getProxyHost())); + publishStubsToScmTask.getContractRepository().getProxyPort().convention(stubs.getProxyPort().orElse(original.getProxyPort())); + publishStubsToScmTask.getContractsMode().convention(extension.getContractsMode()); + publishStubsToScmTask.getDeleteStubsAfterTest().convention(extension.getDeleteStubsAfterTest()); + publishStubsToScmTask.getFailOnNoContracts().convention(extension.getFailOnNoContracts()); + publishStubsToScmTask.getContractsProperties().convention(extension.getContractsProperties()); + publishStubsToScmTask.getStubsDir().convention(generateClientStubs.flatMap(GenerateClientStubsFromDslTask::getStubsOutputDir)); + + publishStubsToScmTask.dependsOn(generateClientStubs); + }); + } + + private TaskProvider createAndConfigureGenerateClientStubs(ContractVerifierExtension extension, + TaskProvider copyContracts) { + TaskProvider task = project.getTasks().register(GenerateClientStubsFromDslTask.TASK_NAME, GenerateClientStubsFromDslTask.class, generateClientStubs -> { + generateClientStubs.setGroup(GROUP_NAME); + generateClientStubs.setDescription("Generate client stubs from the contracts"); + + generateClientStubs.getContractsDslDir().convention(copyContracts.flatMap(ContractsCopyTask::getCopiedContractsFolder)); + generateClientStubs.getExcludedFiles().convention(extension.getExcludedFiles()); + generateClientStubs.getExcludeBuildFolders().convention(extension.getExcludeBuildFolders()); + + generateClientStubs.getStubsOutputDir().convention(extension.getStubsOutputDir().dir(buildRootPath(GenerateClientStubsFromDslTask.DEFAULT_MAPPINGS_FOLDER))); + + generateClientStubs.dependsOn(copyContracts); + }); + return task; + } + + private void createAndConfigureStubsJarTasks(ContractVerifierExtension extension, + TaskProvider generateClientStubs) { + TaskProvider verifierStubsJar = project.getTasks().register(VERIFIER_STUBS_JAR_TASK_NAME, Jar.class); + verifierStubsJar.configure(stubsJar -> { + stubsJar.setDescription("Creates the stubs JAR task"); + stubsJar.setGroup(GROUP_NAME); + stubsJar.getArchiveBaseName().convention(project.provider(project::getName)); + stubsJar.getArchiveClassifier().convention(extension.getStubsSuffix()); + stubsJar.from(extension.getStubsOutputDir()); + + stubsJar.dependsOn(generateClientStubs); + }); + project.artifacts(artifactHandler -> artifactHandler.add("archives", verifierStubsJar)); + createAndConfigureMavenPublishPlugin(verifierStubsJar, extension); + } + + @Deprecated + private void createAndConfigureMavenPublishPlugin(TaskProvider stubsTask, ContractVerifierExtension extension) { + if (!classIsOnClasspath("org.gradle.api.publish.maven.plugins.MavenPublishPlugin")) { + project.getLogger().debug("Maven Publish Plugin is not present - won't add default publication"); + return; + } + // This must be called within afterEvaluate due to getting data from extension, which must be initialised first: + project.afterEvaluate(inner -> { + project.getLogger().debug("Spring Cloud Contract Verifier Plugin: Generating default publication"); + if (extension.getDisableStubPublication().get()) { + project.getLogger().info("You've switched off the stub publication - won't add default publication"); + return; + } + project.getPlugins().withType(MavenPublishPlugin.class, publishingPlugin -> { + PublishingExtension publishingExtension = project.getExtensions().findByType(PublishingExtension.class); + if (hasStubsPublication(publishingExtension)) { + project.getLogger().info("Spring Cloud Contract Verifier Plugin: Stubs publication was present - won't create a new one. Remember about passing stubs as artifact"); + } + else { + project.getLogger().debug("Spring Cloud Contract Verifier Plugin: Stubs publication is not present - will create one"); + setPublications(publishingExtension, stubsTask); + } + }); + }); + } + + @Deprecated + private void setPublications(PublishingExtension publishingExtension, TaskProvider stubsTask) { + project.getLogger().warn("Spring Cloud Contract Verifier Plugin: Creating stubs publication is deprecated"); + publishingExtension.publications(publicationsContainer -> { + publicationsContainer.create("stubs", MavenPublication.class, stubsPublication -> { + stubsPublication.setArtifactId(project.getName()); + stubsPublication.artifact(stubsTask.get()); + }); + }); + } + + private @Nullable TaskProvider stubsTask() { + try { + return project.getTasks().named(VERIFIER_STUBS_JAR_TASK_NAME, Jar.class); + } + catch (Exception e) { + return null; + } + } + + @Deprecated + private boolean hasStubsPublication(PublishingExtension publishingExtension) { + try { + return publishingExtension.getPublications().getByName("stubs") != null; + } + catch (Exception e) { + return false; + } + } + + private TaskProvider createAndConfigureCopyContractsTask(ContractVerifierExtension extension) { + TaskProvider task = project.getTasks().register(ContractsCopyTask.TASK_NAME, ContractsCopyTask.class, contractsCopyTask -> { + contractsCopyTask.setGroup(GROUP_NAME); + contractsCopyTask.setDescription("Copies contracts to the output folder"); + + contractsCopyTask.getConvertToYaml().convention(extension.getConvertToYaml()); + contractsCopyTask.getFailOnNoContracts().convention(extension.getFailOnNoContracts()); + contractsCopyTask.getContractsDirectory().convention(extension.getContractsDslDir()); + contractsCopyTask.getContractDependency().getGroupId().convention(extension.getContractDependency().getGroupId()); + contractsCopyTask.getContractDependency().getArtifactId().convention(extension.getContractDependency().getArtifactId()); + contractsCopyTask.getContractDependency().getVersion().convention(extension.getContractDependency().getVersion()); + contractsCopyTask.getContractDependency().getClassifier().convention(extension.getContractDependency().getClassifier()); + contractsCopyTask.getContractDependency().getStringNotation().convention(extension.getContractDependency().getStringNotation()); + contractsCopyTask.getContractRepository().getRepositoryUrl().convention(extension.getContractRepository().getRepositoryUrl()); + contractsCopyTask.getContractRepository().getUsername().convention(extension.getContractRepository().getUsername()); + contractsCopyTask.getContractRepository().getPassword().convention(extension.getContractRepository().getPassword()); + contractsCopyTask.getContractRepository().getProxyHost().convention(extension.getContractRepository().getProxyHost()); + contractsCopyTask.getContractRepository().getProxyPort().convention(extension.getContractRepository().getProxyPort()); + contractsCopyTask.getContractsMode().convention(extension.getContractsMode()); + contractsCopyTask.getContractsProperties().convention(extension.getContractsProperties()); + contractsCopyTask.getContractsPath().convention(extension.getContractsPath()); + contractsCopyTask.getExcludeBuildFolders().convention(extension.getExcludeBuildFolders()); + contractsCopyTask.getDeleteStubsAfterTest().convention(extension.getDeleteStubsAfterTest()); + + contractsCopyTask.getCopiedContractsFolder().convention(extension.getStubsOutputDir().dir(buildRootPath(ContractsCopyTask.CONTRACTS))); + contractsCopyTask.getBackupContractsFolder().convention(extension.getStubsOutputDir().dir(buildRootPath(ContractsCopyTask.BACKUP))); + }); + return task; + } + + @Deprecated + private boolean classIsOnClasspath(String className) { + try { + Class.forName(className); + return true; + } + catch (Exception e) { + project.getLogger().debug("Maven Publish Plugin is not available"); + } + return false; + } + + private Provider buildRootPath(String path) { + return project.provider(() -> { + StringBuilder builder = new StringBuilder(); + builder.append("META-INF") + .append(File.separator) + .append(project.getGroup()) + .append(File.separator) + .append(project.getName()) + .append(File.separator) + .append(project.getVersion()) + .append(File.separator) + .append(path); + return builder.toString(); + }); + } +} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/package-info.java b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/package-info.java new file mode 100644 index 0000000000..dc403cbcb6 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/java/org/springframework/cloud/contract/verifier/plugin/package-info.java @@ -0,0 +1,4 @@ +@NonNullApi +package org.springframework.cloud.contract.verifier.plugin; + +import org.springframework.lang.NonNullApi; \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierIntegrationSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierIntegrationSpec.groovy index 7f4ae51029..b1d7ca2633 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierIntegrationSpec.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierIntegrationSpec.groovy @@ -145,10 +145,16 @@ abstract class ContractVerifierIntegrationSpec extends Specification { rootFile.eachFileRecurse { File file -> try { if (file.isFile() && file.name.endsWith('jar')) { - new ZipFile(file).entries().each { - if (it.name.endsWith('.groovy')) { - containsGroovyFiles = true + ZipFile zipFile; + try { + zipFile = new ZipFile(file) + zipFile.entries().each { + if (it.name.endsWith('.groovy')) { + containsGroovyFiles = true + } } + } finally { + zipFile?.close() } } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy index ac5e8a8c6f..e3c66b0744 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy @@ -17,12 +17,18 @@ package org.springframework.cloud.contract.verifier.plugin +import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.Directory import org.gradle.api.internal.project.DefaultProject import org.gradle.api.plugins.GroovyPlugin +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.publish.PublicationContainer import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.plugins.MavenPublishPlugin +import org.gradle.api.tasks.SourceSet import org.gradle.testfixtures.ProjectBuilder +import org.springframework.cloud.contract.verifier.config.TestFramework import spock.lang.Specification class ContractVerifierSpec extends Specification { @@ -36,9 +42,9 @@ class ContractVerifierSpec extends Specification { project.plugins.apply(SpringCloudContractVerifierGradlePlugin) } - def "should apply groovy plugin"() { + def "should apply java plugin"() { expect: - project.plugins.hasPlugin(GroovyPlugin) + project.plugins.hasPlugin(JavaPlugin) } def "should create contracts extension"() { @@ -46,24 +52,106 @@ class ContractVerifierSpec extends Specification { project.extensions.findByType(ContractVerifierExtension) != null } - def "should create generateContractTests task"() { + def "should create a test sourceset with java sources"() { + given: + ContractVerifierExtension extension = project.extensions.getByType(ContractVerifierExtension) + Directory projectDir = project.layout.projectDirectory + SourceSet contractTest = project.convention.getPlugin(JavaPluginConvention).getSourceSets().getByName("contractTest") + expect: - project.tasks.named("generateContractTests") != null + contractTest != null + contractTest.java.srcDirs.contains(projectDir.dir("src/contractTest/java").asFile) + contractTest.java.srcDirs.contains(extension.generatedTestJavaSourcesDir.get().asFile) + contractTest.resources.srcDirs.contains(projectDir.dir("src/contractTest/resources").asFile) + contractTest.resources.srcDirs.contains(extension.generatedTestResourcesDir.get().asFile) } - def "should configure generateContractTests task as a dependency of the check task"() { + def "should create a test sourceset with groovy sources, if the groovy plugin is present"() { + given: + project.plugins.apply(GroovyPlugin) + ContractVerifierExtension extension = project.extensions.getByType(ContractVerifierExtension) + Directory projectDir = project.layout.projectDirectory + SourceSet contractTest = project.convention.getPlugin(JavaPluginConvention).getSourceSets().getByName("contractTest") + expect: - project.tasks.check.getDependsOn().contains(project.tasks.named("generateContractTests")) + contractTest != null + contractTest.java.srcDirs.contains(projectDir.dir("src/contractTest/java").asFile) + contractTest.java.srcDirs.contains(extension.generatedTestJavaSourcesDir.get().asFile) + contractTest.groovy.srcDirs.contains(projectDir.dir('src/contractTest/groovy').asFile) + contractTest.groovy.srcDirs.contains(extension.generatedTestGroovySourcesDir.get().asFile) + contractTest.resources.srcDirs.contains(projectDir.dir("src/contractTest/resources").asFile) + contractTest.resources.srcDirs.contains(extension.generatedTestResourcesDir.get().asFile) + } + + def "should setup dependency configurations"() { + given: + Configuration contractTestCompileOnly = project.configurations.contractTestCompileOnly + Configuration contractTestImplementation = project.configurations.contractTestImplementation + Configuration contractTestRuntimeOnly = project.configurations.contractTestRuntimeOnly + + expect: + contractTestCompileOnly != null + contractTestCompileOnly.extendsFrom.contains(project.configurations.testCompileOnly) + contractTestImplementation != null + contractTestImplementation.extendsFrom.contains(project.configurations.testImplementation) + contractTestRuntimeOnly != null + contractTestRuntimeOnly.extendsFrom.contains(project.configurations.testRuntimeOnly) + } + + def "should create contract test task"() { + expect: + project.tasks.named("contractTest").get() != null + } + + def "should create generateContractTests task"() { + expect: + project.tasks.named("generateContractTests").get() != null + } + + def "should configure generateContractTests task as a dependency of the compileContractTestJava task"() { + expect: + project.tasks.compileContractTestJava.getDependsOn().contains(project.tasks.named("generateContractTests")) + project.tasks.findByName("compileContractTestGroovy") == null + } + + def "should configure generateContractTests task as a dependency of the compileContractTestGroovy task"() { + given: + project.plugins.apply(GroovyPlugin) + + expect: + project.tasks.compileContractTestJava.getDependsOn().contains(project.tasks.named("generateContractTests")) + project.tasks.compileContractTestGroovy.getDependsOn().contains(project.tasks.named("generateContractTests")) + } + + def "should configure generatedTestSourcesDir with the appropriate directories"() { + when: + ContractVerifierExtension extension = project.extensions.findByType(ContractVerifierExtension) + GenerateServerTestsTask generateServerTestsTask = project.tasks.getByName("generateContractTests") as GenerateServerTestsTask + + then: + generateServerTestsTask.generatedTestSourcesDir.get().asFile == extension.generatedTestJavaSourcesDir.get().asFile + + and: + extension.testFramework.set(TestFramework.SPOCK) + + then: + generateServerTestsTask.generatedTestSourcesDir.get().asFile == extension.generatedTestGroovySourcesDir.get().asFile + + and: + extension.generatedTestSourcesDir.set(project.file("src/random")) + + then: + generateServerTestsTask.generatedTestSourcesDir.get().asFile == extension.generatedTestSourcesDir.get().asFile } def "should create generateClientStubs task"() { expect: - project.tasks.named("generateClientStubs") != null + project.tasks.named("generateClientStubs").get() != null } def "should create verifierStubsJar task"() { expect: - project.tasks.named("verifierStubsJar") != null + project.tasks.named("verifierStubsJar").get() != null } def "should configure generateClientStubs task as a dependency of the verifierStubsJar task"() { @@ -78,12 +166,12 @@ class ContractVerifierSpec extends Specification { def "should create copyContracts task"() { expect: - project.tasks.named("copyContracts") != null + project.tasks.named("copyContracts").get() != null } def "should configure copyContracts task as a dependency of the verifierStubsJar task"() { expect: - project.tasks.verifierStubsJar.getDependsOn().contains(project.tasks.named("copyContracts")) + project.tasks.verifierStubsJar.getDependsOn().contains(project.tasks.named("generateClientStubs")) } /** @@ -132,4 +220,43 @@ class ContractVerifierSpec extends Specification { expect: extension } + + def "should property merge scm repository settings for publishing stubs to scm"() { + given: + project.plugins.apply(SpringCloudContractVerifierGradlePlugin) + ContractVerifierExtension extension = project.extensions.findByType(ContractVerifierExtension) + PublishStubsToScmTask task = project.tasks.findByName(PublishStubsToScmTask.TASK_NAME) + + when: + extension.contractRepository.with { + repositoryUrl = "https://git.example.com" + username = "username" + password = "password" + proxyHost = "host" + proxyPort = 8080 + } + + then: + task.contractRepository.repositoryUrl.get() == "https://git.example.com" + task.contractRepository.username.get() == "username" + task.contractRepository.password.get() == "password" + task.contractRepository.proxyHost.get() == "host" + task.contractRepository.proxyPort.get() == 8080 + + and: + extension.publishStubsToScm.contractRepository.with { + repositoryUrl = "https://git2.example.com" + username = "username2" + password = "password2" + proxyHost = "host2" + proxyPort = 8081 + } + + then: + task.contractRepository.repositoryUrl.get() == "https://git2.example.com" + task.contractRepository.username.get() == "username2" + task.contractRepository.password.get() == "password2" + task.contractRepository.proxyHost.get() == "host2" + task.contractRepository.proxyPort.get() == 8081 + } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelperSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelperSpec.groovy deleted file mode 100644 index 4c2212467d..0000000000 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloaderHelperSpec.groovy +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2013-2020 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 org.springframework.cloud.contract.verifier.plugin - -import org.gradle.api.internal.provider.DefaultProperty -import org.gradle.api.internal.provider.PropertyHost -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.Property -import org.gradle.internal.service.scopes.ProjectBackedPropertyHost -import spock.lang.Specification - -import org.springframework.cloud.contract.stubrunner.StubConfiguration - -/** - * @author Marcin Grzejszczak - * @author Anatoliy Balakirev - */ -class GradleContractsDownloaderHelperSpec extends Specification { - - ObjectFactory objectFactory = Mock(ObjectFactory) - - def setup() { - // Is there any better way to say that I need a new object on each interaction with mock? - objectFactory.property(String) >>> [prop(String), prop(String), prop(String), prop(String), prop(String)] - } - - def "should parse dependency via string notation"() { - given: - String stringNotation = "com.example:foo:1.0.0:stubs" - ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) - dep.stringNotation.set(stringNotation) - when: - StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep) - then: - stubConfig.groupId == "com.example" - stubConfig.artifactId == "foo" - stubConfig.version == "1.0.0" - stubConfig.classifier == "stubs" - } - - def "should parse dependency via direct setting"() { - given: - ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) - dep.groupId.set("com.example") - dep.artifactId.set("foo") - dep.version.set("1.0.0") - dep.classifier.set("stubs") - when: - StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep) - then: - stubConfig.groupId == "com.example" - stubConfig.artifactId == "foo" - stubConfig.version == "1.0.0" - stubConfig.classifier == "stubs" - } - - def "should parse dependency via string notation with methods"() { - given: - String stringNotation = "com.example:foo:1.0.0:stubs" - ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) - dep.stringNotation.set(stringNotation) - when: - StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep) - then: - stubConfig.groupId == "com.example" - stubConfig.artifactId == "foo" - stubConfig.version == "1.0.0" - stubConfig.classifier == "stubs" - } - - def "should parse dependency via direct setting with methods"() { - given: - ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory) - dep.groupId.set("com.example") - dep.artifactId.set("foo") - dep.version.set("1.0.0") - dep.classifier.set("stubs") - when: - StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep) - then: - stubConfig.groupId == "com.example" - stubConfig.artifactId == "foo" - stubConfig.version == "1.0.0" - stubConfig.classifier == "stubs" - } - - // Have to use this internal property impl here. Is there some better way? - public Property prop(Class aClass) { - return new DefaultProperty(Stub(PropertyHost), aClass) - } -} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ScenarioProjectKotlinSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ScenarioProjectKotlinSpec.groovy index c148a3b812..a58c556b76 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ScenarioProjectKotlinSpec.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ScenarioProjectKotlinSpec.groovy @@ -16,6 +16,8 @@ package org.springframework.cloud.contract.verifier.plugin +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.TaskOutcome import spock.lang.Ignore import spock.lang.Stepwise @@ -35,6 +37,9 @@ class ScenarioProjectKotlinSpec extends ContractVerifierKotlinIntegrationSpec { expect: runTasksSuccessfully(checkAndPublishToMavenLocal()) jarContainsContractVerifierContracts('fraudDetectionService/build/libs') + BuildResult result = run("check", "--info", "--stacktrace") + result.task(":fraudDetectionService:check").outcome == TaskOutcome.UP_TO_DATE + result.task(":loanApplicationService:check").outcome == TaskOutcome.UP_TO_DATE } def "should pass basic flow for JUnit"() { @@ -44,6 +49,9 @@ class ScenarioProjectKotlinSpec extends ContractVerifierKotlinIntegrationSpec { switchToJunitTestFramework() runTasksSuccessfully(checkAndPublishToMavenLocal()) jarContainsContractVerifierContracts('fraudDetectionService/build/libs') + BuildResult result = run("check", "--info", "--stacktrace") + result.task(":fraudDetectionService:check").outcome == TaskOutcome.UP_TO_DATE + result.task(":loanApplicationService:check").outcome == TaskOutcome.UP_TO_DATE } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ScenarioProjectSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ScenarioProjectSpec.groovy index 8b29c3005f..0954bd224a 100755 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ScenarioProjectSpec.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ScenarioProjectSpec.groovy @@ -16,6 +16,8 @@ package org.springframework.cloud.contract.verifier.plugin +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.TaskOutcome import spock.lang.Ignore import spock.lang.Stepwise @@ -35,6 +37,9 @@ class ScenarioProjectSpec extends ContractVerifierIntegrationSpec { expect: runTasksSuccessfully(checkAndPublishToMavenLocal()) jarContainsContractVerifierContracts('fraudDetectionService/build/libs') + BuildResult result = run("check", "--info", "--stacktrace") + result.task(":fraudDetectionService:check").outcome == TaskOutcome.UP_TO_DATE + result.task(":loanApplicationService:check").outcome == TaskOutcome.UP_TO_DATE } def "should pass basic flow for JUnit"() { @@ -45,6 +50,26 @@ class ScenarioProjectSpec extends ContractVerifierIntegrationSpec { emptySourceSet() runTasksSuccessfully(checkAndPublishToMavenLocal()) jarContainsContractVerifierContracts('fraudDetectionService/build/libs') + BuildResult result = run("check", "--info", "--stacktrace") + result.task(":fraudDetectionService:check").outcome == TaskOutcome.UP_TO_DATE + result.task(":loanApplicationService:check").outcome == TaskOutcome.UP_TO_DATE } + def "should properly work with build cache"() { + given: + def gradleUserHomeDir = new File(testProjectDir, ".gradleUserHome") + gradleUserHomeDir.mkdirs() + String[] tasks = ["-g ${gradleUserHomeDir}", "clean", "check", "publishToMavenLocal", "--info", "--stacktrace", "--build-cache"] + assert fileExists("build.gradle") + + expect: + runTasksSuccessfully(tasks) + jarContainsContractVerifierContracts('fraudDetectionService/build/libs') + BuildResult result = run(tasks) + result.task(":fraudDetectionService:copyContracts").outcome == TaskOutcome.FROM_CACHE + result.task(":fraudDetectionService:generateContractTests").outcome == TaskOutcome.FROM_CACHE + result.task(":fraudDetectionService:contractTest").outcome == TaskOutcome.FROM_CACHE + result.task(":fraudDetectionService:generateClientStubs").outcome == TaskOutcome.FROM_CACHE + result.task(":loanApplicationService:check").outcome == TaskOutcome.UP_TO_DATE + } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle index 87440f3b8a..b76a361a95 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle @@ -97,7 +97,7 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService') contractsDslDir = file("${project.projectDir.absolutePath}/mappings/") generatedTestSourcesDir = file("${project.buildDir}/generated-test-sources/") stubsOutputDir = stubsOutputDirRoot - sourceSet = "test" + sourceSet = "contractTest" } jar { @@ -146,5 +146,5 @@ configure(project(':loanApplicationService')) { into "src/test/resources/mappings" } - project.tasks.named("generateContractTests").get().dependsOn('copyCollaboratorStubs') + project.tasks.named("test").get().dependsOn('copyCollaboratorStubs') } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProjectKotlin/build.gradle.kts b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProjectKotlin/build.gradle.kts index 8fd0023e52..193986266d 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProjectKotlin/build.gradle.kts +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProjectKotlin/build.gradle.kts @@ -139,8 +139,8 @@ configure(listOf(project(":loanApplicationService"))) { into("src/test/resources/mappings") } - val generateContractTests by existing - generateContractTests { + val test by existing + test { dependsOn(copyCollaboratorStubs) } }