Skip to content

Commit

Permalink
feat: upgrade swagger dependencies (#1379)
Browse files Browse the repository at this point in the history
  • Loading branch information
tkrop committed Apr 14, 2022
1 parent fe9b094 commit cbcb412
Show file tree
Hide file tree
Showing 34 changed files with 245 additions and 97 deletions.
21 changes: 18 additions & 3 deletions server/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

plugins {
val kotlinVersion = "1.6.20"
val klintVersion = "10.2.1"
Expand All @@ -14,7 +19,8 @@ plugins {
jacoco
`maven-publish`
signing
id("com.github.ben-manes.versions") version "0.20.0"
eclipse
id("com.github.ben-manes.versions") version "0.42.0"
id("org.jetbrains.dokka") version "1.6.10" apply false

// We apply this so that ktlint can format the top level buildscript
Expand All @@ -40,6 +46,7 @@ subprojects {
apply(plugin = "maven-publish")
apply(plugin = "jacoco")
apply(plugin = "signing")
apply(plugin = "eclipse")

kapt {
includeCompileClasspath = false
Expand Down Expand Up @@ -155,15 +162,17 @@ subprojects {
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.yaml:snakeyaml:1.30")

testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2")
testImplementation("com.jayway.jsonpath:json-path-assert:2.7.0")
testImplementation("org.mockito:mockito-core:2.28.2")
testImplementation("org.mockito:mockito-core:4.4.0")
}

jacoco {
toolVersion = "0.8.2"
toolVersion = "0.8.8"
}

tasks.test {
useJUnitPlatform()
finalizedBy(tasks.jacocoTestReport)
}

Expand All @@ -177,4 +186,10 @@ subprojects {
tasks.jar {
archiveBaseName.set(project.name)
}

eclipse {
project {
natures.add("org.jetbrains.kotlin.core.kotlinNature")
}
}
}
7 changes: 4 additions & 3 deletions server/zally-core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
dependencies {
kapt("com.google.auto.service:auto-service:1.0-rc6")
kapt("com.google.auto.service:auto-service:1.0.1")

api(project(":zally-rule-api"))
api("io.swagger.parser.v3:swagger-parser:2.0.26")
api("io.swagger.parser.v3:swagger-parser:2.0.32")
api("io.github.config4k:config4k:0.4.2")
implementation("com.google.auto.service:auto-service:1.0-rc6")
implementation("com.google.auto.service:auto-service:1.0.1")

testImplementation(project(":zally-test"))
testImplementation("org.junit.jupiter:junit-jupiter")
}
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class CaseChecker(
check: CaseCheck?
): List<Violation> = context.api
.getAllParameters()
.filter { type.toLowerCase() == it.`in` }
.filter { type.lowercase() == it.`in` }
.flatMap { param ->
check("$type parameter", "$type parameters", check, param.name)
?.let { context.violations(it, param) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.zalando.zally.core

import com.fasterxml.jackson.core.JsonPointer
import org.zalando.zally.rule.api.Context
import org.zalando.zally.rule.api.Violation

/**
* This validator validates a given OpenAPI definition based
Expand All @@ -14,13 +15,16 @@ class ContextRulesValidator(

override fun parse(content: String, authorization: String?): ContentParseResult<Context> {
// first try to parse an OpenAPI (version 3+)
return when (val parsedAsOpenApi = defaultContextFactory.parseOpenApiContext(content, authorization)) {
is ContentParseResult.NotApplicable ->
// if content was no OpenAPI, try to parse a Swagger (version 2)
defaultContextFactory.parseSwaggerContext(content)
else ->
parsedAsOpenApi
val parsedAsOpenApi = defaultContextFactory.parseOpenApiContext(content, authorization)
if (parsedAsOpenApi !is ContentParseResult.NotApplicable) {
return parsedAsOpenApi
}
// if content was no OpenAPI, try to parse a Swagger (version 2)
val parsedAsSwagger = defaultContextFactory.parseSwaggerContext(content)
if (parsedAsSwagger !is ContentParseResult.NotApplicable) {
return parsedAsSwagger
}
return ContentParseResult.ParsedWithErrors(listOf(Violation("No valid Open API specification", EMPTY_JSON_POINTER)))
}

override fun ignore(root: Context, pointer: JsonPointer, ruleId: String) = root.isIgnored(pointer, ruleId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class JsonSchemaValidator(val schema: JsonNode, schemaRedirects: Map<String, Str
private fun toValidationMessage(processingMessage: ProcessingMessage): Violation {
val node = processingMessage.asJson()
val keyword = node.path("keyword").textValue()
val message = node.path("message").textValue().capitalize()
val message = node.path("message").textValue().replaceFirstChar({ it.uppercase() })
val pointer = node.at("/instance/pointer")?.textValue()?.toJsonPointer()
?: JsonPointer.empty()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class RulesManager(val config: Config, val rules: List<RuleDetails>) {

companion object {
fun fromClassLoader(config: Config) =
javaClass.classLoader
this::class.java.classLoader
.getResources("META-INF/services/${Rule::class.java.name}")
.asSequence()
.flatMap { it.readText().lineSequence() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ abstract class RulesValidator<RootT : Any>(val rules: RulesManager) : ApiValidat
parseResult.violations.map { violation ->
Result(
id = "InternalRuleSet",
url = URI.create("https://github.com/zalando/zally/blob/master/server/rules.md"),
title = "Unable to parse API specification",
url = URI.create("https://zalando.github.io/restful-api-guidelines/#101"),
title = "provide API specification using OpenAPI",
description = violation.description,
violationType = Severity.MUST,
pointer = violation.pointer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class ReverseAstBuilder<T : Any> internal constructor(root: T) {
val nextPath = m.name
.takeIf { it !in this.extensionMethodNames }
?.removePrefix("get")
?.decapitalize()
?.replaceFirstChar({ it.lowercase() })
?: ""

nodes.push(Node(value, pointer + nextPath.toEscapedJsonPointer(), marker))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.zalando.zally.core.ContentParseResultAssert.Companion.assertThat
import org.assertj.core.api.Assertions.assertThat
import org.intellij.lang.annotations.Language
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Disabled

class DefaultContextFactoryTest {

Expand All @@ -26,7 +27,74 @@ class DefaultContextFactoryTest {
}

@Test
fun `OPEN API -- openapi specification without info and paths succeeds with messages`() {
fun `OPEN API -- OpenAPI 3-1 is not applicable`() {
// The parsing results in no OpenAPI 3.1 object model until the latest
// parser is providing support. Than we can remove this test case and
// and enable the subsequent tests.
@Language("YAML")
val content = """
openapi: 3.1.0
"""
val result = defaultContextFactory.parseOpenApiContext(content)
assertThat(result).resultsInNotApplicable()
}

@Test
@Disabled("OpenAPI 3.1 is not supported by latest swagger parser yet")
fun `OPEN API -- OpenAPI 3-1 without info and paths succeeds with messages`() {
// The parsing results in a valid OpenAPI 3.1 object model, but
// with messages that `info` and `paths` are missing. Let the
// rules check that out.
@Language("YAML")
val content = """
openapi: 3.1.0
"""
val result = defaultContextFactory.parseOpenApiContext(content)
assertThat(result).resultsInSuccess()
val success = result as ContentParseResult.ParsedSuccessfully
assertThat(success.result.isOpenAPI3()).isTrue()
}

@Test
@Disabled("OpenAPI 3.1 is not supported by latest swagger parser yet")
fun `OPEN API -- OpenAPI 3-1 with oauth but without scopes succeeds`() {
@Language("YAML")
val content = """
openapi: 3.1.0
info:
title: Foo
version: 1.0.0
security:
- type: oauth2
flow: implicit
authorizationUrl: https://identity.some-server/auth
paths: {}
"""
val result = defaultContextFactory.parseOpenApiContext(content)
assertThat(result).resultsInSuccess()
val success = result as ContentParseResult.ParsedSuccessfully
assertThat(success.result.isOpenAPI3()).isTrue()
}

@Test
@Disabled("OpenAPI 3.1 is not supported by latest swagger parser yet")
fun `OPEN API -- OpenAPI 3-1 is recognised as an OpenAPI3 spec`() {
@Language("YAML")
val content = """
openapi: 3.1.0
info:
title: Foo
version: 1.0.0
paths: {}
"""
val result = defaultContextFactory.parseOpenApiContext(content)
assertThat(result).resultsInSuccess()
val success = result as ContentParseResult.ParsedSuccessfully
assertThat(success.result.isOpenAPI3()).isTrue()
}

@Test
fun `OPEN API -- OpenAPI 3-0-0 without info and paths succeeds`() {
// The parsing results in a valid OpenAPI 3 object model, but
// with messages that `info` and `paths` are missing. Let the
// rules check that out.
Expand All @@ -36,10 +104,57 @@ class DefaultContextFactoryTest {
"""
val result = defaultContextFactory.parseOpenApiContext(content)
assertThat(result).resultsInSuccess()
val success = result as ContentParseResult.ParsedSuccessfully
assertThat(success.result.isOpenAPI3()).isTrue()
}

@Test
fun `OPEN API -- OpenAPI 3-0-1 without info and paths succeeds`() {
// The parsing results in a valid OpenAPI 3 object model, but
// with messages that `info` and `paths` are missing. Let the
// rules check that out.
@Language("YAML")
val content = """
openapi: 3.0.1
"""
val result = defaultContextFactory.parseOpenApiContext(content)
assertThat(result).resultsInSuccess()
val success = result as ContentParseResult.ParsedSuccessfully
assertThat(success.result.isOpenAPI3()).isTrue()
}

@Test
fun `OPEN API -- OpenAPI 3-0-2 without info and paths succeeds`() {
// The parsing results in a valid OpenAPI 3 object model, but
// with messages that `info` and `paths` are missing. Let the
// rules check that out.
@Language("YAML")
val content = """
openapi: 3.0.2
"""
val result = defaultContextFactory.parseOpenApiContext(content)
assertThat(result).resultsInSuccess()
val success = result as ContentParseResult.ParsedSuccessfully
assertThat(success.result.isOpenAPI3()).isTrue()
}

@Test
fun `OPEN API -- OpenAPI 3-0-3 without info and paths succeeds`() {
// The parsing results in a valid OpenAPI 3 object model, but
// with messages that `info` and `paths` are missing. Let the
// rules check that out.
@Language("YAML")
val content = """
openapi: 3.0.3
"""
val result = defaultContextFactory.parseOpenApiContext(content)
assertThat(result).resultsInSuccess()
val success = result as ContentParseResult.ParsedSuccessfully
assertThat(success.result.isOpenAPI3()).isTrue()
}

@Test
fun `OPEN API -- oauth without scopes succeeds`() {
fun `OPEN API -- OpenAPI 3-0-x with oauth but without scopes succeeds`() {
@Language("YAML")
val content = """
openapi: 3.0.0
Expand All @@ -54,10 +169,12 @@ class DefaultContextFactoryTest {
"""
val result = defaultContextFactory.parseOpenApiContext(content)
assertThat(result).resultsInSuccess()
val success = result as ContentParseResult.ParsedSuccessfully
assertThat(success.result.isOpenAPI3()).isTrue()
}

@Test
fun `OPEN API -- OpenAPI is recognised as an OpenAPI3 spec`() {
fun `OPEN API -- OpenAPI 3-0-x is recognised as an OpenAPI3 spec`() {
@Language("YAML")
val content = """
openapi: 3.0.0
Expand All @@ -73,7 +190,7 @@ class DefaultContextFactoryTest {
}

@Test
fun `OPEN API -- does not recognize a Swagger file`() {
fun `OPEN API -- OpenAPI 2-0 is not recognize as OpenAPI3 spec`() {
@Language("YAML")
val content = """
swagger: '2.0'
Expand Down Expand Up @@ -103,7 +220,17 @@ class DefaultContextFactoryTest {
}

@Test
fun `SWAGGER -- error when info and path objects are missing`() {
fun `SWAGGER -- OpenAPI 1-0 without info and path objects succeeds`() {
@Language("YAML")
val content = """
swagger: 1.0
"""
val result = defaultContextFactory.parseSwaggerContext(content)
assertThat(result).resultsInSuccess()
}

@Test
fun `SWAGGER -- OpenAPI 2-0 without info and path objects succeeds`() {
@Language("YAML")
val content = """
swagger: 2.0
Expand All @@ -113,7 +240,7 @@ class DefaultContextFactoryTest {
}

@Test
fun `SWAGGER -- error when securityDefinition type is missing`() {
fun `SWAGGER -- OpenAPI 2-0 without security definition type succeeds`() {
@Language("YAML")
val content = """
swagger: 2.0
Expand All @@ -129,7 +256,7 @@ class DefaultContextFactoryTest {
}

@Test
fun `SWAGGER -- error when oauth elements are missing`() {
fun `SWAGGER -- OpenAPI 2-0 without oauth elements succeeds`() {
// Specific case where converting from Swagger to OpenAPI 3 (using the `Context`
// object) would throw an exception. New behaviour tested here: the returned `Context`
// is null because the file was not parsed (convertible, here).
Expand All @@ -151,7 +278,7 @@ class DefaultContextFactoryTest {
}

@Test
fun `SWAGGER -- minimal Swagger API is not recognized as an OpenAPI3 spec`() {
fun `SWAGGER -- OpenAPI 2-0 is not recognized as an OpenAPI3 spec`() {
@Language("YAML")
val content = """
swagger: 2.0
Expand All @@ -167,7 +294,7 @@ class DefaultContextFactoryTest {
}

@Test
fun `SWAGGER -- recursive-model-extension`() {
fun `SWAGGER -- OpenAPI 2-0 with recursive-model-extension succeeds`() {
@Language("YAML")
val content = """
swagger: '2.0'
Expand Down Expand Up @@ -215,6 +342,7 @@ class DefaultContextFactoryTest {
// This Swagger, after being converted, causes the `components` property to exist (not
// null), but having a null `schemas`, which causes the NPE.
val ref = "\$ref"

@Language("YAML")
val content = """
swagger: '2.0'
Expand Down
Loading

0 comments on commit cbcb412

Please sign in to comment.