diff --git a/modules/openapi-generator-sbt-plugin/README.md b/modules/openapi-generator-sbt-plugin/README.md new file mode 100644 index 000000000000..daf21a9d03fe --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/README.md @@ -0,0 +1,122 @@ +# sbt-openapi-generator + +A Sbt plugin to support the OpenAPI generator project + +# Usage + +Add to your `project/plugins.sbt`: + +```sbt +addSbtPlugin("org.openapitools" % "sbt-openapi-generator" % "4.3.0") +``` + +# Configuration + +Configuration based on project module is recommended way to separate specifications by modules. + +You must define one of the settings `openApiInputSpec` or `openApiConfigFile` to able run plugin to generate. + +Settings will be picked up from `openApiConfigFile` first if defined and then will be overwritten with module specified settings + +With the next example module `generated` will be defined as: + +```sbt +lazy val generated = project.in(file("generated")) + .settings( + inConfig(OpenApiCodegen) { + Seq( + openApiInputSpec := "openapi.yaml", + openApiConfigFile := "config.yaml" + ) + } + ) +``` + +There is a helpers to have boolean settings more readable. Instead of `Some(true)` you can do next: +```sbt + openApiValidateSpec := SettingDisabled, + openApiGenerateModelTests := SettingEnabled, +``` +# Execution + +To print all available languages use +```shell script +sbt openApiGenerators +``` + +To run generation process +```shell script +sbt openApiGenerate +``` +or per defined module +```shell script +sbt generated/openApiGenerate +``` + +# Settings + + +| Setting | Type | Description | +|----------|--------|--------------------------| +| openApiGeneratorName | `String` | The name of the generator which will handle codegen. (see \"openApiGenerators\" task) | +| openApiInputSpec| `String` | The Open API 2.0/3.x specification location | +| openApiOutputDir| `String` | The output target directory into which code will be generated | +| openApiConfigFile| `String` | Path to json configuration file | +| openApiAdditionalProperties | `Map[String, String]` | 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 | +| openApiSystemProperties | `Map[String, String]` |Sets specified system properties | +| openApiVerbose | `Option[Boolean]` | The verbosity of generation | +| openApiValidateSpec | `Option[Boolean]` | Whether or not an input specification should be validated upon generation | +| openApiTemplateDir | `String` | The template directory holding a custom template | +| openApiAuth | `String` | Adds authorization headers when fetching the OpenAPI definitions remotely. Pass in a URL-encoded string of name:header with a comma separating multiple values | +| openApiSkipOverwrite | `Option[Boolean]` | Specifies if the existing files should be overwritten during the generation | +| openApiPackageName | `String` | Package for generated classes (where supported) | +| openApiApiPackage | `String` | Package for generated api classes | +| openApiModelPackage | `String` | Package for generated models | +| openApiModelNamePrefix | `String` | Prefix that will be prepended to all model names | +| openApiModelNameSuffix | `String` | Suffix that will be appended to all model names | +| openApiInstantiationTypes | `Map[String, String]` | Sets instantiation type mappings | +| openApiTypeMappings | `Map[String, String]` | Sets mappings between OpenAPI spec types and generated code types | +| openApiServerVariables | `Map[String, String]` | 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 | +| openApiLanguageSpecificPrimitives | `List[String]` | Specifies additional language specific primitive types in the format of type1,type2,type3,type3. For example: String,boolean,Boolean,Double | +| openApiImportMappings | `Map[String, String]` | Specifies mappings between a given class and the import that should be used for that class | +| openApiInvokerPackage | `String` | Root package for generated code | +| openApiGroupId | `String` | groupId in generated pom.xml/build.sbt | +| openApiId | `String` | artifactId in generated pom.xml/build.sbt. This also becomes part of the generated library's filename | +| openApiLibrary | `String` | library template (sub-template) | +| openApiGitHost | `String` |Git host, e.g. gitlab.com | +| openApiGitUserId | `String` | Git user ID, e.g. openapitools | +| openApiGitRepoId | `String` | Git repo ID, e.g. openapi-generator | +| openApiReleaseNote | `String` | Release note, default to 'Minor update' | +| openApiHttpUserAgent | `String` | HTTP user agent, e.g. codegen_csharp_api_client, default to 'OpenAPI-Generator/{packageVersion}}/{language}' | +| openApiReservedWordsMappings | `Map[String, String]` | ]("Specifies how a reserved name should be escaped to | +| openApiIgnoreFileOverride | `String` | Specifies an override location for the .openapi-generator-ignore file. Most useful on initial generation. | +| openApiRemoveOperationIdPrefix | `Option[Boolean]` | Remove prefix of operationId, e.g. config_getId => getId | +| openApiApiFilesConstrainedTo | `List[String]` | Defines which API-related files should be generated. This allows you to create a subset of generated files (or none at all) | +| openApiModelFilesConstrainedTo | `List[String]` | Defines which model-related files should be generated. This allows you to create a subset of generated files (or none at all) | +| openApiSupportingFilesConstrainedTo | `List[String]` | Defines which supporting files should be generated. This allows you to create a subset of generated files (or none at all | +| openApiGenerateModelTests | `Option[Boolean]` | Specifies that model tests are to be generated | +| openApiGenerateModelDocumentation | `Option[Boolean]` | Defines whether or not model-related _documentation_ files should be generated | +| openApiGenerateApiTests | `Option[Boolean]` | Specifies that api tests are to be generated | +| openApiGenerateApiDocumentation | `Option[Boolean]` | Defines whether or not api-related _documentation_ files should be generated | +| openApiWithXml | `Option[Boolean]` | 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 | +| openApiLogToStderr | `Option[Boolean]` | To write all log messages (not just errors) to STDOUT | +| openApiEnablePostProcessFile | `Option[Boolean]` | Enable post-processing file using environment variables | \ +| openApiSkipValidateSpec | `Option[Boolean]` | To skip spec validation. When true, we will skip the default behavior of validating a spec before generation | +| openApiGenerateAliasAsModel | `Option[Boolean]` | Generate model implementation for aliases to map and array schemas | + +# Examples + +Please see [an sbt-test configuration](src/sbt-test) for using the plugin. +Do not run those examples directly, please copy them to separate place first. + +# Contribution and Tests + +Write plugin integration tests under [src/sbt-test](src/sbt-test) + +Execute next to run tests: + +```shell script +sbt scripted +``` + +More information how to write and execute tests [is here](https://www.scala-sbt.org/1.x/docs/Testing-sbt-plugins.html) \ No newline at end of file diff --git a/modules/openapi-generator-sbt-plugin/build.sbt b/modules/openapi-generator-sbt-plugin/build.sbt new file mode 100644 index 000000000000..e9ed485c0540 --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/build.sbt @@ -0,0 +1,51 @@ +lazy val `sbt-openapi-generator` = Project("sbt-openapi-generator", file(".")) +resolvers ++= Seq( + Resolver.sbtPluginRepo("snapshots"), + Resolver.sonatypeRepo("snapshots") +) +scalaVersion := "2.12.10" + +name := "sbt-openapi-generator" +description := """ +This plugin supports common functionality found in Open API Generator CLI as a sbt plugin. + +This gives you the ability to generate client SDKs, documentation, new generators, and to validate Open API 2.0 and 3.x +specifications as part of your build. Other tasks are available as command line tasks. +""" +homepage := Some(url("https://openapi-generator.tech")) + +organization := "org.openapitools" +organizationName := "OpenAPI-Generator Contributors" +organizationHomepage := Some(url("https://github.com/OpenAPITools")) + +licenses += ("The Apache Software License, Version 2.0", url("https://www.apache.org/licenses/LICENSE-2.0.txt")) + +developers += Developer( + id = "openapitools", + name = "OpenAPI-Generator Contributors", + email = "team@openapitools.org", + url = url("https://github.com/OpenAPITools") +) + +scmInfo := Some( + ScmInfo( + browseUrl = url("https://github.com/OpenAPITools/openapi-generator"), + connection = "scm:git:git://github.com/OpenAPITools/openapi-generator.git", + devConnection = "scm:git:ssh://git@github.com:OpenAPITools/openapi-generator.git") +) + +bintrayOrganization := Some("sbt") +bintrayRepository := "sbt-plugins" +bintrayReleaseOnPublish in ThisBuild := false + +crossScalaVersions := Seq(scalaVersion.value, "2.11.12") +crossSbtVersions := List("0.13.17", "1.3.8") + +libraryDependencies += "org.openapitools" % "openapi-generator" % version.value + +enablePlugins(SbtPlugin) + +scriptedLaunchOpts := { + scriptedLaunchOpts.value ++ Seq("-Xmx1024M", "-Dplugin.version=" + version.value) +} +scriptedBufferLog := false diff --git a/modules/openapi-generator-sbt-plugin/project/build.properties b/modules/openapi-generator-sbt-plugin/project/build.properties new file mode 100644 index 000000000000..a919a9b5f46b --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.3.8 diff --git a/modules/openapi-generator-sbt-plugin/project/plugin.sbt b/modules/openapi-generator-sbt-plugin/project/plugin.sbt new file mode 100644 index 000000000000..4f240f5f5676 --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/project/plugin.sbt @@ -0,0 +1,2 @@ +libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value +addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.6") diff --git a/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/OpenApiGeneratorKeys.scala b/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/OpenApiGeneratorKeys.scala new file mode 100644 index 000000000000..9a1ab2af41e3 --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/OpenApiGeneratorKeys.scala @@ -0,0 +1,148 @@ +package sbtopenapigenerator + +import org.openapitools.codegen.CodegenConstants +import sbt.taskKey +import sbt.settingKey +import sbt.File + +trait OpenApiGeneratorKeys { + final val openApiGenerate = taskKey[Seq[File]]("Generate code via Open API Tools Generator for Open API 2.0 or 3.x specification documents.") + final val openApiGenerators = taskKey[Unit]("Print list of available generators") + + final val openApiInputSpec = settingKey[String]("The Open API 2.0/3.x specification location.") + final val openApiOutputDir = settingKey[String]("The output target directory into which code will be generated.") + final val openApiConfigFile = settingKey[String]("Path to json configuration file.\n" + + "File content should be in a json format { \"optionKey\":\"optionValue\", \"optionKey1\":\"optionValue1\"...}\n" + + "Supported options can be different for each language. Run config-help -g {generator name} command for language specific config options.") + final val openApiAdditionalProperties = settingKey[Map[String, String]]("Sets additional properties that can be referenced by the mustache templates in the format of name=value,name=value.\n" + + "You can also have multiple occurrences of this option.") + final val openApiSystemProperties = settingKey[Map[String, String]]("Sets specified system properties.") + + final val openApiVerbose = settingKey[Option[Boolean]]("The verbosity of generation") + final val openApiValidateSpec = settingKey[Option[Boolean]]("Whether or not an input specification should be validated upon generation.") + final val openApiGeneratorName = settingKey[String]("The name of the generator which will handle codegen. (see \"openApiGenerators\" task)") + final val openApiTemplateDir = settingKey[String]("The template directory holding a custom template.") + final val openApiAuth = settingKey[String]("Adds authorization headers when fetching the OpenAPI definitions remotely.\n" + + "Pass in a URL-encoded string of name:header with a comma separating multiple values") + final val openApiSkipOverwrite = settingKey[Option[Boolean]]("Specifies if the existing files should be overwritten during the generation.") + final val openApiPackageName = settingKey[String](CodegenConstants.PACKAGE_NAME_DESC) + final val openApiApiPackage = settingKey[String](CodegenConstants.API_PACKAGE_DESC) + final val openApiModelPackage = settingKey[String](CodegenConstants.MODEL_PACKAGE_DESC) + final val openApiModelNamePrefix = settingKey[String](CodegenConstants.MODEL_NAME_PREFIX_DESC) + final val openApiModelNameSuffix = settingKey[String](CodegenConstants.MODEL_NAME_SUFFIX_DESC) + final val openApiInstantiationTypes = settingKey[Map[String, String]]("Sets instantiation type mappings.") + final val openApiTypeMappings = settingKey[Map[String, String]]("Sets mappings between OpenAPI spec types and generated code types.") + final val openApiServerVariables = settingKey[Map[String, String]]("Sets server variable for server URL template substitution, in the format of name=value,name=value.\n" + + "You can also have multiple occurrences of this option.") + + final val openApiLanguageSpecificPrimitives = settingKey[List[String]]("Specifies additional language specific primitive types in the format of type1,type2,type3,type3. For example: String,boolean,Boolean,Double.") + final val openApiImportMappings = settingKey[Map[String, String]]("Specifies mappings between a given class and the import that should be used for that class.") + final val openApiInvokerPackage = settingKey[String](CodegenConstants.INVOKER_PACKAGE_DESC) + //TODO: change to sbt organization + final val openApiGroupId = settingKey[String](CodegenConstants.GROUP_ID_DESC) + //TODO: change to sbt name + final val openApiId = settingKey[String](CodegenConstants.ARTIFACT_ID_DESC) + + final val openApiLibrary = settingKey[String](CodegenConstants.LIBRARY_DESC) + final val openApiGitHost = settingKey[String](CodegenConstants.GIT_HOST_DESC) + final val openApiGitUserId = settingKey[String](CodegenConstants.GIT_USER_ID_DESC) + final val openApiGitRepoId = settingKey[String](CodegenConstants.GIT_REPO_ID_DESC) + final val openApiReleaseNote = settingKey[String](CodegenConstants.RELEASE_NOTE_DESC) + final val openApiHttpUserAgent = settingKey[String](CodegenConstants.HTTP_USER_AGENT_DESC) + final val openApiReservedWordsMappings = settingKey[Map[String, String]]("Specifies how a reserved name should be escaped to.") + final val openApiIgnoreFileOverride = settingKey[String](CodegenConstants.IGNORE_FILE_OVERRIDE_DESC) + final val openApiRemoveOperationIdPrefix = settingKey[Option[Boolean]]("Remove prefix of operationId, e.g. config_getId => getId") + + /** + * Defines which API-related files should be generated. This allows you to create a subset of generated files (or none at all). + * + * This option enables/disables generation of ALL api-related files. + * + * NOTE: Configuring any one of [apiFilesConstrainedTo], [modelFilesConstrainedTo], or [supportingFilesConstrainedTo] results + * 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]. + */ + final val openApiApiFilesConstrainedTo = settingKey[List[String]]("Defines which API-related files should be generated. This allows you to create a subset of generated files (or none at all).") + + /** + * Defines which model-related files should be generated. This allows you to create a subset of generated files (or none at all). + * + * NOTE: Configuring any one of [apiFilesConstrainedTo], [modelFilesConstrainedTo], or [supportingFilesConstrainedTo] results + * 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]. + */ + final val openApiModelFilesConstrainedTo = settingKey[List[String]]("Defines which model-related files should be generated. This allows you to create a subset of generated files (or none at all).") + + /** + * Defines which supporting files should be generated. This allows you to create a subset of generated files (or none at all). + * + * Supporting files are those related to projects/frameworks which may be modified + * by consumers. + * + * NOTE: Configuring any one of [apiFilesConstrainedTo], [modelFilesConstrainedTo], or [supportingFilesConstrainedTo] results + * 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]. + */ + final val openApiSupportingFilesConstrainedTo = settingKey[List[String]]("Defines which supporting files should be generated. This allows you to create a subset of generated files (or none at all).") + + /** + * Defines whether or not model-related _test_ files should be generated. + * + * This option enables/disables generation of ALL model-related _test_ files. + * + * For more control over generation of individual files, configure an ignore file and + * refer to it via [ignoreFileOverride]. + */ + final val openApiGenerateModelTests = settingKey[Option[Boolean]](CodegenConstants.GENERATE_MODEL_TESTS_DESC) + + /** + * Defines whether or not model-related _documentation_ files should be generated. + * + * This option enables/disables generation of ALL model-related _documentation_ files. + * + * For more control over generation of individual files, configure an ignore file and + * refer to it via [ignoreFileOverride]. + */ + final val openApiGenerateModelDocumentation = settingKey[Option[Boolean]]("Defines whether or not model-related _documentation_ files should be generated.") + + /** + * Defines whether or not api-related _test_ files should be generated. + * + * This option enables/disables generation of ALL api-related _test_ files. + * + * For more control over generation of individual files, configure an ignore file and + * refer to it via [ignoreFileOverride]. + */ + final val openApiGenerateApiTests = settingKey[Option[Boolean]](CodegenConstants.GENERATE_API_TESTS_DESC) + + /** + * Defines whether or not api-related _documentation_ files should be generated. + * + * This option enables/disables generation of ALL api-related _documentation_ files. + * + * For more control over generation of individual files, configure an ignore file and + * refer to it via [ignoreFileOverride]. + */ + final val openApiGenerateApiDocumentation = settingKey[Option[Boolean]]("Defines whether or not api-related _documentation_ files should be generated.") + final val openApiWithXml = settingKey[Option[Boolean]]("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.") + + final val openApiLogToStderr = settingKey[Option[Boolean]]("To write all log messages (not just errors) to STDOUT") + + /** + * To enable the file post-processing hook. This enables executing an external post-processor (usually a linter program). + * This only enables the post-processor. To define the post-processing command, define an environment variable such as + * 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. + */ + final val openApiEnablePostProcessFile = settingKey[Option[Boolean]](CodegenConstants.ENABLE_POST_PROCESS_FILE_DESC) + + final val openApiSkipValidateSpec = settingKey[Option[Boolean]]("To skip spec validation. When true, we will skip the default behavior of validating a spec before generation.") + + /** + * To generate alias (array, list, map) as model. When false, top-level objects defined as array, list, or map will result in those + * 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. + */ + final val openApiGenerateAliasAsModel = settingKey[Option[Boolean]](CodegenConstants.GENERATE_ALIAS_AS_MODEL_DESC) + +} diff --git a/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/OpenApiGeneratorPlugin.scala b/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/OpenApiGeneratorPlugin.scala new file mode 100644 index 000000000000..7a0f1a87f80a --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/OpenApiGeneratorPlugin.scala @@ -0,0 +1,87 @@ +package sbtopenapigenerator + +import sbt._ +import sbt.{Def, ThisBuild} +import sbt.Keys.{baseDirectory, aggregate, _} +import sbt.plugins.JvmPlugin +import sbtopenapigenerator.tasks.{OpenApiGenerateTask, OpenApiGeneratorsTask} + +object OpenApiGeneratorPlugin extends sbt.AutoPlugin + with OpenApiGeneratorsTask + with OpenApiGenerateTask + { + self => + + override def requires: JvmPlugin.type = sbt.plugins.JvmPlugin + + override def trigger: sbt.PluginTrigger = allRequirements + + object autoImport extends OpenApiGeneratorKeys { + + def SettingEnabled: Option[Boolean] = Some(true) + def SettingDisabled: Option[Boolean] = Some(false) + + + } + val out = taskKey[Seq[File]]("Out") + + val OpenApiCodegen = config("openApiCodegen") + + override def globalSettings: Seq[Def.Setting[_]] = Seq( + aggregate in openApiGenerators := false + ) + + private lazy val baseSettings: Seq[sbt.Setting[_]] = Seq[sbt.Setting[_]]( + openApiInputSpec := "", + openApiOutputDir := "", + openApiConfigFile := "", + openApiAdditionalProperties := Map.empty[String, String], + openApiSystemProperties := Map.empty[String, String], + openApiVerbose := None, + openApiValidateSpec := None, + openApiGeneratorName := "", + openApiTemplateDir := "", + openApiAuth := "", + openApiSkipOverwrite := None, + openApiPackageName := "", + openApiApiPackage := "", + openApiModelPackage := "", + openApiModelNamePrefix := "", + openApiModelNameSuffix := "", + openApiInstantiationTypes := Map.empty[String, String], + openApiTypeMappings := Map.empty[String, String], + openApiServerVariables := Map.empty[String, String], + openApiLanguageSpecificPrimitives := List[String](), + openApiImportMappings := Map.empty[String, String], + openApiInvokerPackage := "", + openApiGroupId := "", + openApiId := "", + openApiLibrary := "", + openApiGitHost := "", + openApiGitUserId := "", + openApiGitRepoId := "", + openApiReleaseNote := "", + openApiHttpUserAgent := "", + openApiReservedWordsMappings := Map.empty[String, String], + openApiIgnoreFileOverride := "", + openApiRemoveOperationIdPrefix := None, + openApiApiFilesConstrainedTo := List[String](), + openApiModelFilesConstrainedTo := List[String](), + openApiSupportingFilesConstrainedTo := List[String](), + openApiGenerateModelTests := None, + openApiGenerateModelDocumentation := None, + openApiGenerateApiTests := None, + openApiGenerateApiDocumentation := None, + openApiWithXml := None, + openApiLogToStderr := None, + openApiEnablePostProcessFile := None, + openApiSkipValidateSpec := None, + openApiGenerateAliasAsModel := None, + ) + + override lazy val projectSettings: Seq[Def.Setting[_]] = Seq[sbt.Setting[_]]( + openApiGenerators := openApiGeneratorsTask.value, + openApiGenerate := openApiGenerateTask.value, + ) ++ baseSettings + +} diff --git a/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/tasks/OpenApiGenerateTask.scala b/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/tasks/OpenApiGenerateTask.scala new file mode 100644 index 000000000000..a83b89694fcd --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/tasks/OpenApiGenerateTask.scala @@ -0,0 +1,250 @@ +package sbtopenapigenerator.tasks + +import org.openapitools.codegen.{CodegenConstants, DefaultGenerator} +import org.openapitools.codegen.config.{CodegenConfigurator, GlobalSettings} +import sbt.{Def, _} +import sbt.Keys._ +import sbtopenapigenerator.OpenApiGeneratorKeys + +import scala.collection.JavaConverters._ +import scala.util.{Failure, Success, Try} + +trait OpenApiGenerateTask extends OpenApiGeneratorKeys { + + protected[this] def openApiGenerateTask: Def.Initialize[Task[Seq[File]]] = Def.task { + + val logger = sbt.Keys.streams.value.log + + if (openApiConfigFile.value.isEmpty && openApiGeneratorName.value.isEmpty) { + Seq() + } else { + val configurator: CodegenConfigurator = if (openApiConfigFile.value.nonEmpty) { + val config = openApiConfigFile.value + logger.info(s"read configuration from $config") + CodegenConfigurator.fromFile(config) + } else new CodegenConfigurator() + + if (openApiSupportingFilesConstrainedTo.value.nonEmpty) { + GlobalSettings.setProperty(CodegenConstants.SUPPORTING_FILES, openApiSupportingFilesConstrainedTo.value.mkString(",")) + } else { + GlobalSettings.clearProperty(CodegenConstants.SUPPORTING_FILES) + } + + if (openApiModelFilesConstrainedTo.value.nonEmpty) { + GlobalSettings.setProperty(CodegenConstants.MODELS, openApiModelFilesConstrainedTo.value.mkString(",")) + } else { + GlobalSettings.clearProperty(CodegenConstants.MODELS) + } + + if (openApiApiFilesConstrainedTo.value.nonEmpty) { + GlobalSettings.setProperty(CodegenConstants.APIS, openApiApiFilesConstrainedTo.value.mkString(",")) + } else { + GlobalSettings.clearProperty(CodegenConstants.APIS) + } + + openApiGenerateApiDocumentation.value.foreach { value => + GlobalSettings.setProperty(CodegenConstants.API_DOCS, value.toString) + } + + openApiGenerateModelDocumentation.value.foreach { value => + GlobalSettings.setProperty(CodegenConstants.MODEL_DOCS, value.toString) + } + + openApiGenerateModelTests.value.foreach { value => + GlobalSettings.setProperty(CodegenConstants.MODEL_TESTS, value.toString) + } + + openApiGenerateApiTests.value.foreach { value => + GlobalSettings.setProperty(CodegenConstants.API_TESTS, value.toString) + } + + openApiWithXml.value.foreach { value => + GlobalSettings.setProperty(CodegenConstants.WITH_XML, value.toString) + } + + // now override with any specified parameters + openApiVerbose.value.foreach { value => + configurator.setVerbose(value) + } + openApiValidateSpec.value.foreach { value => + configurator.setValidateSpec(value) + } + + openApiSkipOverwrite.value.foreach { value => + configurator.setSkipOverwrite(value) + } + + if (openApiInputSpec.value.nonEmpty) { + configurator.setInputSpec(openApiInputSpec.value) + } + + + if (openApiGeneratorName.value.nonEmpty) { + configurator.setGeneratorName(openApiGeneratorName.value) + } + + if (openApiOutputDir.value.nonEmpty) { + configurator.setOutputDir(openApiOutputDir.value) + } + + if (openApiAuth.value.nonEmpty) { + configurator.setAuth(openApiAuth.value) + } + + if (openApiTemplateDir.value.nonEmpty) { + configurator.setTemplateDir(openApiTemplateDir.value) + } + + if (openApiPackageName.value.nonEmpty) { + configurator.setPackageName(openApiPackageName.value) + } + + if (openApiApiPackage.value.nonEmpty) { + configurator.setApiPackage(openApiApiPackage.value) + } + + if (openApiModelPackage.value.nonEmpty) { + configurator.setModelPackage(openApiModelPackage.value) + } + + if (openApiModelNamePrefix.value.nonEmpty) { + configurator.setModelNamePrefix(openApiModelNamePrefix.value) + } + + if (openApiModelNameSuffix.value.nonEmpty) { + configurator.setModelNameSuffix(openApiModelNameSuffix.value) + } + + if (openApiInvokerPackage.value.nonEmpty) { + configurator.setInvokerPackage(openApiInvokerPackage.value) + } + + if (openApiGroupId.value.nonEmpty) { + configurator.setGroupId(openApiGroupId.value) + } + + if (openApiId.value.nonEmpty) { + configurator.setArtifactId(openApiId.value) + } + + if ((version in openApiGenerate).value.nonEmpty) { + configurator.setArtifactVersion(version.value) + } + + if (openApiLibrary.value.nonEmpty) { + configurator.setLibrary(openApiLibrary.value) + } + + if (openApiGitHost.value.nonEmpty) { + configurator.setGitHost(openApiGitHost.value) + } + + if (openApiGitUserId.value.nonEmpty) { + configurator.setGitUserId(openApiGitUserId.value) + } + + + if (openApiGitRepoId.value.nonEmpty) { + configurator.setGitRepoId(openApiGitRepoId.value) + } + + if (openApiReleaseNote.value.nonEmpty) { + configurator.setReleaseNote(openApiReleaseNote.value) + } + + if (openApiHttpUserAgent.value.nonEmpty) { + configurator.setHttpUserAgent(openApiHttpUserAgent.value) + } + + if (openApiIgnoreFileOverride.value.nonEmpty) { + configurator.setIgnoreFileOverride(openApiIgnoreFileOverride.value) + } + + openApiRemoveOperationIdPrefix.value.foreach { value => + configurator.setRemoveOperationIdPrefix(value) + } + + openApiLogToStderr.value.foreach { value => + configurator.setLogToStderr(value) + } + + openApiEnablePostProcessFile.value.foreach { value => + configurator.setEnablePostProcessFile(value) + } + + openApiSkipValidateSpec.value.foreach { value => + configurator.setValidateSpec(!value) + } + + openApiGenerateAliasAsModel.value.foreach { value => + configurator.setGenerateAliasAsModel(value) + } + + if (openApiSystemProperties.value.nonEmpty) { + openApiSystemProperties.value.foreach { entry => + configurator.addSystemProperty(entry._1, entry._2) + } + } + + if (openApiInstantiationTypes.value.nonEmpty) { + openApiInstantiationTypes.value.foreach { entry => + configurator.addInstantiationType(entry._1, entry._2) + } + } + + if (openApiImportMappings.value.nonEmpty) { + openApiImportMappings.value.foreach { entry => + configurator.addImportMapping(entry._1, entry._2) + } + } + + if (openApiTypeMappings.value.nonEmpty) { + openApiTypeMappings.value.foreach { entry => + configurator.addTypeMapping(entry._1, entry._2) + } + } + + if (openApiAdditionalProperties.value.nonEmpty) { + openApiAdditionalProperties.value.foreach { entry => + configurator.addAdditionalProperty(entry._1, entry._2) + } + } + + if (openApiServerVariables.value.nonEmpty) { + openApiServerVariables.value.foreach { entry => + configurator.addServerVariable(entry._1, entry._2) + } + } + + if (openApiLanguageSpecificPrimitives.value.nonEmpty) { + openApiLanguageSpecificPrimitives.value.foreach { it => + configurator.addLanguageSpecificPrimitive(it) + } + } + + if (openApiReservedWordsMappings.value.nonEmpty) { + openApiReservedWordsMappings.value.foreach { entry => + configurator.addAdditionalReservedWordMapping(entry._1, entry._2) + } + } + + Try(configurator.toClientOptInput) match { + case Success(clientOptInput) => + Try { + val gen = new DefaultGenerator().opts(clientOptInput) + val res = gen.generate().asScala + + logger.out(s"Successfully generated code to ${clientOptInput.getConfig.getOutputDir}") + res + } match { + case Success(value) => value + case Failure(ex) => + throw new Exception("Code generation failed.", ex) + } + case Failure(ex) => + logger.info(ex.getMessage) + Seq() + } + } + } +} diff --git a/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/tasks/OpenApiGeneratorsTask.scala b/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/tasks/OpenApiGeneratorsTask.scala new file mode 100644 index 000000000000..4f466c13c42c --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/main/scala/sbtopenapigenerator/tasks/OpenApiGeneratorsTask.scala @@ -0,0 +1,46 @@ +package sbtopenapigenerator.tasks + +import org.openapitools.codegen.{CodegenConfigLoader, CodegenType} +import org.openapitools.codegen.meta.Stability +import sbt.{Def, Task} +import scala.collection.JavaConverters._ + + +trait OpenApiGeneratorsTask { + + protected[this] def openApiGeneratorsTask: Def.Initialize[Task[Unit]] = Def.task { + val generators = CodegenConfigLoader.getAll.asScala + val types = CodegenType.values() + + val stabilities = Stability.values().filterNot { + _ == Stability.DEPRECATED + } + + val out = new StringBuilder + + out ++= "The following generators are available:" + System.lineSeparator() + for (t <- types) { + val filteredGenerators = generators.filter(_.getTag == t).sortBy(_.getName) + if (filteredGenerators.nonEmpty) { + out ++= s" $t generators:" + System.lineSeparator() + filteredGenerators.foreach { + generator => + val meta = generator.getGeneratorMetadata + val stability = meta.getStability + val include = stabilities.contains(stability) + if (include) { + out ++= s" - ${generator.getName}" + if (stability != Stability.STABLE) { + out ++= s" (${stability.value()})" + } + out ++= System.lineSeparator() + } + } + } + } + out ++= System.lineSeparator() + out ++= System.lineSeparator() + + println(out) + } +} diff --git a/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/build.sbt b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/build.sbt new file mode 100644 index 000000000000..2a8e9c2042f8 --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/build.sbt @@ -0,0 +1,18 @@ +scalaVersion := "2.12.10" + +enablePlugins(OpenApiGeneratorPlugin) + +lazy val generated = project.in(file("generated")) + .settings( + openApiInputSpec := "openapi.yaml", + openApiConfigFile := "config.yaml", + openApiValidateSpec := SettingDisabled, + openApiGenerateModelTests := SettingEnabled + ) + +lazy val root = (project in file(".")) + .settings( + name := "openapi-generator-example" + ) + .dependsOn(generated) + .aggregate(generated) diff --git a/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/config.yaml b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/config.yaml new file mode 100644 index 000000000000..5825894ef1d1 --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/config.yaml @@ -0,0 +1,3 @@ +apiPackage: "petstore" +generatorName: "scala-akka" +outputDir : "generated" diff --git a/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/openapi.yaml b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/openapi.yaml new file mode 100644 index 000000000000..55363f77661a --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/openapi.yaml @@ -0,0 +1,721 @@ +openapi: 3.0.1 +info: + title: Swagger Petstore + description: 'This is a sample server Petstore server. You can find out more about Swagger + at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For + this sample, you can use the api key `special-key` to test the authorization filters.' + termsOfService: http://swagger.io/terms/ + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + version: 1.0.0 +externalDocs: + description: Find out more about Swagger + url: http://swagger.io +servers: +- url: https://petstore.swagger.io/v2 +- url: http://petstore.swagger.io/v2 +tags: +- name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: http://swagger.io +- name: store + description: Access to Petstore orders +- name: user + description: Operations about user + externalDocs: + description: Find out more about our store + url: http://swagger.io +paths: + /pet: + put: + tags: + - pet + summary: Update an existing pet + operationId: updatePet + requestBody: + description: Pet object that needs to be added to the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + required: true + responses: + 400: + description: Invalid ID supplied + content: {} + 404: + description: Pet not found + content: {} + 405: + description: Validation exception + content: {} + security: + - petstore_auth: + - write:pets + - read:pets + post: + tags: + - pet + summary: Add a new pet to the store + operationId: addPet + requestBody: + description: Pet object that needs to be added to the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + required: true + responses: + 405: + description: Invalid input + content: {} + security: + - petstore_auth: + - write:pets + - read:pets + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + style: form + explode: true + schema: + type: array + items: + type: string + default: available + enum: + - available + - pending + - sold + responses: + 200: + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + 400: + description: Invalid status value + content: {} + security: + - petstore_auth: + - write:pets + - read:pets + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags + description: Muliple tags can be provided with comma separated strings. Use tag1, + tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - name: tags + in: query + description: Tags to filter by + required: true + style: form + explode: true + schema: + type: array + items: + type: string + responses: + 200: + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + 400: + description: Invalid tag value + content: {} + deprecated: true + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}: + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + 200: + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + 400: + description: Invalid ID supplied + content: {} + 404: + description: Pet not found + content: {} + security: + - api_key: [] + post: + tags: + - pet + summary: Updates a pet in the store with form data + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/x-www-form-urlencoded: + schema: + properties: + name: + type: string + description: Updated name of the pet + status: + type: string + description: Updated status of the pet + responses: + 405: + description: Invalid input + content: {} + security: + - petstore_auth: + - write:pets + - read:pets + delete: + tags: + - pet + summary: Deletes a pet + operationId: deletePet + parameters: + - name: api_key + in: header + schema: + type: string + - name: petId + in: path + description: Pet id to delete + required: true + schema: + type: integer + format: int64 + responses: + 400: + description: Invalid ID supplied + content: {} + 404: + description: Pet not found + content: {} + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}/uploadImage: + post: + tags: + - pet + summary: uploads an image + operationId: uploadFile + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + schema: + type: integer + format: int64 + requestBody: + content: + multipart/form-data: + schema: + properties: + additionalMetadata: + type: string + description: Additional data to pass to server + file: + type: string + description: file to upload + format: binary + responses: + 200: + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + security: + - petstore_auth: + - write:pets + - read:pets + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet + operationId: placeOrder + requestBody: + description: order placed for purchasing the pet + content: + '*/*': + schema: + $ref: '#/components/schemas/Order' + required: true + responses: + 200: + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + 400: + description: Invalid Order + content: {} + /store/order/{orderId}: + get: + tags: + - store + summary: Find purchase order by ID + description: For valid response try integer IDs with value >= 1 and <= 10. Other + values will generated exceptions + operationId: getOrderById + parameters: + - name: orderId + in: path + description: ID of pet that needs to be fetched + required: true + schema: + maximum: 10.0 + minimum: 1.0 + type: integer + format: int64 + responses: + 200: + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + 400: + description: Invalid ID supplied + content: {} + 404: + description: Order not found + content: {} + delete: + tags: + - store + summary: Delete purchase order by ID + description: For valid response try integer IDs with positive integer value. Negative + or non-integer values will generate API errors + operationId: deleteOrder + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + schema: + minimum: 1.0 + type: integer + format: int64 + responses: + 400: + description: Invalid ID supplied + content: {} + 404: + description: Order not found + content: {} + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + requestBody: + description: Created user object + content: + '*/*': + schema: + $ref: '#/components/schemas/User' + required: true + responses: + default: + description: successful operation + content: {} + /user/createWithArray: + post: + tags: + - user + summary: Creates list of users with given input array + operationId: createUsersWithArrayInput + requestBody: + description: List of user object + content: + '*/*': + schema: + type: array + items: + $ref: '#/components/schemas/User' + required: true + responses: + default: + description: successful operation + content: {} + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + operationId: createUsersWithListInput + requestBody: + description: List of user object + content: + '*/*': + schema: + type: array + items: + $ref: '#/components/schemas/User' + required: true + responses: + default: + description: successful operation + content: {} + /user/login: + get: + tags: + - user + summary: Logs user into the system + operationId: loginUser + parameters: + - name: username + in: query + description: The user name for login + required: true + schema: + type: string + - name: password + in: query + description: The password for login in clear text + required: true + schema: + type: string + responses: + 200: + description: successful operation + headers: + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + type: integer + format: int32 + X-Expires-After: + description: date in UTC when token expires + schema: + type: string + format: date-time + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + 400: + description: Invalid username/password supplied + content: {} + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + operationId: logoutUser + responses: + default: + description: successful operation + content: {} + /user/{username}: + get: + tags: + - user + summary: Get user by user name + operationId: getUserByName + parameters: + - name: username + in: path + description: 'The name that needs to be fetched. Use user1 for testing. ' + required: true + schema: + type: string + responses: + 200: + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/User' + application/json: + schema: + $ref: '#/components/schemas/User' + 400: + description: Invalid username supplied + content: {} + 404: + description: User not found + content: {} + put: + tags: + - user + summary: Updated user + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - name: username + in: path + description: name that need to be updated + required: true + schema: + type: string + requestBody: + description: Updated user object + content: + '*/*': + schema: + $ref: '#/components/schemas/User' + required: true + responses: + 400: + description: Invalid user supplied + content: {} + 404: + description: User not found + content: {} + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + schema: + type: string + responses: + 400: + description: Invalid username supplied + content: {} + 404: + description: User not found + content: {} +components: + schemas: + Order: + type: object + properties: + id: + type: integer + format: int64 + petId: + type: integer + format: int64 + quantity: + type: integer + format: int32 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + complete: + type: boolean + default: false + xml: + name: Order + Category: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Category + User: + type: object + properties: + id: + type: integer + format: int64 + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + userStatus: + type: integer + description: User Status + format: int32 + xml: + name: User + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Pet: + required: + - name + - photoUrls + type: object + properties: + id: + type: integer + format: int64 + category: + $ref: '#/components/schemas/Category' + name: + type: string + example: doggie + photoUrls: + type: array + xml: + name: photoUrl + wrapped: true + items: + type: string + tags: + type: array + xml: + name: tag + wrapped: true + items: + $ref: '#/components/schemas/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + ApiResponse: + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: http://petstore.swagger.io/oauth/dialog + scopes: + write:pets: modify pets in your account + read:pets: read your pets + api_key: + type: apiKey + name: api_key + in: header diff --git a/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/project/build.properties b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/project/build.properties new file mode 100644 index 000000000000..a919a9b5f46b --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.3.8 diff --git a/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/project/plugin.sbt b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/project/plugin.sbt new file mode 100644 index 000000000000..6e42bf98ede2 --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/project/plugin.sbt @@ -0,0 +1,8 @@ +resolvers ++= Seq( + Resolver.sonatypeRepo("snapshots") +) +sys.props.get("plugin.version") match { + case Some(x) => addSbtPlugin("org.openapitools" % "sbt-openapi-generator" % x) + case _ => + throw new Exception("The system property 'plugin.version' is not defined.") +} \ No newline at end of file diff --git a/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/src/main/scala/.swagger-codegen-ignore b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/src/main/scala/.swagger-codegen-ignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/src/main/scala/Main.scala b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/src/main/scala/Main.scala new file mode 100644 index 000000000000..25b0d070851b --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/src/main/scala/Main.scala @@ -0,0 +1,5 @@ +package simple.src.main.scala + +object Main extends App { + println("hello world") +} diff --git a/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/test b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/test new file mode 100644 index 000000000000..440749b3d0b9 --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/test @@ -0,0 +1,5 @@ +> openApiGenerators +> openApiGenerate +$ exists generated/build.sbt +> reload +> compile diff --git a/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/version.sbt b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/version.sbt new file mode 100644 index 000000000000..652457f75774 --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/src/sbt-test/sbt-openapi-generator/simple/version.sbt @@ -0,0 +1 @@ +version := "0.1.0" \ No newline at end of file diff --git a/modules/openapi-generator-sbt-plugin/version.sbt b/modules/openapi-generator-sbt-plugin/version.sbt new file mode 100644 index 000000000000..09f80f0152ee --- /dev/null +++ b/modules/openapi-generator-sbt-plugin/version.sbt @@ -0,0 +1 @@ +version in ThisBuild := "4.3.0-SNAPSHOT" \ No newline at end of file