Skip to content

Commit

Permalink
Fix: same endpoints with different schema not working
Browse files Browse the repository at this point in the history
  • Loading branch information
Narmada M committed Feb 9, 2023
1 parent b9fd2b4 commit 4710ea8
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 23 deletions.
1 change: 1 addition & 0 deletions openapi-merger-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies {
testImplementation(group = "io.kotest", name = "kotest-assertions-core-jvm", version = "4.3.1")
testImplementation(group = "io.kotest", name = "kotest-framework-engine-jvm", version = "4.3.1")
testImplementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.4.1")
testImplementation(group = "io.mockk", name = "mockk", version = "1.12.2")

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,26 @@ import java.util.*
/**
* A class to merge a Map with string keys and value T
*/
class MapMerger<T>: Mergeable<Map<String, T>> {
open class MapMerger<T>: Mergeable<Map<String, T>> {
private val log = LoggerFactory.getLogger(javaClass)
private val map = TreeMap<String, T>()
protected val map = TreeMap<String, T>()

override fun merge(from: Map<String, T>?) {
from?.run {
forEach { entry ->
if (map.containsKey(entry.key)) {
log.warn("{} already exist in the map. Hence skipping", entry.key)
whenKeyExists(entry.key, entry.value)
} else {
map[entry.key] = entry.value
}
}
}
}

open fun whenKeyExists(key: String, value: T) {
log.warn("{} already exist in the map. Hence skipping", key)
}

override fun get(): Map<String, T>? {
return if (map.size > 0) map else null
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.rameshkp.openapi.merger.app.mergers

import io.swagger.v3.oas.models.PathItem
import org.slf4j.LoggerFactory

class PathItemMerger(private val path: String, private val pathItem: PathItem): Mergeable<PathItem> {
private val log = LoggerFactory.getLogger(javaClass)

override fun merge(from: PathItem?) {
from?.apply {
pathItem.setIfMissing("description", { pathItem -> pathItem.description}, { pathItem -> pathItem.description = description })
pathItem.setIfMissing("summary", { pathItem -> pathItem.summary}, { pathItem -> pathItem.summary = summary })
pathItem.setIfMissing("get", { pathItem -> pathItem.get}, { pathItem -> pathItem.get = get })
pathItem.setIfMissing("put", { pathItem -> pathItem.put}, { pathItem -> pathItem.put = put })
pathItem.setIfMissing("post", { pathItem -> pathItem.post}, { pathItem -> pathItem.post = post })
pathItem.setIfMissing("delete", { pathItem -> pathItem.delete}, { pathItem -> pathItem.delete = delete })
pathItem.setIfMissing("options", { pathItem -> pathItem.options}, { pathItem -> pathItem.options = options })
pathItem.setIfMissing("head", { pathItem -> pathItem.head}, { pathItem -> pathItem.head = head })
pathItem.setIfMissing("patch", { pathItem -> pathItem.patch}, { pathItem -> pathItem.patch = patch })
pathItem.setIfMissing("trace", { pathItem -> pathItem.trace}, { pathItem -> pathItem.trace = trace })
pathItem.setIfMissing("servers", { pathItem -> pathItem.servers}, { pathItem -> pathItem.servers = servers })
pathItem.setIfMissing("parameters", { pathItem -> pathItem.parameters}, { pathItem -> pathItem.parameters = parameters })
pathItem.setIfMissing("ref", { pathItem -> pathItem.`$ref`}, { pathItem -> pathItem.`$ref` = `$ref` })
pathItem.setIfMissing("extensions", { pathItem -> pathItem.extensions}, { pathItem -> pathItem.extensions = extensions })
}
}

override fun get(): PathItem {
return pathItem
}

private fun <T> PathItem.setIfMissing(propertyName: String, getProperty: (pathItem: PathItem) -> T?, setProperty: (pathItem: PathItem) -> Unit) {
val property = getProperty(this)
if (property != null) log.warn("{} in path item for path {} already exists. Hence skipping", propertyName, path) else setProperty(this)
}

}
Original file line number Diff line number Diff line change
@@ -1,28 +1,17 @@
package com.rameshkp.openapi.merger.app.mergers

import io.swagger.v3.oas.models.PathItem
import io.swagger.v3.oas.models.Paths
import org.slf4j.LoggerFactory

/**
* A class to merge path objects in open api
*/
class PathsMerger: Mergeable<Paths> {
private val log = LoggerFactory.getLogger(javaClass)
private val paths = Paths()

override fun merge(from: Paths?) {
from?.run {
forEach { entry ->
if (paths.containsKey(entry.key)) {
log.warn("Path entry for {} already exists. Hence skipping", entry.key)
} else {
paths[entry.key] = entry.value
}
}
}
class PathsMerger: MapMerger<PathItem>() {
override fun whenKeyExists(key: String, value: PathItem) {
val existingPathItem = map.getValue(key)
PathItemMerger(key, existingPathItem).merge(value)
}

override fun get(): Paths? {
return if (paths.size > 0) paths else null
override fun get(): Paths {
val path = Paths()
map.forEach { (key, value) -> path.addPathItem(key, value)}
return path
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.rameshkp.openapi.merger.app.mergers

import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.mockk.mockk
import io.swagger.v3.oas.models.PathItem

class PathItemMergerTest: BehaviorSpec({
given("for a path /a with 2 path items p1 and p2") {
val path = "/a"
val p1 = PathItem()
p1.description = "p1 description"
p1.get = mockk()
val p2 = PathItem()
p2.description = "p2 description"
p2.post = mockk()

`when`("PathMerger('/a', p1).merge(p2)") {
PathItemMerger(path, p1).merge(p2)

then("all non null property value of p1 should not be replaced") {
p1.description shouldBe "p1 description"
}
then("all null property value of p1 should be replaced with corresponding p2 property value") {
p1.post shouldNotBe null
}
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.rameshkp.openapi.merger.app.mergers

import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.mockk.mockk
import io.swagger.v3.oas.models.PathItem
import io.swagger.v3.oas.models.Paths

internal class PathsMergerTest : BehaviorSpec({
given("2 paths object with same name and with same scheme") {
`when`("merged using paths merger") {
then("the result should contain one paths object with 1 schema") {
val paths1 = Paths()
val paths2 = Paths()

val pathItem1 = PathItem()
pathItem1.get = mockk()
val pathItem2 = PathItem()
pathItem1.get = mockk()

paths1["/a"] = pathItem1
paths2["/a"] = pathItem2


val pathMerger = PathsMerger()
pathMerger.merge(paths1)
pathMerger.merge(paths2)

val path = pathMerger.get()
path.size shouldBe 1
path["/a"] shouldNotBe null
path["/a"]?.get shouldNotBe null
}
}
}
given("2 paths object with same name and with different schema") {
`when`("merged using path merger") {
then("the result should contain one paths object with 2 schema") {
val paths1 = Paths()
val paths2 = Paths()

val pathItem1 = PathItem()
pathItem1.get = mockk()
val pathItem2 = PathItem()
pathItem1.post = mockk()

paths1["/a"] = pathItem1
paths2["/a"] = pathItem2


val pathMerger = PathsMerger()
pathMerger.merge(paths1)
pathMerger.merge(paths2)

val path = pathMerger.get()
path.size shouldBe 1
path["/a"] shouldNotBe null
path["/a"]?.get shouldNotBe null
path["/a"]?.post shouldNotBe null
}
}
}
})

0 comments on commit 4710ea8

Please sign in to comment.