-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement an OpenAPIComparator #194
base: master
Are you sure you want to change the base?
Implement an OpenAPIComparator #194
Conversation
d811249
to
4b54551
Compare
4b54551
to
fefeb58
Compare
@adamw, I was unable to add tests for reading |
@abdelfetah18 let's then maybe create an |
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPIComparator.scala
Outdated
Show resolved
Hide resolved
Can you review this? It's almost done, except for encoding, callbacks, and security. The tests are now using OpenAPI JSON files |
...-comparator-tests/src/test/scala/sttp/apispec/openapi/validation/OpenAPIComparatorTest.scala
Outdated
Show resolved
Hide resolved
...-comparator-tests/src/test/scala/sttp/apispec/openapi/validation/OpenAPIComparatorTest.scala
Outdated
Show resolved
Hide resolved
...-comparator-tests/src/test/scala/sttp/apispec/openapi/validation/OpenAPIComparatorTest.scala
Show resolved
Hide resolved
|
||
assert(openAPIComparator.compare() == expected) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apart from the naming issues, I think what I'm missing is positive test cases (different schemas, but compatible).
Another thing: evolution of the request body, e.g. adding a new variant to a coproduct. Both in the request, and response bodies. Depending where the variant is added (client/server and response/request), we'll get compatible, or incompatible changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another missing test case: changing metadata - adding or removing examples, description itd - this should not influence compatibility
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yet another: adding an alternative media type to the response. E.g. there's an application/json, but now the server can also return XML. Or the client can send XML.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And one more: introducing new response codes. E.g. 200 was defined, now additionally 400 and 401 are defined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is changing the parameter's name a compatible change? E.g. was /pet/{petId}
, now is: /pet/{id}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is changing the parameter's name a compatible change? E.g. was
/pet/{petId}
, now is:/pet/{id}
?
No, changing the parameter's name from /pet/{petId}
to /pet/{id}
is not a compatible change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, so we should test this as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yet another: adding an alternative media type to the response. E.g. there's an application/json, but now the server can also return XML. Or the client can send XML.
And one more: introducing new response codes. E.g. 200 was defined, now additionally 400 and 401 are defined
I've already tested them. one is missing response content media type
and the other is missing response
.
I think I should rename the test cases because they’re unclear.
serverOpenAPI: OpenAPI | ||
) { | ||
private val httpMethods = List("get", "post", "patch", "delete", "options", "trace", "head", "put") | ||
private var serverSchemas: Map[String, Schema] = Map.empty[String, Schema] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we definitely shouldn't have mutable state that's shared among a couple of method invocations. This makes an OpenAPIComparator
instance not re-usable. We either need to pass the state explicitly, or create a private mutable comparator instance which will be single-use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a test that you can reuse the comparator instance
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPIComparator.scala
Outdated
Show resolved
Hide resolved
I have updated the test file names and resolved all issues except for the following two:
I cannot make the
Could you provide an example of a positive test case? Currently, we have the |
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPIComparator.scala
Outdated
Show resolved
Hide resolved
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPIComparator.scala
Outdated
Show resolved
Hide resolved
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPICompatibilityIssue.scala
Outdated
Show resolved
Hide resolved
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPICompatibilityIssue.scala
Outdated
Show resolved
Hide resolved
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPICompatibilityIssue.scala
Show resolved
Hide resolved
case class IncompatibleParameter( | ||
name: String, | ||
subIssues: List[OpenAPICompatibilityIssue] | ||
) extends OpenAPICompatibilitySubIssues { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm so here I'm wondering if the hierarchy is correct - we've got Issues
and SubIssues
, which are also Issues
. Can IncompatibleParameter
be used as a top-level issue, or only as a sub-issue of IncompatibleRequiredParameter
? Should SubIssue extend Issue
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a naming issue—my apologies. What I intended to convey is that the required
value is set to true
on the client side and false
on the server side. However, this concept isn't limited to parameters. it can also apply to other components, such as RequestBody
and Header
.
I will generalize the class for use in different contexts, similar to how the style
attribute is handled.
case class IncompatibleRequiredValue(
clientValue: Boolean,
serverValue: Boolean
) extends OpenAPICompatibilityIssue {
def description: String =
s"required value mismatch: client has $clientValue, but server has $serverValue"
}
The IncompatibleRequiredValue
will simply be a sub-issue of IncompatibleParameter
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so IncompatibleRequiredValue
can be used as a top-level issue as well? That is, it can be used in two contexts: as a top-level and sub-level issue?
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPICompatibilityIssue.scala
Outdated
Show resolved
Hide resolved
...-comparator-tests/src/test/scala/sttp/apispec/openapi/validation/OpenAPIComparatorTest.scala
Outdated
Show resolved
Hide resolved
...-comparator-tests/src/test/scala/sttp/apispec/openapi/validation/OpenAPIComparatorTest.scala
Outdated
Show resolved
Hide resolved
...-comparator-tests/src/test/scala/sttp/apispec/openapi/validation/OpenAPIComparatorTest.scala
Show resolved
Hide resolved
Some(IncompatibleSchema(schemaIssues)) | ||
else | ||
None | ||
case _ => None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't it an incompatibility if one schema is provided, and the other is not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I use MissingClientSchema
and MissingServerSchema
, or should I simply check if the schema is missing from the server and use just MissingSchema
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess just MissingSchema
is enough
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OpenAPI requires that a Parameter
or Header
object defines either a schema
or content
with exactly one entry. I guess it could technically be possible for two parameters/headers to be compatible when one of them has a schema
while the other one has a content
?
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPIComparator.scala
Show resolved
Hide resolved
@abdelfetah18 thanks, this looks good, there's a couple of TODOs left in code still, though? |
Yes, I'll take care of those TODOs and put them in a separate commit to make it easier for you to review. |
…, and allowReserved values
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPICompatibilityIssue.scala
Outdated
Show resolved
Hide resolved
I’ve started working on encoding compatibility issues and will push the changes tomorrow once it’s done, including callbacks and security compatibility. For now, I’ve fixed all the previous issues. Regarding |
I’m done except for the I’ve declared some callbacks in the OpenAPI definition, but when I try to print them, the callback path items are missing. It seems like this might be an encoding issue, causing the paths not to be properly parsed or included when the file is read. Here’s my test file for reference: {
"openapi": "3.0.3",
"info": {
"title": "Simple Pet Store API",
"version": "1.0.0"
},
"paths": {
"/pets": {
"post": {
"summary": "Add a new pet",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
}
}
}
},
"responses": {
"201": {
"description": "Pet created successfully."
}
},
"callbacks": {
"onPetStatusChange": {
"{$request.body#/callbackUrl}": {
"post": {
"summary": "Notify about pet status change",
"description": "This callback is triggered whenever the status of a pet changes.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"description": "The new status of the pet.",
"enum": [
"available",
"pending",
"sold"
]
},
"timestamp": {
"type": "string",
"format": "date-time",
"description": "The time when the status change occurred."
},
"petId": {
"type": "integer",
"description": "The unique ID of the pet whose status changed."
}
},
"required": [
"status",
"timestamp",
"petId"
]
}
}
}
},
"responses": {
"200": {
"description": "Callback successfully processed."
},
"400": {
"description": "Invalid payload provided.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "string",
"description": "Details about the error."
}
}
}
}
}
}
}
}
}
}
}
}
}
}
} |
@abdelfetah18 maybe the circe decoder is incorrect? let's create an issue to fix this (both the decoder & the comparator), and skip callbacks for now |
openapi-model/src/main/scala/sttp/apispec/openapi/validation/OpenAPICompatibilityIssue.scala
Show resolved
Hide resolved
Almost done, thanks! :) I needed to increase the memory for the tests to pass (which is weird ... but probably another issue: #197) |
I've started implementing
OpenAPIComparator
based on the structure outlined in the other issue on the tapir project: softwaremill/tapir#3645 (comment)I've made some progress, but I'd appreciate feedback to ensure I'm heading in the right direction.