Skip to content

Commit

Permalink
Merge pull request #1252 from zalando/gh-459-validate-path-parameters
Browse files Browse the repository at this point in the history
Add validation of required path parameters
  • Loading branch information
vadeg committed Jun 22, 2021
2 parents 34594a2 + e575f34 commit 750a31c
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,5 @@ fun Schema<Any>.extensibleEnum(): List<Any?> =
if (this.isExtensibleEnum()) {
(this.extensions["x-extensible-enum"] as List<Any?>)
} else emptyList<Any?>()

fun Parameter.isInPath() = this.`in` == "path"
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.zalando.zally.ruleset.zally

import org.zalando.zally.core.util.getAllParameters
import org.zalando.zally.core.util.isInPath
import org.zalando.zally.rule.api.Check
import org.zalando.zally.rule.api.Context
import org.zalando.zally.rule.api.Rule
import org.zalando.zally.rule.api.Severity
import org.zalando.zally.rule.api.Violation

@Rule(
ruleSet = ZallyRuleSet::class,
id = "Z001",
severity = Severity.MUST,
title = "Path parameters must have 'required' attribute"
)
class PathParameterRule {

companion object {
const val ERROR_MESSAGE = "Parameter with location \"path\" must have an attribute \"required=true\" set"
}

@Check(severity = Severity.MUST)
fun validate(context: Context): List<Violation> =
context.api.getAllParameters().map { entry -> entry.value }
.filter { parameter ->
parameter.isInPath() && !parameter.required
}
.map {
context.violation(ERROR_MESSAGE, it)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package org.zalando.zally.ruleset.zally

import org.intellij.lang.annotations.Language
import org.junit.Test
import org.zalando.zally.core.DefaultContextFactory
import org.zalando.zally.test.ZallyAssertions.assertThat

class PathParameterRuleTest {

private val rule = PathParameterRule()

@Test
internal fun `return violation if a path parameter is not marked as "required"`() {
@Language("YAML")
val context = DefaultContextFactory().getOpenApiContext(
"""
openapi: '3.0.0'
info:
title: API 1
contact:
info: "Team One"
paths:
/items/{item-id}:
get:
parameters:
- name: item-id
in: path
description: The id of the pet to retrieve
schema:
type: string
responses:
default:
description: Response
content:
application/json:
schema:
type: string
""".trimIndent()
)

val violations = rule.validate(context)

assertThat(violations)
.descriptionsAllEqualTo(PathParameterRule.ERROR_MESSAGE)
.pointersEqualTo("/paths/~1items~1{item-id}/get/parameters/0")
}

@Test
fun `return no violations if a path parameter marked as "required"`() {
@Language("YAML")
val context = DefaultContextFactory().getOpenApiContext(
"""
openapi: '3.0.0'
info:
title: API 1
contact:
info: "Team One"
paths:
/items/{item-id}:
get:
parameters:
- name: item-id
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
default:
description: Response
content:
application/json:
schema:
type: string
""".trimIndent()
)
val violations = rule.validate(context)
assertThat(violations).isEmpty()
}

@Test
internal fun `return violation if a path parameter in components is not marked as "required"`() {
@Language("YAML")
val context = DefaultContextFactory().getOpenApiContext(
"""
openapi: '3.0.0'
info:
title: API 1
contact:
info: "Team One"
paths:
/items/{item-id}:
get:
parameters:
- $\ref: "#/components/parameters/QueryParameter"
responses:
default:
description: Response
content:
application/json:
schema:
type: string
components:
parameters:
QueryParameter:
name: item-id
in: path
description: The id of the pet to retrieve
schema:
type: string
""".trimIndent()
)

val violations = rule.validate(context)

assertThat(violations)
.descriptionsAllEqualTo(PathParameterRule.ERROR_MESSAGE)
.pointersEqualTo("/components/parameters/QueryParameter")
}
}

0 comments on commit 750a31c

Please sign in to comment.