diff --git a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt index bb2380d0a61..3eeb5955a74 100644 --- a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt +++ b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt @@ -107,7 +107,7 @@ open class GenerateTask @Inject constructor(private val objectFactory: ObjectFac * Local root folder with spec files */ @Optional - @get:InputFile + @get:InputDirectory @PathSensitive(PathSensitivity.RELATIVE) val inputSpecRootDirectory = project.objects.property(); @@ -585,10 +585,22 @@ open class GenerateTask @Inject constructor(private val objectFactory: ObjectFac @Suppress("unused") @TaskAction fun doWork() { - inputSpecRootDirectory.ifNotEmpty { inputSpecRootDirectoryValue -> { - inputSpec.set(MergedSpecBuilder(inputSpecRootDirectoryValue, mergedFileName.get()).buildMergedSpec()) - logger.info("Merge input spec would be used - {}", inputSpec.get()) - }} + var resolvedInputSpec = "" + + inputSpec.ifNotEmpty { value -> + resolvedInputSpec = value + } + + remoteInputSpec.ifNotEmpty { value -> + resolvedInputSpec = value + } + + inputSpecRootDirectory.ifNotEmpty { inputSpecRootDirectoryValue -> + run { + resolvedInputSpec = MergedSpecBuilder(inputSpecRootDirectoryValue, mergedFileName.getOrElse("merged")).buildMergedSpec() + logger.info("Merge input spec would be used - {}", resolvedInputSpec) + } + } cleanupOutput.ifNotEmpty { cleanup -> if (cleanup) { @@ -655,6 +667,8 @@ open class GenerateTask @Inject constructor(private val objectFactory: ObjectFac logger.warn("Both inputSpec and remoteInputSpec is specified. The remoteInputSpec will take priority over inputSpec.") } + configurator.setInputSpec(resolvedInputSpec) + // now override with any specified parameters verbose.ifNotEmpty { value -> configurator.setVerbose(value) @@ -668,14 +682,6 @@ open class GenerateTask @Inject constructor(private val objectFactory: ObjectFac configurator.setSkipOverwrite(value ?: false) } - inputSpec.ifNotEmpty { value -> - configurator.setInputSpec(value) - } - - remoteInputSpec.ifNotEmpty { value -> - configurator.setInputSpec(value) - } - generatorName.ifNotEmpty { value -> configurator.setGeneratorName(value) } diff --git a/modules/openapi-generator-gradle-plugin/src/test/kotlin/GenerateTaskDslTest.kt b/modules/openapi-generator-gradle-plugin/src/test/kotlin/GenerateTaskDslTest.kt index a4f288d34a6..30a725f70e0 100644 --- a/modules/openapi-generator-gradle-plugin/src/test/kotlin/GenerateTaskDslTest.kt +++ b/modules/openapi-generator-gradle-plugin/src/test/kotlin/GenerateTaskDslTest.kt @@ -4,7 +4,9 @@ import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome import org.testng.annotations.Test import java.io.File +import java.nio.file.Files.createDirectory import java.nio.file.Files.createTempDirectory +import java.nio.file.Paths import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -88,6 +90,75 @@ class GenerateTaskDslTest : TestBase() { ) } + @Test + fun `openApiGenerate should create an expected file structure from root directory config`() { + val projectFiles = mapOf( + "spec.yaml" to javaClass.classLoader.getResourceAsStream("specs/petstore-v3.0.yaml"), + "spec-2.yaml" to javaClass.classLoader.getResourceAsStream("specs/petstore-v3.1.yaml") + ) + + // Arrange + val buildContents = """ + plugins { + id 'org.openapi.generator' + } + openApiGenerate { + generatorName = "kotlin" + inputSpecRootDirectory = file("specs").absolutePath + outputDir = file("build/kotlin").absolutePath + apiPackage = "org.openapitools.example.api" + invokerPackage = "org.openapitools.example.invoker" + modelPackage = "org.openapitools.example.model" + configOptions = [ + dateLibrary: "java8" + ] + } + """.trimIndent() + val tempContractDirectory: File = createDirectory(Paths.get("${temp.path}/specs")).toFile() + + File(temp, "build.gradle").writeText(buildContents) + projectFiles.forEach { entry -> + val target = File(tempContractDirectory, entry.key) + entry.value?.copyTo(target.outputStream()) + } + + // Act + val result = GradleRunner.create() + .withProjectDir(temp) + .withArguments("openApiGenerate", "--stacktrace") + .withPluginClasspath() + .build() + + // Assert + assertTrue( + result.output.contains("Successfully generated code to"), + "User friendly generate notice is missing." + ) + + listOf( + "build/kotlin/.openapi-generator-ignore", + "build/kotlin/docs/PetsApi.md", + "build/kotlin/docs/Error.md", + "build/kotlin/docs/Pet.md", + "build/kotlin/README.md", + "build/kotlin/build.gradle", + "build/kotlin/.openapi-generator/VERSION", + "build/kotlin/settings.gradle", + "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/api/PetsApi.kt", + "build/kotlin/src/main/kotlin/org/openapitools/client/infrastructure/ApiClient.kt" + ).map { + val f = File(temp, it) + assertTrue(f.exists() && f.isFile, "An expected file was not generated when invoking the generation: $f") + } + + assertEquals( + TaskOutcome.SUCCESS, result.task(":openApiGenerate")?.outcome, + "Expected a successful run, but found ${result.task(":openApiGenerate")?.outcome}" + ) + } + @Test fun `openApiGenerate should create an expected file structure from DSL config`() { // Arrange diff --git a/modules/openapi-generator-gradle-plugin/src/test/resources/specs/petstore-v3.1.yaml b/modules/openapi-generator-gradle-plugin/src/test/resources/specs/petstore-v3.1.yaml new file mode 100644 index 00000000000..0a8d3d15973 --- /dev/null +++ b/modules/openapi-generator-gradle-plugin/src/test/resources/specs/petstore-v3.1.yaml @@ -0,0 +1,109 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /v3/pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /v3/pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string