Skip to content

Commit

Permalink
SchemaComparator class is public so that it can be reused for its cache
Browse files Browse the repository at this point in the history
  • Loading branch information
ghik committed Apr 23, 2024
1 parent 88e7a55 commit 078f9a1
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@ import scala.collection.mutable

object SchemaComparator {
final val RefPrefix = "#/components/schemas/"
}

/**
* Utility for comparing schemas for compatibility.
* See [[compare]] for more details.
*
* Since this class contains a cache of comparison results,
* it is meant to be reused between multiple schema comparisons.
*
* @param writerNamedSchemas named schemas which may be referred to by the writer schema
* @param readerNamedSchemas named schemas which may be referred to by the reader schema
*/
class SchemaComparator(
writerNamedSchemas: Map[String, Schema],
readerNamedSchemas: Map[String, Schema]
) {

import SchemaComparator._

private val cache = new mutable.HashMap[(Schema, Schema), List[SchemaCompatibilityIssue]]

// keeps schema pairs for which comparison is ongoing in order to short circuit recursive schema comparisons
private val inComparison = new mutable.HashSet[(Schema, Schema)]

/**
* Compares two schemas for compatibility. More precisely, checks if data that is valid according to [[writerSchema]]
Expand All @@ -25,35 +48,11 @@ object SchemaComparator {
* Before being compared, all schemas are stripped of keywords which do not affect the comparison, e.g. annotations
* like `title`, `description`, etc.
*
* @param writerSchema schema of the data being written
* @param readerSchema schema of the data being read
* @param writerNamedSchemas named schemas which may be referred to by the writer schema
* @param readerNamedSchemas named schemas which may be referred to by the reader schema
* @param writerSchema schema of the data being written
* @param readerSchema schema of the data being read
* @return a list of incompatibilities between the schemas
*/
def compare(
writerSchema: Schema,
readerSchema: Schema,
writerNamedSchemas: Map[String, Schema] = Map.empty,
readerNamedSchemas: Map[String, Schema] = Map.empty
): List[SchemaCompatibilityIssue] =
new SchemaComparator(writerNamedSchemas, readerNamedSchemas)
.compare(writerSchema, readerSchema)
}

private class SchemaComparator(
writerNamedSchemas: Map[String, Schema],
readerNamedSchemas: Map[String, Schema]
) {

import SchemaComparator._

private val cache = new mutable.HashMap[(Schema, Schema), List[SchemaCompatibilityIssue]]

// keeps schema pairs for which comparison is ongoing in order to short circuit recursive schema comparisons
private val inComparison = new mutable.HashSet[(Schema, Schema)]

private def compare(writerSchema: SchemaLike, readerSchema: SchemaLike): List[SchemaCompatibilityIssue] = {
def compare(writerSchema: SchemaLike, readerSchema: SchemaLike): List[SchemaCompatibilityIssue] = {
val normalizedWriterSchema = normalize(writerSchema, writerNamedSchemas)
val normalizedReaderSchema = normalize(readerSchema, readerNamedSchemas)
val cacheKey = (normalizedWriterSchema, normalizedReaderSchema)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class SchemaComparatorTest extends AnyFunSuite {
Schema.referenceTo(SchemaComparator.RefPrefix, name)

private def compare(writerSchema: Schema, readerSchema: Schema): List[SchemaCompatibilityIssue] =
SchemaComparator.compare(writerSchema, readerSchema, writerSchemas, readerSchemas)
new SchemaComparator(writerSchemas, readerSchemas).compare(writerSchema, readerSchema)

test("ignoring annotations") {
assert(compare(
Expand Down

0 comments on commit 078f9a1

Please sign in to comment.