Skip to content

Commit

Permalink
Support reading from a configuration file. (#2)
Browse files Browse the repository at this point in the history
* Support reading from a configuration file.

* update docs for naming conventions
  • Loading branch information
jrouly authored Feb 23, 2021
1 parent bdb19c8 commit c75bcd6
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 82 deletions.
44 changes: 38 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,51 @@ Invoke one of the provided tasks:
> openApiStyleValidationResult
```

# sbt keys
## Configuration

You may specify [openapi-style-validator](https://github.com/OpenAPITools/openapi-style-validator) settings either in a configuration file or directly in the sbt build definition using the provided keys.

### Using a configuration file

Supported formats include Java properties, [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md) and JSON.

Specify the config file in `build.sbt`:
```sbt
openApiStyleConfig := Some(file("openapi-style-validator.conf"))
```

Specify configurations in the config file:
```hocon
validateNaming = true
pathNamingConvention = UnderscoreCase
// etc.
```

Keys are the same as in [openapi-style-validator](https://github.com/OpenAPITools/openapi-style-validator).

### Directly in sbt

You may also specify configuration settings directly in `build.sbt` via the provided keys:
```sbt
openApiStyleValidateNaming := Some(true)
openApiStylePathNamingConvention := Some(NamingConvention.UnderscoreCase)
// etc.
```

## sbt keys

| Key | Type | Description |
| ------- | ---- | ----------- |
| --- | ---- | ----------- |
| `openApiStyleConfig` | `SettingKey[Option[File]]` | OpenAPI Style Validator configuration file. Defaults to `None`. |
| `openApiStyleSpec` | `TaskKey[File]` | OpenAPI specification file. |
| `openApiStyleValidate` | `TaskKey[Unit]` | Validates OpenAPI specification file: success or failure. |
| `openApiStyleValidationResult` | `TaskKey[Seq[String]]` | Validates OpenAPI specification file: evaluates to a list of detailed error messages. |
| `openApiStyleValidatorParameters` | `SettingKey[ValidatorParameters]` | OpenAPI Style Validator parameters. |
| `openApiStyleValidatorParameters` | `SettingKey[ValidatorParameters]` | OpenAPI Style Validator parameters, typically set using configuration keys or a configuration file. |

The following configurations are supported from [OpenAPI Style Validator](https://github.com/OpenAPITools/openapi-style-validator):
The following configuration keys are supported:

| Key | Type | Description |
| ------- | ---- | ----------- |
| --- | ---- | ----------- |
| `openApiStyleValidateInfoLicense` | `SettingKey[Option[Boolean]]` | Ensures that there is a license section in the info section. |
| `openApiStyleValidateInfoDescription` | `SettingKey[Option[Boolean]]` | Ensures that there is a description attribute in the info section. |
| `openApiStyleValidateInfoContact` | `SettingKey[Option[Boolean]]` | Ensures that there is a contact section in the info section. |
Expand All @@ -49,7 +81,7 @@ The following configurations are supported from [OpenAPI Style Validator](https:
| `openApiStyleValidateOperationSummary` | `SettingKey[Option[Boolean]]` | Ensures that there is a summary for each operation. |
| `openApiStyleValidateModelPropertiesExample` | `SettingKey[Option[Boolean]]` | Ensures that the properties of the Schemas have an example value defined. |
| `openApiStyleValidateNaming` | `SettingKey[Option[Boolean]]` | Ensures the names follow a given naming convention. |
| `openApiStyleIgnoreHeaderXNaming` | `SettingKey[Option[Boolean]]` | Exclude from validation header parameters starting with x-. |
| `openApiStyleIgnoreHeaderXNaming` | `SettingKey[Option[Boolean]]` | Exclude from validation header parameters starting with `x-`. |
| `openApiStylePathNamingConvention` | `SettingKey[Option[NamingConvention]]` | Naming convention for paths. |
| `openApiStyleParameterNamingConvention` | `SettingKey[Option[NamingConvention]]` | Naming convention for parameters. |
| `openApiStyleHeaderNamingConvention` | `SettingKey[Option[NamingConvention]]` | Naming convention for headers. |
Expand Down
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ crossSbtVersions := List("0.13.18", "1.4.6")
libraryDependencies ++= Seq(
"org.openapitools.openapistylevalidator" % "openapi-style-validator-lib" % "1.5",
"io.swagger.parser.v3" % "swagger-parser" % "2.0.24",
"org.openapitools.empoa" % "empoa-swagger-core" % "2.0.0"
"org.openapitools.empoa" % "empoa-swagger-core" % "2.0.0",
"com.typesafe" % "config" % "1.4.1"
)

publishMavenStyle := false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import sbt._

trait OpenApiStyleKeys {

final val openApiStyleConfig = settingKey[Option[File]]("OpenAPI Style Validator configuration file.")
final val openApiStyleSpec = taskKey[File]("OpenAPI specification file.")
final val openApiStyleValidate = taskKey[Unit]("Validates OpenAPI specification file: success or failure.")
final val openApiStyleValidationResult = taskKey[Seq[String]]("Validates OpenAPI specification file: evaluates to a list of detailed error messages.")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,43 @@
package org.openapitools.openapistylevalidator.sbt.plugin

import io.swagger.v3.parser.OpenAPIV3Parser
import org.openapitools.empoa.swagger.core.internal.SwAdapter
import org.openapitools.openapistylevalidator.OpenApiSpecStyleValidator
import org.openapitools.openapistylevalidator.ValidatorParameters.NamingConvention
import org.openapitools.openapistylevalidator.sbt.plugin.tasks.OpenApiStyleValidatorParametersTask
import org.openapitools.openapistylevalidator.styleerror.StyleError
import org.openapitools.openapistylevalidator.ValidatorParameters
import org.openapitools.openapistylevalidator.sbt.plugin.tasks._
import sbt._
import sbt.plugins.JvmPlugin

import java.util.function.Consumer
import scala.collection.mutable.ListBuffer

object OpenApiStylePlugin
extends sbt.AutoPlugin
with OpenApiStyleKeys
with OpenApiStyleValidatorParametersTask {
with OpenApiStyleValidatorParametersTask
with OpenApiStyleValidationResultTask
with OpenApiStyleValidateTask {

override def requires: JvmPlugin.type = sbt.plugins.JvmPlugin

override def trigger: sbt.PluginTrigger = noTrigger

object autoImport extends OpenApiStyleKeys
object autoImport extends OpenApiStyleKeys {

// Aliases for convenience.
object NamingConvention {
import ValidatorParameters.{NamingConvention => ValidatorNamingConvention}

val UnderscoreCase = ValidatorNamingConvention.UnderscoreCase
val UnderscoreUpperCase = ValidatorNamingConvention.UnderscoreUpperCase
val CamelCase = ValidatorNamingConvention.CamelCase
val HyphenCase = ValidatorNamingConvention.HyphenCase
}

}

override lazy val projectSettings: Seq[Def.Setting[_]] = pluginSettings ++ defaultSettings

private lazy val pluginSettings: Seq[Def.Setting[_]] = Seq(
openApiStyleSpec := sys.error("openApiStyleFile is undefined. Did you forget to set it?"),
openApiStyleValidatorParameters := openApiStyleValidatorParametersTask().value,
openApiStyleValidationResult := {
val parameters = openApiStyleValidatorParameters.value

val swaggerOpenApiParser = new OpenAPIV3Parser()
val swaggerOpenApi = swaggerOpenApiParser.read(openApiStyleSpec.value.getAbsolutePath)

val openApi = SwAdapter.toOpenAPI(swaggerOpenApi)
val validator = new OpenApiSpecStyleValidator(openApi)

val errors = ListBuffer.empty[String]
validator.validate(parameters).forEach(new Consumer[StyleError] {
override def accept(error: StyleError): Unit = errors += error.toString
})
errors.toList
},
openApiStyleValidate := {
val errors = openApiStyleValidationResult.value
if (errors.nonEmpty) {
val report = errors.mkString("\n")
sys.error("OpenAPI specification style validation failed.\n" + report)
} else ()
}
openApiStyleConfig := None,
openApiStyleValidationResult := openApiStyleValidationResultTask().value,
openApiStyleValidate := openApiStyleValidateTask().value,
openApiStyleValidatorParameters := openApiStyleValidatorParametersTask().value
)

private lazy val defaultSettings: Seq[Def.Setting[_]] = Seq(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.openapitools.openapistylevalidator.sbt.plugin.config

import com.typesafe.config.Config

private[plugin] object ConfigExtension {

implicit class RichConfig(val underlying: Config) extends AnyVal {
def getOptionalBoolean(path: String): Option[Boolean] = {
if (underlying.hasPath(path)) {
Some(underlying.getBoolean(path))
} else {
None
}
}

def getOptionalString(path: String): Option[String] = {
if (underlying.hasPath(path)) {
Some(underlying.getString(path))
} else {
None
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.openapitools.openapistylevalidator.sbt.plugin.tasks

import org.openapitools.openapistylevalidator.sbt.plugin.OpenApiStyleKeys
import sbt.Keys._
import sbt.{Def, Task}

trait OpenApiStyleValidateTask extends OpenApiStyleKeys {

def openApiStyleValidateTask(): Def.Initialize[Task[Unit]] = Def.task {
val log = streams.value.log
val errors = openApiStyleValidationResult.value

if (errors.nonEmpty) {
val report = errors.mkString("\n")
sys.error("OpenAPI specification style validation failed.\n" + report)
} else log.info("OpenAPI specification style validation passed.")
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.openapitools.openapistylevalidator.sbt.plugin.tasks

import io.swagger.v3.parser.OpenAPIV3Parser
import org.openapitools.empoa.swagger.core.internal.SwAdapter
import org.openapitools.openapistylevalidator.OpenApiSpecStyleValidator
import org.openapitools.openapistylevalidator.sbt.plugin.OpenApiStyleKeys
import org.openapitools.openapistylevalidator.styleerror.StyleError
import sbt.{Def, Task}

import java.util.function.Consumer
import scala.collection.mutable.ListBuffer

trait OpenApiStyleValidationResultTask extends OpenApiStyleKeys {

def openApiStyleValidationResultTask(): Def.Initialize[Task[List[String]]] = Def.task {
val parameters = openApiStyleValidatorParameters.value

val swaggerOpenApiParser = new OpenAPIV3Parser()
val swaggerOpenApi = swaggerOpenApiParser.read(openApiStyleSpec.value.getAbsolutePath)

val openApi = SwAdapter.toOpenAPI(swaggerOpenApi)
val validator = new OpenApiSpecStyleValidator(openApi)

val errors = ListBuffer.empty[String]
validator.validate(parameters).forEach(new Consumer[StyleError] {
override def accept(error: StyleError): Unit = errors += error.toString
})
errors.toList
}

}
Original file line number Diff line number Diff line change
@@ -1,70 +1,101 @@
package org.openapitools.openapistylevalidator.sbt.plugin.tasks

import com.typesafe.config.ConfigFactory
import org.openapitools.openapistylevalidator.ValidatorParameters
import org.openapitools.openapistylevalidator.ValidatorParameters.NamingConvention
import org.openapitools.openapistylevalidator.sbt.plugin.OpenApiStyleKeys
import org.openapitools.openapistylevalidator.sbt.plugin.config.ConfigExtension._
import sbt._

trait OpenApiStyleValidatorParametersTask extends OpenApiStyleKeys {

def openApiStyleValidatorParametersTask(): Def.Initialize[ValidatorParameters] = Def.setting {
val parameters = new ValidatorParameters()

openApiStyleValidateInfoLicense.value.foreach { setting =>
parameters.setValidateInfoLicense(setting)
}
openApiStyleConfig.value.foreach { file =>
val config = ConfigFactory.parseFile(file)

openApiStyleValidateInfoDescription.value.foreach { setting =>
parameters.setValidateInfoDescription(setting)
}
config
.getOptionalBoolean("validateInfoLicense")
.foreach(parameters.setValidateInfoLicense)

openApiStyleValidateInfoContact.value.foreach { setting =>
parameters.setValidateInfoContact(setting)
}
config
.getOptionalBoolean("validateInfoDescription")
.foreach(parameters.setValidateInfoDescription)

openApiStyleValidateOperationId.value.foreach { setting =>
parameters.setValidateOperationOperationId(setting)
}
config
.getOptionalBoolean("validateInfoContact")
.foreach(parameters.setValidateInfoContact)

openApiStyleValidateOperationDescription.value.foreach { setting =>
parameters.setValidateOperationDescription(setting)
}
config
.getOptionalBoolean("validateOperationId")
.foreach(parameters.setValidateOperationOperationId)

openApiStyleValidateOperationTag.value.foreach { setting =>
parameters.setValidateOperationTag(setting)
}
config
.getOptionalBoolean("validateOperationDescription")
.foreach(parameters.setValidateOperationDescription)

openApiStyleValidateOperationSummary.value.foreach { setting =>
parameters.setValidateOperationSummary(setting)
}
config
.getOptionalBoolean("validateOperationTag")
.foreach(parameters.setValidateOperationTag)

openApiStyleValidateModelPropertiesExample.value.foreach { setting =>
parameters.setValidateModelPropertiesExample(setting)
}
config
.getOptionalBoolean("validateOperationSummary")
.foreach(parameters.setValidateOperationSummary)

openApiStyleValidateNaming.value.foreach { setting =>
parameters.setValidateNaming(setting)
}
config
.getOptionalBoolean("validateModelPropertiesExample")
.foreach(parameters.setValidateModelPropertiesExample)

openApiStyleIgnoreHeaderXNaming.value.foreach { setting =>
parameters.setIgnoreHeaderXNaming(setting)
}
config
.getOptionalBoolean("validateNaming")
.foreach(parameters.setValidateNaming)

openApiStylePathNamingConvention.value.foreach { setting =>
parameters.setPathNamingConvention(setting)
}
config
.getOptionalBoolean("ignoreHeaderXNaming")
.foreach(parameters.setIgnoreHeaderXNaming)

openApiStyleParameterNamingConvention.value.foreach { setting =>
parameters.setParameterNamingConvention(setting)
}
config
.getOptionalString("pathNamingConvention")
.map(NamingConvention.valueOf)
.foreach(parameters.setPathNamingConvention)

openApiStyleHeaderNamingConvention.value.foreach { setting =>
parameters.setHeaderNamingConvention(setting)
}
config
.getOptionalString("parameterNamingConvention")
.map(NamingConvention.valueOf)
.foreach(parameters.setParameterNamingConvention)

openApiStylePropertyNamingConvention.value.foreach { setting =>
parameters.setParameterNamingConvention(setting)
config
.getOptionalString("headerNamingConvention")
.map(NamingConvention.valueOf)
.foreach(parameters.setHeaderNamingConvention)

config
.getOptionalString("propertyNamingConvention")
.map(NamingConvention.valueOf)
.foreach(parameters.setPropertyNamingConvention)
}

openApiStyleValidateInfoLicense.value.foreach(parameters.setValidateInfoLicense)
openApiStyleValidateInfoDescription.value.foreach(parameters.setValidateInfoDescription)
openApiStyleValidateInfoContact.value.foreach(parameters.setValidateInfoContact)

openApiStyleValidateOperationId.value.foreach(parameters.setValidateOperationOperationId)
openApiStyleValidateOperationDescription.value.foreach(parameters.setValidateOperationDescription)
openApiStyleValidateOperationTag.value.foreach(parameters.setValidateOperationTag)
openApiStyleValidateOperationSummary.value.foreach(parameters.setValidateOperationSummary)

openApiStyleValidateModelPropertiesExample.value.foreach(parameters.setValidateModelPropertiesExample)

openApiStyleValidateNaming.value.foreach(parameters.setValidateNaming)

openApiStyleIgnoreHeaderXNaming.value.foreach(parameters.setIgnoreHeaderXNaming)

openApiStylePathNamingConvention.value.foreach(parameters.setPathNamingConvention)
openApiStyleParameterNamingConvention.value.foreach(parameters.setParameterNamingConvention)
openApiStyleHeaderNamingConvention.value.foreach(parameters.setHeaderNamingConvention)
openApiStylePropertyNamingConvention.value.foreach(parameters.setParameterNamingConvention)

parameters
}

Expand Down
Loading

0 comments on commit c75bcd6

Please sign in to comment.