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 13, 2022
1 parent fe9b094 commit 1e8e5a0
Show file tree
Hide file tree
Showing 34 changed files with 176 additions and 84 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 31 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 31 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 31 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 31 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 30 without info and paths succeeds with messages`() {
// 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,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 -- oauth without scopes succeeds`() {
fun `OPEN API -- OpenAPI 30 with oauth but without scopes succeeds`() {
@Language("YAML")
val content = """
openapi: 3.0.0
Expand All @@ -54,10 +124,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 30 is recognised as an OpenAPI3 spec`() {
@Language("YAML")
val content = """
openapi: 3.0.0
Expand All @@ -73,7 +145,7 @@ class DefaultContextFactoryTest {
}

@Test
fun `OPEN API -- does not recognize a Swagger file`() {
fun `OPEN API -- OpenAPI 20 does not recognize a Swagger file`() {
@Language("YAML")
val content = """
swagger: '2.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ReverseAstBuilderTest {
"getDescription",
"getExtensions",
"getLicense",
"getSummary",
"getTermsOfService",
"getTitle",
"getVersion"
Expand Down Expand Up @@ -95,10 +96,12 @@ class ReverseAstBuilderTest {
"getExtensions",
"getExternalDocs",
"getInfo",
"getJsonSchemaDialect",
"getOpenapi",
"getSecurity",
"getServers",
"getTags",
"getWebhooks",
"getPaths"
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class ReverseAstTest {
val ast = ReverseAst.fromObject(spec).build()

val description = spec.paths?.get("/tests")?.get?.responses?.get("200")?.description
assertThat(ast.getPointer(description!!)).hasToString("/paths/~1tests/get/responses/200/description")
assertThat(ast.getPointer(description!!)).hasToString("/paths/~1tests/get/responsesObject/200/description")
}

@Test
Expand Down Expand Up @@ -68,15 +68,15 @@ class ReverseAstTest {

var pointer = "/paths/~1tests/get/responses/200/description".toJsonPointer()
assertThat(ast.isIgnored(pointer, "*")).isTrue()
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).contains("*")
assertThat(ast.getIgnoreValues(pointer)).hasSize(0)

pointer = "/paths/~1tests/get".toJsonPointer()
assertThat(ast.isIgnored(pointer, "*")).isTrue()
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).contains("*")
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).isEqualTo(setOf("*"))

pointer = "/paths/~1tests".toJsonPointer()
assertThat(ast.isIgnored(pointer, "*")).isTrue()
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).contains("*")
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).isEqualTo(setOf("*"))

pointer = PATHS.toJsonPointer()
assertThat(ast.isIgnored(pointer, "*")).isFalse()
Expand Down Expand Up @@ -132,15 +132,15 @@ class ReverseAstTest {

var pointer = "/paths/~1tests/get/responses/200/description".toJsonPointer()
assertThat(ast.isIgnored(pointer, "*")).isTrue()
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).contains("*")
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).isEqualTo(setOf("*"))

pointer = "/paths/~1tests/get".toJsonPointer()
assertThat(ast.isIgnored(pointer, "*")).isTrue()
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).contains("*")
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).isEqualTo(setOf("*"))

pointer = "/paths/~1tests".toJsonPointer()
assertThat(ast.isIgnored(pointer, "*")).isTrue()
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).contains("*")
assertThat(ast.getIgnoreValues(pointer)).hasSize(1).isEqualTo(setOf("*"))

pointer = PATHS.toJsonPointer()
assertThat(ast.isIgnored(pointer, "*")).isFalse()
Expand Down
8 changes: 4 additions & 4 deletions server/zally-rule-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
dependencies {
implementation("io.swagger.core.v3:swagger-models:2.1.1")
implementation("io.swagger:swagger-models:1.6.0")
implementation("io.swagger.core.v3:swagger-models:2.2.0")
implementation("io.swagger:swagger-models:1.6.6")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.12.2")

testImplementation(platform("org.junit:junit-bom:5.8.1"))
testImplementation(platform("org.junit:junit-bom:5.8.2"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.assertj:assertj-core:3.11.0")
testImplementation("org.assertj:assertj-core:3.22.0")
}
1 change: 1 addition & 0 deletions server/zally-ruleset-zalando/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dependencies {
implementation("de.mpg.mpi-inf:javatools:1.1")

testImplementation(project(":zally-test"))
testImplementation("org.junit.jupiter:junit-jupiter")
}

tasks.register<MediaTypesConfigurationTask>("generate-media-types-config") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ class ProprietaryHeadersRule(rulesConfig: Config) {
private val standardResponseHeaders =
rulesConfig.getConfig(javaClass.simpleName).getStringList("standard_response_headers")

private val requestHeaders = (standardRequestHeaders + zalandoHeaders).map { it.toLowerCase() }
private val responseHeaders = (standardResponseHeaders + zalandoHeaders).map { it.toLowerCase() }
private val requestHeaders = (standardRequestHeaders + zalandoHeaders).map { it.lowercase() }
private val responseHeaders = (standardResponseHeaders + zalandoHeaders).map { it.lowercase() }

private val requestDescription = "use only standardized or specified request headers"
private val responseDescription = "use only standardized or specified response headers"

@Check(severity = Severity.SHOULD)
fun validateRequestHeaders(context: Context): List<Violation> = requestHeaders(context)
.filterNot { it.name.toLowerCase() in requestHeaders }
.filterNot { it.name.lowercase() in requestHeaders }
.map { context.violation(requestDescription, it) }

@Check(severity = Severity.SHOULD)
fun validateResponseHeaders(context: Context): List<Violation> = responseHeaders(context)
.filterNot { it.key.toLowerCase() in responseHeaders }
.filterNot { it.key.lowercase() in responseHeaders }
.map { context.violation(responseDescription, it.value) }

private fun requestHeaders(context: Context): List<Parameter> = context.api.paths?.values
Expand Down
Loading

0 comments on commit 1e8e5a0

Please sign in to comment.