Skip to content
This repository was archived by the owner on Mar 16, 2025. It is now read-only.

Commit 89b9af6

Browse files
committed
improve parser error handling
1 parent bb16eb8 commit 89b9af6

9 files changed

Lines changed: 176 additions & 14 deletions

File tree

src/main/kotlin/io/openapiprocessor/core/parser/OpenApi.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ interface OpenApi {
2626
fun getPaths(): Map<String, Path>
2727

2828
fun getRefResolver(): RefResolver
29+
2930
fun printWarnings()
31+
fun hasWarnings(): Boolean
3032

3133
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright 2021 https://github.com/openapi-processor/openapi-processor-core
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.core.parser
7+
8+
class ParserException(ex: Exception) : RuntimeException(ex)

src/main/kotlin/io/openapiprocessor/core/parser/openapi4j/OpenApi.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import io.openapiprocessor.core.parser.OpenApi as ParserOpenApi
99
import io.openapiprocessor.core.parser.Path as ParserPath
1010
import io.openapiprocessor.core.parser.RefResolver as ParserRefResolver
1111
import org.openapi4j.core.validation.ValidationResults
12+
import org.slf4j.Logger
13+
import org.slf4j.LoggerFactory
1214
import org.openapi4j.parser.model.v3.OpenApi3 as O4jOpenApi
1315
import org.openapi4j.parser.model.v3.Path as O4jPath
1416

@@ -19,6 +21,8 @@ class OpenApi(
1921
private val api: O4jOpenApi,
2022
private val validations: ValidationResults,
2123
): ParserOpenApi {
24+
private val log: Logger = LoggerFactory.getLogger(this.javaClass.name)
25+
2226
private val refResolver: RefResolverNative = RefResolverNative(api)
2327

2428
override fun getPaths(): Map<String, ParserPath> {
@@ -39,7 +43,14 @@ class OpenApi(
3943
override fun getRefResolver(): ParserRefResolver = RefResolver (api)
4044

4145
override fun printWarnings() {
42-
// already prints warnings
46+
validations.items()
47+
.forEach {
48+
log.warn("{} - {} ({}}", it.severity(), it.message(), it.code())
49+
}
50+
}
51+
52+
override fun hasWarnings(): Boolean {
53+
return validations.items().isNotEmpty()
4354
}
4455

4556
}

src/main/kotlin/io/openapiprocessor/core/parser/openapi4j/Parser.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,41 @@
1616

1717
package io.openapiprocessor.core.parser.openapi4j
1818

19+
import io.openapiprocessor.core.parser.ParserException
1920
import io.openapiprocessor.core.support.toURL
21+
import org.openapi4j.core.exception.ResolutionException
22+
import org.openapi4j.core.validation.ValidationException
2023
import io.openapiprocessor.core.parser.OpenApi as ParserOpenApi
2124
import org.openapi4j.parser.OpenApi3Parser
2225
import org.openapi4j.parser.validation.v3.OpenApi3Validator
26+
import org.slf4j.Logger
27+
import org.slf4j.LoggerFactory
2328

2429
/**
2530
* openapi4j parser.
2631
*
2732
* @author Martin Hauner
2833
*/
2934
open class Parser {
35+
private val log: Logger = LoggerFactory.getLogger(this.javaClass.name)
3036

3137
fun parse(apiPath: String): ParserOpenApi {
38+
try {
39+
return run(apiPath)
40+
} catch (ex: ResolutionException) {
41+
log.error("can't read OpenAPI description!")
42+
log.error(ex.message)
43+
throw ParserException(ex)
44+
} catch (ex: ValidationException) {
45+
log.error("failed to parse OpenAPI description!")
46+
log.error(ex.results().toString())
47+
throw ParserException(ex)
48+
}
49+
}
50+
51+
private fun run(apiPath: String): ParserOpenApi {
3252
val api = OpenApi3Parser()
33-
.parse(toURL (apiPath), true)
53+
.parse(toURL(apiPath), true)
3454

3555
val results = OpenApi3Validator
3656
.instance()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright 2021 https://github.com/openapi-processor/openapi-processor-core
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.core.parser.swagger
7+
8+
class FailedException : RuntimeException()

src/main/kotlin/io/openapiprocessor/core/parser/swagger/OpenApi.kt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,13 @@ class OpenApi(private val result: SwaggerParseResult): ParserOpenApi {
4545
override fun getRefResolver(): ParserRefResolver = RefResolver (result.openAPI)
4646

4747
override fun printWarnings() {
48-
print (result.messages)
49-
}
50-
51-
private fun print (warnings: List<String>) {
52-
if (warnings.isEmpty()) {
53-
return
54-
}
55-
56-
warnings.forEach {
48+
result.messages?.forEach {
5749
log.warn(it)
5850
}
5951
}
6052

53+
override fun hasWarnings(): Boolean {
54+
return result.messages.isNotEmpty()
55+
}
56+
6157
}

src/main/kotlin/io/openapiprocessor/core/parser/swagger/Parser.kt

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
package io.openapiprocessor.core.parser.swagger
1818

19+
import io.openapiprocessor.core.parser.ParserException
1920
import io.openapiprocessor.core.support.toURL
2021
import io.swagger.v3.parser.OpenAPIV3Parser
2122
import io.swagger.v3.parser.core.models.ParseOptions
22-
import io.swagger.v3.parser.core.models.SwaggerParseResult
23+
import org.slf4j.Logger
24+
import org.slf4j.LoggerFactory
2325
import io.openapiprocessor.core.parser.OpenApi as ParserOpenApi
2426

2527
const val SCHEME_RESOURCE = "resource:"
@@ -30,15 +32,49 @@ const val SCHEME_RESOURCE = "resource:"
3032
* @author Martin Hauner
3133
*/
3234
open class Parser {
35+
private val log: Logger = LoggerFactory.getLogger(this.javaClass.name)
36+
37+
private enum class Source {URL, STRING}
3338

3439
fun parse(apiPath: String): ParserOpenApi {
40+
try {
41+
return run(apiPath, Source.URL)
42+
} catch (ex: Exception) {
43+
log.error("can't read OpenAPI description!")
44+
throw ParserException(ex)
45+
}
46+
}
47+
48+
/** test only */
49+
fun parseString(api: String): ParserOpenApi {
50+
try {
51+
return run(api, Source.STRING)
52+
} catch (ex: Exception) {
53+
throw ParserException(ex)
54+
}
55+
}
56+
57+
private fun run(api: String, source: Source): ParserOpenApi {
3558
val opts = ParseOptions()
3659
// loads $refs to other files into #/components/schema and replaces the $refs to the
3760
// external files with $refs to #/components/schema.
3861
opts.isResolve = true
3962

40-
val result: SwaggerParseResult = OpenAPIV3Parser()
41-
.readLocation(preparePath(apiPath), null, opts)
63+
val result = when(source) {
64+
Source.URL -> {
65+
OpenAPIV3Parser().readLocation(preparePath(api), null, opts)
66+
}
67+
Source.STRING -> {
68+
OpenAPIV3Parser().readContents(api, null, opts)
69+
}
70+
}
71+
72+
if (result.openAPI == null) {
73+
result.messages?.forEach {
74+
log.error(it)
75+
}
76+
throw FailedException()
77+
}
4278

4379
return OpenApi(result)
4480
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2021 https://github.com/openapi-processor/openapi-processor-core
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.core.parser.openapi4j
7+
8+
import io.kotest.assertions.throwables.shouldThrow
9+
import io.kotest.core.spec.style.StringSpec
10+
import io.openapiprocessor.core.parser.ParserException
11+
import io.openapiprocessor.test.stream.Memory
12+
13+
class ParserSpec : StringSpec({
14+
15+
"catch parser resource exception" {
16+
val parser = Parser()
17+
18+
shouldThrow<ParserException> {
19+
parser.parse("memory:openapi.yaml")
20+
}
21+
}
22+
23+
"catch parser validation exception" {
24+
Memory.add("openapi.yaml", """
25+
openapi: 3.0.3
26+
info:
27+
title: missing path
28+
version: '1.0'
29+
""".trimIndent()
30+
)
31+
32+
val parser = Parser()
33+
34+
shouldThrow<ParserException> {
35+
parser.parse("memory:openapi.yaml")
36+
}
37+
}
38+
39+
afterEach {
40+
Memory.clear()
41+
}
42+
43+
})
44+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2021 https://github.com/openapi-processor/openapi-processor-core
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.core.parser.swagger
7+
8+
import io.kotest.assertions.throwables.shouldThrow
9+
import io.kotest.core.spec.style.StringSpec
10+
import io.kotest.matchers.shouldBe
11+
import io.openapiprocessor.core.parser.ParserException
12+
13+
class ParserSpec : StringSpec ({
14+
15+
"catch parser resource exception" {
16+
val parser = Parser()
17+
18+
shouldThrow<ParserException> {
19+
parser.parse("openapi.yaml")
20+
}
21+
}
22+
23+
"has warning messages" {
24+
val parser = Parser()
25+
26+
val api = parser.parseString("""
27+
openapi: 3.0.3
28+
info:
29+
title: missing path
30+
version: '1.0'
31+
32+
""".trimIndent())
33+
34+
api.hasWarnings() shouldBe true
35+
}
36+
37+
})

0 commit comments

Comments
 (0)