Skip to content

Commit

Permalink
Merge pull request #1306 from znsio/fixes
Browse files Browse the repository at this point in the history
Miscellaneous fixes
  • Loading branch information
joelrosario authored Sep 17, 2024
2 parents 5b37651 + 7b4f79b commit d35e7a4
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 35 deletions.
118 changes: 86 additions & 32 deletions application/src/main/kotlin/application/ExamplesCommand.kt
Original file line number Diff line number Diff line change
@@ -1,55 +1,82 @@
package application

import io.specmatic.core.examples.server.ExamplesInteractiveServer
import io.specmatic.core.log.StringLog
import io.specmatic.core.log.consoleLog
import io.specmatic.core.log.logger
import io.specmatic.core.log.*
import io.specmatic.core.utilities.exceptionCauseMessage
import io.specmatic.core.utilities.exitWithMessage
import io.specmatic.mock.NoMatchingScenario
import net.bytebuddy.implementation.bytecode.Throw
import picocli.CommandLine.*
import java.io.File
import java.lang.Thread.sleep
import java.util.concurrent.Callable
import kotlin.system.exitProcess

@Command(name = "examples",
mixinStandardHelpOptions = true,
description = ["Generate externalised JSON example files with API requests and responses"],
subcommands = [ExamplesCommand.Validate::class, ExamplesCommand.Interactive::class]
@Command(
name = "examples",
mixinStandardHelpOptions = true,
description = ["Generate externalised JSON example files with API requests and responses"],
subcommands = [ExamplesCommand.Validate::class, ExamplesCommand.Interactive::class]
)
class ExamplesCommand : Callable<Unit> {

@Option(names = ["--filter-name"], description = ["Use only APIs with this value in their name"], defaultValue = "\${env:SPECMATIC_FILTER_NAME}")
@Option(
names = ["--filter-name"],
description = ["Use only APIs with this value in their name"],
defaultValue = "\${env:SPECMATIC_FILTER_NAME}"
)
var filterName: String = ""

@Option(names = ["--filter-not-name"], description = ["Use only APIs which do not have this value in their name"], defaultValue = "\${env:SPECMATIC_FILTER_NOT_NAME}")
@Option(
names = ["--filter-not-name"],
description = ["Use only APIs which do not have this value in their name"],
defaultValue = "\${env:SPECMATIC_FILTER_NOT_NAME}"
)
var filterNotName: String = ""

@Option(names = ["--extensive"], description = ["Generate all examples (by default, generates one example per 2xx API)"], defaultValue = "false")
@Option(
names = ["--extensive"],
description = ["Generate all examples (by default, generates one example per 2xx API)"],
defaultValue = "false"
)
var extensive: Boolean = false

@Option(names = ["--overwrite"], description = ["Overwrite the examples directory if it exists"], defaultValue = "false")
@Option(
names = ["--overwrite"],
description = ["Overwrite the examples directory if it exists"],
defaultValue = "false"
)
var overwrite: Boolean = false

@Parameters(index = "0", description = ["Contract file path"], arity = "0..1")
var contractFile: File? = null

@Option(names = ["--debug"], description = ["Debug logs"])
var verbose = false

override fun call() {
if(contractFile == null) {
if (contractFile == null) {
println("No contract file provided. Use a subcommand or provide a contract file. Use --help for more details.")
return
}
if (!contractFile!!.exists())
exitWithMessage("Could not find file ${contractFile!!.path}")

configureLogger(this.verbose)

try {
ExamplesInteractiveServer.generate(contractFile!!, ExamplesInteractiveServer.ScenarioFilter(filterName, filterNotName), extensive, overwrite)
} catch (e: Exception) {
exitWithMessage("An unexpected error occurred while generating examples: ${e.message}")
ExamplesInteractiveServer.generate(
contractFile!!,
ExamplesInteractiveServer.ScenarioFilter(filterName, filterNotName),
extensive,
overwrite
)
} catch (e: Throwable) {
logger.log(e)
exitProcess(1)
}
}


@Command(
name = "validate",
mixinStandardHelpOptions = true,
Expand All @@ -62,10 +89,15 @@ class ExamplesCommand : Callable<Unit> {
@Option(names = ["--example-file"], description = ["Example file path"], required = false)
val exampleFile: File? = null

@Option(names = ["--debug"], description = ["Debug logs"])
var verbose = false

override fun call() {
if (!contractFile.exists())
exitWithMessage("Could not find file ${contractFile.path}")

configureLogger(this.verbose)

if (exampleFile != null) {
try {
ExamplesInteractiveServer.validate(contractFile, exampleFile)
Expand All @@ -78,7 +110,7 @@ class ExamplesCommand : Callable<Unit> {
} else {
val result = ExamplesInteractiveServer.validate(contractFile)

if(result.isSuccess() == false) {
if (result.isSuccess() == false) {
logger.log(result.reportString())
exitProcess(1)
}
Expand All @@ -95,28 +127,41 @@ class ExamplesCommand : Callable<Unit> {
@Option(names = ["--contract-file"], description = ["Contract file path"], required = false)
var contractFile: File? = null

@Option(names = ["--filter-name"], description = ["Use only APIs with this value in their name"], defaultValue = "\${env:SPECMATIC_FILTER_NAME}")
@Option(
names = ["--filter-name"],
description = ["Use only APIs with this value in their name"],
defaultValue = "\${env:SPECMATIC_FILTER_NAME}"
)
var filterName: String = ""

@Option(names = ["--filter-not-name"], description = ["Use only APIs which do not have this value in their name"], defaultValue = "\${env:SPECMATIC_FILTER_NOT_NAME}")
@Option(
names = ["--filter-not-name"],
description = ["Use only APIs which do not have this value in their name"],
defaultValue = "\${env:SPECMATIC_FILTER_NOT_NAME}"
)
var filterNotName: String = ""

@Option(names = ["--debug"], description = ["Debug logs"])
var verbose = false

var server: ExamplesInteractiveServer? = null

override fun call() {
try {
if (contractFile != null && !contractFile!!.exists())
exitWithMessage("Could not find file ${contractFile!!.path}")

server = ExamplesInteractiveServer("0.0.0.0", 9001, contractFile, filterName, filterNotName)
addShutdownHook()

consoleLog(StringLog("Examples Interactive server is running on http://0.0.0.0:9001/_specmatic/examples. Ctrl + C to stop."))
while(true) sleep(10000)
} catch(e: Exception) {
logger.log(exceptionCauseMessage(e))
exitWithMessage(e.message.orEmpty())
}
configureLogger(verbose)

try {
if (contractFile != null && !contractFile!!.exists())
exitWithMessage("Could not find file ${contractFile!!.path}")

server = ExamplesInteractiveServer("0.0.0.0", 9001, contractFile, filterName, filterNotName)
addShutdownHook()

consoleLog(StringLog("Examples Interactive server is running on http://0.0.0.0:9001/_specmatic/examples. Ctrl + C to stop."))
while (true) sleep(10000)
} catch (e: Exception) {
logger.log(exceptionCauseMessage(e))
exitWithMessage(e.message.orEmpty())
}
}

private fun addShutdownHook() {
Expand All @@ -135,3 +180,12 @@ class ExamplesCommand : Callable<Unit> {
}
}
}

private fun configureLogger(verbose: Boolean) {
val logPrinters = listOf(ConsolePrinter)

logger = if (verbose)
Verbose(CompositePrinter(logPrinters))
else
NonVerbose(CompositePrinter(logPrinters))
}
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,7 @@ class OpenApiSpecification(
toJSONObjectPattern(properties, "(${componentName})")
}

if (oneOfs.size == 1)
val pattern = if (oneOfs.size == 1)
oneOfs.single()
else if (oneOfs.size > 1)
AnyPattern(oneOfs)
Expand All @@ -1276,6 +1276,10 @@ class OpenApiSpecification(
AnyPattern(schemaProperties.map { toJSONObjectPattern(it, "(${patternName})") })
else
toJSONObjectPattern(schemaProperties.single(), "(${patternName})")

cacheComponentPattern(patternName, pattern)

pattern
} else if (schema.oneOf != null) {
val candidatePatterns = schema.oneOf.filterNot { nullableEmptyObject(it) }.map { componentSchema ->
val (componentName, schemaToProcess) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ class ExamplesInteractiveServer(

if(examplesDir.exists() && overwriteByDefault.not()) {
val response: String = Scanner(System.`in`).use { scanner ->
print("Do you want to overwrite all the existing examples in ${examplesDir.name}? (y/n): ")
print("Found examples working directory at \"${examplesDir.path}\". Overwrite it? (y/n): ")
scanner.nextLine().trim().lowercase()
}
if(response == "y") {
Expand Down
2 changes: 1 addition & 1 deletion version.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=2.0.21
version=2.0.22

0 comments on commit d35e7a4

Please sign in to comment.