[gradle] Enabling up-to-date checks and gradle caching for openapigenerator tasks (#6716)

This commit is contained in:
HenningWaack 2020-08-10 03:40:19 +02:00 committed by GitHub
parent 6f0bef61ba
commit e4c858cd25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 250 additions and 101 deletions

View File

@ -15,14 +15,14 @@ install:
# install gradle # install gradle
- ps: | - ps: |
Add-Type -AssemblyName System.IO.Compression.FileSystem Add-Type -AssemblyName System.IO.Compression.FileSystem
if (!(Test-Path -Path "C:\gradle" )) { if (!(Test-Path -Path "C:\gradle\gradle-5.6.4" )) {
(new-object System.Net.WebClient).DownloadFile( (new-object System.Net.WebClient).DownloadFile(
'https://services.gradle.org/distributions/gradle-5.3.1-bin.zip', 'https://services.gradle.org/distributions/gradle-5.6.4-bin.zip',
'C:\gradle-bin.zip' 'C:\gradle-bin.zip'
) )
[System.IO.Compression.ZipFile]::ExtractToDirectory("C:\gradle-bin.zip", "C:\gradle") [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\gradle-bin.zip", "C:\gradle")
} }
- cmd: SET PATH=C:\maven\apache-maven-3.2.5\bin;C:\gradle\gradle-5.3.1\bin;%JAVA_HOME%\bin;%PATH% - cmd: SET PATH=C:\maven\apache-maven-3.2.5\bin;C:\gradle\gradle-5.6.4\bin;%JAVA_HOME%\bin;%PATH%
- cmd: SET MAVEN_OPTS=-Xmx4g - cmd: SET MAVEN_OPTS=-Xmx4g
- cmd: SET JAVA_OPTS=-Xmx4g - cmd: SET JAVA_OPTS=-Xmx4g
- cmd: SET M2_HOME=C:\maven\apache-maven-3.2.5 - cmd: SET M2_HOME=C:\maven\apache-maven-3.2.5

View File

@ -61,6 +61,35 @@ task validateBadSpec(type: org.openapitools.generator.gradle.plugin.tasks.Valida
task validateSpecs(dependsOn: ['validateGoodSpec', 'validateBadSpec']) task validateSpecs(dependsOn: ['validateGoodSpec', 'validateBadSpec'])
---- ----
[NOTE]
====
The tasks support Gradle Up-To-Date checking and Gradle Cache. Enable caching globally by setting `org.gradle.caching=true` in the `gradle.settings`
file or by passing the command line property `--build-cache` when executing on the command line.
Disable up-to-date checks and caching by setting the following property when using the extension:
.Disable caching for extension
[source,groovy]
----
tasks.withType(org.openapitools.generator.gradle.plugin.tasks.GenerateTask) {
outputs.upToDateWhen { false }
outputs.cacheIf { false }
}
----
Disable up-to-date checks and caching for a custom task:
.Disable caching for custom task
[source,groovy]
----
task validateGoodSpec(type: org.openapitools.generator.gradle.plugin.tasks.ValidateTask){
outputs.upToDateWhen { false }
outputs.cacheIf { false }
inputSpec = "$rootDir/petstore-v3.0.yaml".toString()
}
----
====
== Plugin Setup == Plugin Setup
//# RELEASE_VERSION //# RELEASE_VERSION

View File

@ -1,5 +1,5 @@
#Thu Jan 30 22:14:34 EST 2020 #Thu Jan 30 22:14:34 EST 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -1,18 +1,18 @@
# Local Spec Sample # Local Spec Sample
This example assumes you have Gradle 4.7+ installed. No gradle wrapper is provided in samples. This example assumes you have Gradle 5.6.4+ installed. No gradle wrapper is provided in samples.
First, publish the openapi-generator-gradle-plugin locally via `./gradlew assemble install` in the module directory. First, publish the openapi-generator-gradle-plugin locally via `./gradlew assemble publishToMavenLocal` in the module directory.
Then, run the following tasks in this example directory. Then, run the following tasks in this example directory.
```bash ```bash
gradle openApiGenerate gradle openApiGenerate # expected outcome: BUILD SCCESSFUL
gradle openApiMeta gradle openApiMeta # expected outcome: BUILD SCCESSFUL
gradle openApiValidate gradle openApiValidate # expected outcome: BUILD FAILED
gradle buildGoSdk gradle buildGoSdk # expected outcome: BUILD SCCESSFUL
gradle buildDotnetSdk gradle buildDotnetSdk # expected outcome: BUILD SCCESSFUL
gradle generateGoWithInvalidSpec gradle generateGoWithInvalidSpec # expected outcome: BUILD FAILED
``` ```
The samples can be tested against other versions of the plugin using the `openApiGeneratorVersion` property. For example: The samples can be tested against other versions of the plugin using the `openApiGeneratorVersion` property. For example:

View File

@ -55,9 +55,9 @@ class OpenApiGeneratorPlugin : Plugin<Project> {
) )
val generators = extensions.create( val generators = extensions.create(
"openApiGenerators", "openApiGenerators",
OpenApiGeneratorGeneratorsExtension::class.java, OpenApiGeneratorGeneratorsExtension::class.java,
project project
) )
generate.outputDir.set("$buildDir/generate-resources/main") generate.outputDir.set("$buildDir/generate-resources/main")

View File

@ -317,7 +317,7 @@ open class OpenApiGeneratorGenerateExtension(project: Project) {
} }
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
fun applyDefaults(){ fun applyDefaults() {
releaseNote.set("Minor update") releaseNote.set("Minor update")
modelNamePrefix.set("") modelNamePrefix.set("")
modelNameSuffix.set("") modelNameSuffix.set("")

View File

@ -19,7 +19,14 @@ package org.openapitools.generator.gradle.plugin.tasks
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.GradleException import org.gradle.api.GradleException
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Internal
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.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.options.Option import org.gradle.api.tasks.options.Option
import org.gradle.internal.logging.text.StyledTextOutput import org.gradle.internal.logging.text.StyledTextOutput
@ -32,7 +39,6 @@ import org.openapitools.codegen.DefaultGenerator
import org.openapitools.codegen.config.CodegenConfigurator import org.openapitools.codegen.config.CodegenConfigurator
import org.openapitools.codegen.config.GlobalSettings import org.openapitools.codegen.config.GlobalSettings
/** /**
* A task which generates the desired code. * A task which generates the desired code.
* *
@ -43,63 +49,73 @@ import org.openapitools.codegen.config.GlobalSettings
* @author Jim Schubert * @author Jim Schubert
*/ */
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
@CacheableTask
open class GenerateTask : DefaultTask() { open class GenerateTask : DefaultTask() {
/** /**
* The verbosity of generation * The verbosity of generation
*/ */
@get:Internal @Optional
@Input
val verbose = project.objects.property<Boolean>() val verbose = project.objects.property<Boolean>()
/** /**
* Whether or not an input specification should be validated upon generation. * Whether or not an input specification should be validated upon generation.
*/ */
@get:Internal @Optional
@Input
val validateSpec = project.objects.property<Boolean>() val validateSpec = project.objects.property<Boolean>()
/** /**
* The name of the generator which will handle codegen. (see "openApiGenerators" task) * The name of the generator which will handle codegen. (see "openApiGenerators" task)
*/ */
@get:Internal @Optional
@Input
val generatorName = project.objects.property<String>() val generatorName = project.objects.property<String>()
/** /**
* The output target directory into which code will be generated. * The output target directory into which code will be generated.
*/ */
@get:Internal @Optional
@get:OutputDirectory
val outputDir = project.objects.property<String>() val outputDir = project.objects.property<String>()
@Suppress("unused") @Suppress("unused")
@get:Internal @get:Internal
@set:Option(option = "input", description = "The input specification.") @set:Option(option = "input", description = "The input specification.")
@Input
var input: String? = null var input: String? = null
set(value) { set(value) {
inputSpec.set(value) inputSpec.set(value)
} }
/** /**
* The Open API 2.0/3.x specification location. * The Open API 2.0/3.x specification location.
*/ */
@get:Internal @get:InputFile
@PathSensitive(PathSensitivity.RELATIVE)
val inputSpec = project.objects.property<String>() val inputSpec = project.objects.property<String>()
/** /**
* The template directory holding a custom template. * The template directory holding a custom template.
*/ */
@get:Internal @Optional
@Input
val templateDir = project.objects.property<String?>() val templateDir = project.objects.property<String?>()
/** /**
* Adds authorization headers when fetching the OpenAPI definitions remotely. * Adds authorization headers when fetching the OpenAPI definitions remotely.
* Pass in a URL-encoded string of name:header with a comma separating multiple values * Pass in a URL-encoded string of name:header with a comma separating multiple values
*/ */
@get:Internal @Optional
@Input
val auth = project.objects.property<String>() val auth = project.objects.property<String>()
/** /**
* Sets specified global properties. * Sets specified global properties.
*/ */
@get:Internal @Optional
@Input
val globalProperties = project.objects.mapProperty<String, String>() val globalProperties = project.objects.mapProperty<String, String>()
/** /**
@ -107,159 +123,185 @@ open class GenerateTask : DefaultTask() {
* File content should be in a json format { "optionKey":"optionValue", "optionKey1":"optionValue1"...} * File content should be in a json format { "optionKey":"optionValue", "optionKey1":"optionValue1"...}
* Supported options can be different for each language. Run config-help -g {generator name} command for language specific config options. * Supported options can be different for each language. Run config-help -g {generator name} command for language specific config options.
*/ */
@get:Internal @Optional
@Input
val configFile = project.objects.property<String>() val configFile = project.objects.property<String>()
/** /**
* Specifies if the existing files should be overwritten during the generation. * Specifies if the existing files should be overwritten during the generation.
*/ */
@get:Internal @Optional
@Input
val skipOverwrite = project.objects.property<Boolean?>() val skipOverwrite = project.objects.property<Boolean?>()
/** /**
* Package for generated classes (where supported) * Package for generated classes (where supported)
*/ */
@get:Internal @Optional
@Input
val packageName = project.objects.property<String>() val packageName = project.objects.property<String>()
/** /**
* Package for generated api classes * Package for generated api classes
*/ */
@get:Internal @Optional
@Input
val apiPackage = project.objects.property<String>() val apiPackage = project.objects.property<String>()
/** /**
* Package for generated models * Package for generated models
*/ */
@get:Internal @Optional
@Input
val modelPackage = project.objects.property<String>() val modelPackage = project.objects.property<String>()
/** /**
* Prefix that will be prepended to all model names. Default is the empty string. * Prefix that will be prepended to all model names. Default is the empty string.
*/ */
@get:Internal @Optional
@Input
val modelNamePrefix = project.objects.property<String>() val modelNamePrefix = project.objects.property<String>()
/** /**
* Suffix that will be appended to all model names. Default is the empty string. * Suffix that will be appended to all model names. Default is the empty string.
*/ */
@get:Internal @Optional
@Input
val modelNameSuffix = project.objects.property<String>() val modelNameSuffix = project.objects.property<String>()
/** /**
* Sets instantiation type mappings. * Sets instantiation type mappings.
*/ */
@get:Internal @Optional
@Input
val instantiationTypes = project.objects.mapProperty<String, String>() val instantiationTypes = project.objects.mapProperty<String, String>()
/** /**
* Sets mappings between OpenAPI spec types and generated code types. * Sets mappings between OpenAPI spec types and generated code types.
*/ */
@get:Internal @Optional
@Input
val typeMappings = project.objects.mapProperty<String, String>() val typeMappings = project.objects.mapProperty<String, String>()
/** /**
* Sets additional properties that can be referenced by the mustache templates in the format of name=value,name=value. * Sets additional properties that can be referenced by the mustache templates in the format of name=value,name=value.
* You can also have multiple occurrences of this option. * You can also have multiple occurrences of this option.
*/ */
@get:Internal @Optional
@Input
val additionalProperties = project.objects.mapProperty<String, String>() val additionalProperties = project.objects.mapProperty<String, String>()
/** /**
* Sets server variable for server URL template substitution, in the format of name=value,name=value. * Sets server variable for server URL template substitution, in the format of name=value,name=value.
* You can also have multiple occurrences of this option. * You can also have multiple occurrences of this option.
*/ */
@get:Internal @Optional
@Input
val serverVariables = project.objects.mapProperty<String, String>() val serverVariables = project.objects.mapProperty<String, String>()
/** /**
* Specifies additional language specific primitive types in the format of type1,type2,type3,type3. For example: String,boolean,Boolean,Double. * Specifies additional language specific primitive types in the format of type1,type2,type3,type3. For example: String,boolean,Boolean,Double.
*/ */
@get:Internal @Optional
@Input
val languageSpecificPrimitives = project.objects.listProperty<String>() val languageSpecificPrimitives = project.objects.listProperty<String>()
/** /**
* Specifies mappings between a given class and the import that should be used for that class. * Specifies mappings between a given class and the import that should be used for that class.
*/ */
@get:Internal @Optional
@Input
val importMappings = project.objects.mapProperty<String, String>() val importMappings = project.objects.mapProperty<String, String>()
/** /**
* Root package for generated code. * Root package for generated code.
*/ */
@get:Internal @Optional
@Input
val invokerPackage = project.objects.property<String>() val invokerPackage = project.objects.property<String>()
/** /**
* GroupId in generated pom.xml/build.gradle or other build script. Language-specific conversions occur in non-jvm generators. * GroupId in generated pom.xml/build.gradle or other build script. Language-specific conversions occur in non-jvm generators.
*/ */
@get:Internal @Optional
@Input
val groupId = project.objects.property<String>() val groupId = project.objects.property<String>()
/** /**
* ArtifactId in generated pom.xml/build.gradle or other build script. Language-specific conversions occur in non-jvm generators. * ArtifactId in generated pom.xml/build.gradle or other build script. Language-specific conversions occur in non-jvm generators.
*/ */
@get:Internal @Optional
@Input
val id = project.objects.property<String>() val id = project.objects.property<String>()
/** /**
* Artifact version in generated pom.xml/build.gradle or other build script. Language-specific conversions occur in non-jvm generators. * Artifact version in generated pom.xml/build.gradle or other build script. Language-specific conversions occur in non-jvm generators.
*/ */
@get:Internal @Optional
@Input
val version = project.objects.property<String>() val version = project.objects.property<String>()
/** /**
* Reference the library template (sub-template) of a generator. * Reference the library template (sub-template) of a generator.
*/ */
@get:Internal @Optional
@Input
val library = project.objects.property<String?>() val library = project.objects.property<String?>()
/** /**
* Git host, e.g. gitlab.com. * Git host, e.g. gitlab.com.
*/ */
@get:Internal @Optional
@Input
val gitHost = project.objects.property<String?>() val gitHost = project.objects.property<String?>()
/** /**
* Git user ID, e.g. openapitools. * Git user ID, e.g. openapitools.
*/ */
@get:Internal @Optional
@Input
val gitUserId = project.objects.property<String?>() val gitUserId = project.objects.property<String?>()
/** /**
* Git repo ID, e.g. openapi-generator. * Git repo ID, e.g. openapi-generator.
*/ */
@get:Internal @Optional
@Input
val gitRepoId = project.objects.property<String?>() val gitRepoId = project.objects.property<String?>()
/** /**
* Release note, default to 'Minor update'. * Release note, default to 'Minor update'.
*/ */
@get:Internal @Optional
@Input
val releaseNote = project.objects.property<String?>() val releaseNote = project.objects.property<String?>()
/** /**
* HTTP user agent, e.g. codegen_csharp_api_client, default to 'OpenAPI-Generator/{packageVersion}/{language}' * HTTP user agent, e.g. codegen_csharp_api_client, default to 'OpenAPI-Generator/{packageVersion}/{language}'
*/ */
@get:Internal @Optional
@Input
val httpUserAgent = project.objects.property<String?>() val httpUserAgent = project.objects.property<String?>()
/** /**
* Specifies how a reserved name should be escaped to. * Specifies how a reserved name should be escaped to.
*/ */
@get:Internal @Optional
@Input
val reservedWordsMappings = project.objects.mapProperty<String, String>() val reservedWordsMappings = project.objects.mapProperty<String, String>()
/** /**
* Specifies an override location for the .openapi-generator-ignore file. Most useful on initial generation. * Specifies an override location for the .openapi-generator-ignore file. Most useful on initial generation.
*/ */
@get:Internal @Optional
@Input
val ignoreFileOverride = project.objects.property<String?>() val ignoreFileOverride = project.objects.property<String?>()
/** /**
* Remove prefix of operationId, e.g. config_getId => getId * Remove prefix of operationId, e.g. config_getId => getId
*/ */
@get:Internal @Optional
@Input
val removeOperationIdPrefix = project.objects.property<Boolean?>() val removeOperationIdPrefix = project.objects.property<Boolean?>()
/** /**
@ -271,7 +313,8 @@ open class GenerateTask : DefaultTask() {
* in others being disabled. That is, OpenAPI Generator considers any one of these to define a subset of generation. * in others being disabled. That is, OpenAPI Generator considers any one of these to define a subset of generation.
* For more control over generation of individual files, configure an ignore file and refer to it via [ignoreFileOverride]. * For more control over generation of individual files, configure an ignore file and refer to it via [ignoreFileOverride].
*/ */
@get:Internal @Optional
@Input
val apiFilesConstrainedTo = project.objects.listProperty<String>() val apiFilesConstrainedTo = project.objects.listProperty<String>()
/** /**
@ -281,7 +324,8 @@ open class GenerateTask : DefaultTask() {
* in others being disabled. That is, OpenAPI Generator considers any one of these to define a subset of generation. * in others being disabled. That is, OpenAPI Generator considers any one of these to define a subset of generation.
* For more control over generation of individual files, configure an ignore file and refer to it via [ignoreFileOverride]. * For more control over generation of individual files, configure an ignore file and refer to it via [ignoreFileOverride].
*/ */
@get:Internal @Optional
@Input
val modelFilesConstrainedTo = project.objects.listProperty<String>() val modelFilesConstrainedTo = project.objects.listProperty<String>()
/** /**
@ -294,7 +338,8 @@ open class GenerateTask : DefaultTask() {
* in others being disabled. That is, OpenAPI Generator considers any one of these to define a subset of generation. * in others being disabled. That is, OpenAPI Generator considers any one of these to define a subset of generation.
* For more control over generation of individual files, configure an ignore file and refer to it via [ignoreFileOverride]. * For more control over generation of individual files, configure an ignore file and refer to it via [ignoreFileOverride].
*/ */
@get:Internal @Optional
@Input
val supportingFilesConstrainedTo = project.objects.listProperty<String>() val supportingFilesConstrainedTo = project.objects.listProperty<String>()
/** /**
@ -305,7 +350,8 @@ open class GenerateTask : DefaultTask() {
* For more control over generation of individual files, configure an ignore file and * For more control over generation of individual files, configure an ignore file and
* refer to it via [ignoreFileOverride]. * refer to it via [ignoreFileOverride].
*/ */
@get:Internal @Optional
@Input
val generateModelTests = project.objects.property<Boolean>() val generateModelTests = project.objects.property<Boolean>()
/** /**
@ -316,7 +362,8 @@ open class GenerateTask : DefaultTask() {
* For more control over generation of individual files, configure an ignore file and * For more control over generation of individual files, configure an ignore file and
* refer to it via [ignoreFileOverride]. * refer to it via [ignoreFileOverride].
*/ */
@get:Internal @Optional
@Input
val generateModelDocumentation = project.objects.property<Boolean>() val generateModelDocumentation = project.objects.property<Boolean>()
/** /**
@ -327,7 +374,8 @@ open class GenerateTask : DefaultTask() {
* For more control over generation of individual files, configure an ignore file and * For more control over generation of individual files, configure an ignore file and
* refer to it via [ignoreFileOverride]. * refer to it via [ignoreFileOverride].
*/ */
@get:Internal @Optional
@Input
val generateApiTests = project.objects.property<Boolean>() val generateApiTests = project.objects.property<Boolean>()
/** /**
@ -338,21 +386,23 @@ open class GenerateTask : DefaultTask() {
* For more control over generation of individual files, configure an ignore file and * For more control over generation of individual files, configure an ignore file and
* refer to it via [ignoreFileOverride]. * refer to it via [ignoreFileOverride].
*/ */
@get:Internal @Optional
@Input
val generateApiDocumentation = project.objects.property<Boolean>() val generateApiDocumentation = project.objects.property<Boolean>()
/** /**
* A special-case setting which configures some generators with XML support. In some cases, * A special-case setting which configures some generators with XML support. In some cases,
* this forces json OR xml, so the default here is false. * this forces json OR xml, so the default here is false.
*/ */
@get:Internal @Optional
@Input
val withXml = project.objects.property<Boolean>() val withXml = project.objects.property<Boolean>()
/** /**
* To write all log messages (not just errors) to STDOUT * To write all log messages (not just errors) to STDOUT
*/ */
@get:Internal @Optional
@Input
val logToStderr = project.objects.property<Boolean>() val logToStderr = project.objects.property<Boolean>()
/** /**
@ -361,13 +411,15 @@ open class GenerateTask : DefaultTask() {
* LANG_POST_PROCESS_FILE (e.g. GO_POST_PROCESS_FILE, SCALA_POST_PROCESS_FILE). Please open an issue if your target * LANG_POST_PROCESS_FILE (e.g. GO_POST_PROCESS_FILE, SCALA_POST_PROCESS_FILE). Please open an issue if your target
* generator does not support this functionality. * generator does not support this functionality.
*/ */
@get:Internal @Optional
@Input
val enablePostProcessFile = project.objects.property<Boolean>() val enablePostProcessFile = project.objects.property<Boolean>()
/** /**
* To skip spec validation. When true, we will skip the default behavior of validating a spec before generation. * To skip spec validation. When true, we will skip the default behavior of validating a spec before generation.
*/ */
@get:Internal @Optional
@Input
val skipValidateSpec = project.objects.property<Boolean>() val skipValidateSpec = project.objects.property<Boolean>()
/** /**
@ -375,19 +427,22 @@ open class GenerateTask : DefaultTask() {
* definitions generated as top-level Array-of-items, List-of-items, Map-of-items definitions. * definitions generated as top-level Array-of-items, List-of-items, Map-of-items definitions.
* When true, A model representation either containing or extending the array,list,map (depending on specific generator implementation) will be generated. * When true, A model representation either containing or extending the array,list,map (depending on specific generator implementation) will be generated.
*/ */
@get:Internal @Optional
@Input
val generateAliasAsModel = project.objects.property<Boolean>() val generateAliasAsModel = project.objects.property<Boolean>()
/** /**
* A dynamic map of options specific to a generator. * A dynamic map of options specific to a generator.
*/ */
@get:Internal @Optional
@Input
val configOptions = project.objects.mapProperty<String, String>() val configOptions = project.objects.mapProperty<String, String>()
/** /**
* Templating engine: "mustache" (default) or "handlebars" (beta) * Templating engine: "mustache" (default) or "handlebars" (beta)
*/ */
@get:Internal @Optional
@Input
val engine = project.objects.property<String?>() val engine = project.objects.property<String?>()
private fun <T : Any?> Property<T>.ifNotEmpty(block: Property<T>.(T) -> Unit) { private fun <T : Any?> Property<T>.ifNotEmpty(block: Property<T>.(T) -> Unit) {

View File

@ -19,15 +19,18 @@ package org.openapitools.generator.gradle.plugin.tasks
import com.samskivert.mustache.Mustache import com.samskivert.mustache.Mustache
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.GradleException import org.gradle.api.GradleException
import org.gradle.api.tasks.Internal import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
import org.gradle.internal.logging.text.StyledTextOutput import org.gradle.internal.logging.text.StyledTextOutput
import org.gradle.internal.logging.text.StyledTextOutputFactory import org.gradle.internal.logging.text.StyledTextOutputFactory
import org.gradle.kotlin.dsl.property import org.gradle.kotlin.dsl.property
import org.openapitools.codegen.* import org.openapitools.codegen.CodegenConfig
import org.openapitools.codegen.api.TemplatePathLocator import org.openapitools.codegen.CodegenConstants
import org.openapitools.codegen.SupportingFile
import org.openapitools.codegen.TemplateManager
import org.openapitools.codegen.templating.CommonTemplateContentLocator import org.openapitools.codegen.templating.CommonTemplateContentLocator
import org.openapitools.codegen.templating.GeneratorTemplateContentLocator
import org.openapitools.codegen.templating.MustacheEngineAdapter import org.openapitools.codegen.templating.MustacheEngineAdapter
import org.openapitools.codegen.templating.TemplateManagerOptions import org.openapitools.codegen.templating.TemplateManagerOptions
import java.io.File import java.io.File
@ -39,15 +42,16 @@ import java.nio.charset.Charset
* *
* @author Jim Schubert * @author Jim Schubert
*/ */
@CacheableTask
open class MetaTask : DefaultTask() { open class MetaTask : DefaultTask() {
@get:Internal @get:Input
val generatorName = project.objects.property<String>() val generatorName = project.objects.property<String>()
@get:Internal @get:Input
val packageName = project.objects.property<String>() val packageName = project.objects.property<String>()
@get:Internal @get:OutputDirectory
val outputFolder = project.objects.property<String>() val outputFolder = project.objects.property<String>()
@Suppress("unused") @Suppress("unused")
@ -92,9 +96,9 @@ open class MetaTask : DefaultTask() {
val outputFile = File(destinationFolder, it.destinationFilename) val outputFile = File(destinationFolder, it.destinationFilename)
val templateProcessor = TemplateManager( val templateProcessor = TemplateManager(
TemplateManagerOptions(false, false), TemplateManagerOptions(false, false),
MustacheEngineAdapter(), MustacheEngineAdapter(),
arrayOf(CommonTemplateContentLocator("codegen")) arrayOf(CommonTemplateContentLocator("codegen"))
) )
val template = templateProcessor.getFullTemplateContents(it.templateFile) val template = templateProcessor.getFullTemplateContents(it.templateFile)

View File

@ -23,7 +23,12 @@ import io.swagger.v3.parser.core.models.ParseOptions
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.GradleException import org.gradle.api.GradleException
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Internal
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.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.options.Option import org.gradle.api.tasks.options.Option
import org.gradle.internal.logging.text.StyledTextOutput import org.gradle.internal.logging.text.StyledTextOutput
@ -49,10 +54,12 @@ import org.openapitools.codegen.validations.oas.RuleConfiguration
* @author Jim Schubert * @author Jim Schubert
*/ */
open class ValidateTask : DefaultTask() { open class ValidateTask : DefaultTask() {
@get:Internal @get:InputFile
@PathSensitive(PathSensitivity.RELATIVE)
var inputSpec = project.objects.property<String>() var inputSpec = project.objects.property<String>()
@get:Internal @Optional
@Input
var recommend = project.objects.property<Boolean?>() var recommend = project.objects.property<Boolean?>()
@Suppress("unused") @Suppress("unused")
@ -75,12 +82,11 @@ open class ValidateTask : DefaultTask() {
val options = ParseOptions() val options = ParseOptions()
options.isResolve = true options.isResolve = true
val result = OpenAPIParser().readLocation(spec, null, options) val result = OpenAPIParser().readLocation(spec, null, options)
val messages = result.messages.toSet() val messages = result.messages.toSet()
val out = services.get(StyledTextOutputFactory::class.java).create("openapi") val out = services.get(StyledTextOutputFactory::class.java).create("openapi")
val ruleConfiguration = RuleConfiguration() val ruleConfiguration = RuleConfiguration()
ruleConfiguration.isEnableRecommendations = recommendations ruleConfiguration.isEnableRecommendations = recommendations

View File

@ -7,7 +7,7 @@ import java.io.File
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
class GenerateTaskDslTest : TestBase() { class GenerateTaskDslTest : TestBase() {
override var temp: File = createTempDir(javaClass.simpleName) override var temp: File = createTempDir(javaClass.simpleName)
private val defaultBuildGradle = """ private val defaultBuildGradle = """
@ -46,18 +46,18 @@ class GenerateTaskDslTest : TestBase() {
assertTrue(result.output.contains("Successfully generated code to"), "User friendly generate notice is missing.") assertTrue(result.output.contains("Successfully generated code to"), "User friendly generate notice is missing.")
listOf( listOf(
"build/kotlin/.openapi-generator-ignore", "build/kotlin/.openapi-generator-ignore",
"build/kotlin/docs/PetsApi.md", "build/kotlin/docs/PetsApi.md",
"build/kotlin/docs/Error.md", "build/kotlin/docs/Error.md",
"build/kotlin/docs/Pet.md", "build/kotlin/docs/Pet.md",
"build/kotlin/README.md", "build/kotlin/README.md",
"build/kotlin/build.gradle", "build/kotlin/build.gradle",
"build/kotlin/.openapi-generator/VERSION", "build/kotlin/.openapi-generator/VERSION",
"build/kotlin/settings.gradle", "build/kotlin/settings.gradle",
"build/kotlin/src/main/kotlin/org/openapitools/example/model/Pet.kt", "build/kotlin/src/main/kotlin/org/openapitools/example/model/Pet.kt",
"build/kotlin/src/main/kotlin/org/openapitools/example/model/Error.kt", "build/kotlin/src/main/kotlin/org/openapitools/example/model/Error.kt",
"build/kotlin/src/main/kotlin/org/openapitools/example/api/PetsApi.kt", "build/kotlin/src/main/kotlin/org/openapitools/example/api/PetsApi.kt",
"build/kotlin/src/main/kotlin/org/openapitools/client/infrastructure/ApiClient.kt" "build/kotlin/src/main/kotlin/org/openapitools/client/infrastructure/ApiClient.kt"
).map { ).map {
val f = File(temp, it) val f = File(temp, it)
assertTrue(f.exists() && f.isFile, "An expected file was not generated when invoking the generation.") assertTrue(f.exists() && f.isFile, "An expected file was not generated when invoking the generation.")
@ -67,6 +67,61 @@ class GenerateTaskDslTest : TestBase() {
"Expected a successful run, but found ${result.task(":openApiGenerate")?.outcome}") "Expected a successful run, but found ${result.task(":openApiGenerate")?.outcome}")
} }
@Test
fun `openApiGenerate should used up-to-date instead of regenerate`() {
// Arrange
val projectFiles = mapOf(
"spec.yaml" to javaClass.classLoader.getResourceAsStream("specs/petstore-v3.0.yaml")
)
withProject(defaultBuildGradle, projectFiles)
// Act
val resultFirstRun = GradleRunner.create()
.withProjectDir(temp)
.withArguments("openApiGenerate")
.withPluginClasspath()
.build()
val resultSecondRun = GradleRunner.create()
.withProjectDir(temp)
.withArguments("openApiGenerate")
.withPluginClasspath()
.build()
// Assert
assertTrue(resultFirstRun.output.contains("Task ':openApiGenerate' is not up-to-date"), "First run should not be up-to-date")
assertTrue(resultSecondRun.output.contains("Task :openApiGenerate UP-TO-DATE"), "Task of second run should be up-to-date")
}
@Test
fun `openApiGenerate should use cache instead of regenerate`() {
// Arrange
val projectFiles = mapOf(
"spec.yaml" to javaClass.classLoader.getResourceAsStream("specs/petstore-v3.0.yaml")
)
withProject(defaultBuildGradle, projectFiles)
// Act
val resultFirstRun = GradleRunner.create()
.withProjectDir(temp)
.withArguments("openApiGenerate", "--build-cache")
.withPluginClasspath()
.build()
// delete the build directory from the last run
File(temp, "build/kotlin").deleteRecursively()
// re-run
val resultSecondRun = GradleRunner.create()
.withProjectDir(temp)
.withArguments("openApiGenerate", "--build-cache")
.withPluginClasspath()
.build()
// Assert
assertTrue(resultFirstRun.output.contains("Task ':openApiGenerate' is not up-to-date"), "First run should not be up-to-date")
assertTrue(resultSecondRun.output.contains("Task :openApiGenerate FROM-CACHE"), "Task of second run should be from cache")
}
@Test @Test
fun `openApiValidate should fail on invalid spec`() { fun `openApiValidate should fail on invalid spec`() {
// Arrange // Arrange
@ -128,10 +183,10 @@ class GenerateTaskDslTest : TestBase() {
} }
@Test @Test
fun `openapiGenerate should attempt to set handlebars when specified as engine`(){ fun `openapiGenerate should attempt to set handlebars when specified as engine`() {
// Arrange // Arrange
val projectFiles = mapOf( val projectFiles = mapOf(
"spec.yaml" to javaClass.classLoader.getResourceAsStream("specs/petstore-v3.0.yaml") "spec.yaml" to javaClass.classLoader.getResourceAsStream("specs/petstore-v3.0.yaml")
) )
withProject(""" withProject("""
@ -151,16 +206,16 @@ class GenerateTaskDslTest : TestBase() {
// Act // Act
val result = GradleRunner.create() val result = GradleRunner.create()
.withProjectDir(temp) .withProjectDir(temp)
.withArguments("openApiGenerate", "--stacktrace") .withArguments("openApiGenerate", "--stacktrace")
.withPluginClasspath() .withPluginClasspath()
.buildAndFail() .buildAndFail()
// Assert // Assert
// rather than write out full handlebars generator templates, we'll just test that the configurator has set handlebars as the engine. // rather than write out full handlebars generator templates, we'll just test that the configurator has set handlebars as the engine.
assertTrue(result.output.contains("HandlebarsException"), "Stack should expose an exception for missing templates.") assertTrue(result.output.contains("HandlebarsException"), "Stack should expose an exception for missing templates.")
assertTrue(result.output.contains("handlebars"), "Build should have attempted to use handlebars.") assertTrue(result.output.contains("handlebars"), "Build should have attempted to use handlebars.")
assertEquals(TaskOutcome.FAILED, result.task(":openApiGenerate")?.outcome, assertEquals(TaskOutcome.FAILED, result.task(":openApiGenerate")?.outcome,
"Expected a failed run, but found ${result.task(":openApiGenerate")?.outcome}") "Expected a failed run, but found ${result.task(":openApiGenerate")?.outcome}")
} }
} }