10000 #573 Handle nonclass Scala types and add tests by jarrodcodes · Pull Request #574 · http4s/rho · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

#573 Handle nonclass Scala types and add tests #574

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

Merged
merged 2 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 38 additions & 27 deletions swagger/src/main/scala/org/http4s/rho/swagger/TypeBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -210,27 +210,34 @@ object TypeBuilder {
ArrayProperty(items = itemProperty)
} else if (tpe.isOption)
typeToProperty(tpe.typeArgs.head, sfs).withRequired(false)
else if (tpe.isAnyVal && !tpe.isPrimitive)
typeToProperty(
ptSym.asClass.primaryConstructor.asMethod.paramLists.flatten.head.typeSignature,
sfs
)
else if (isCaseClass(ptSym) || (isSumType(ptSym) && !isObjectEnum(ptSym)))
RefProperty(tpe.simpleName)
else
DataType.fromType(tpe) match {
case DataType.ValueDataType(name, format, qName) =>
AbstractProperty(`type` = name, description = qName, format = format)
case DataType.ComplexDataType(name, qName) =>
AbstractProperty(`type` = name, description = qName)
case DataType.ContainerDataType(name, _, _) =>
AbstractProperty(`type` = name)
case DataType.EnumDataType(enums) =>
StringProperty(enums = enums)
else if (tpe.isAnyVal && !tpe.isPrimitive && ptSym.isClass) {
val symbolOption = ptSym.asClass.primaryConstructor.asMethod.paramLists.flatten.headOption
symbolOption match {
case Some(symbol) =>
typeToProperty(
symbol.typeSignature,
sfs
)
case None => dataTypeFromType(tpe)
}
} else if (isCaseClass(ptSym) || (isSumType(ptSym) && !isObjectEnum(ptSym)))
RefProperty(tpe.simpleName)
else dataTypeFromType(tpe)
}
)

private def dataTypeFromType(tpe: Type)(implicit showType: ShowType): Property =
DataType.fromType(tpe) match {
case DataType.ValueDataType(name, format, qName) =>
AbstractProperty(`type` = name, description = qName, format = format)
case DataType.ComplexDataType(name, qName) =>
AbstractProperty(`type` = name, description = qName)
case DataType.ContainerDataType(name, _, _) =>
AbstractProperty(`type` = name)
case DataType.EnumDataType(enums) =>
StringProperty(enums = enums)
}

sealed trait DataType {
def name: String
}
Expand Down Expand Up @@ -288,7 +295,6 @@ object TypeBuilder {

private[swagger] def fromType(t: Type)(implicit st: ShowType): DataType = {
val klass = if (t.isOption && t.typeArgs.nonEmpty) t.typeArgs.head else t

if (klass.isNothingOrNull || klass.isUnitOrVoid)
ComplexDataType("string", qualifiedName = Option(klass.fullName))
else if (isString(klass)) this.String
Expand All @@ -312,21 +318,26 @@ object TypeBuilder {
if (t.typeArgs.nonEmpty) GenArray(fromType(t.typeArgs(1)))
else GenArray()
} else if (klass <:< typeOf[AnyVal]) {
fromType(
klass.members
.filter(_.isConstructor)
.flatMap(_.asMethod.paramLists.flatten)
.head
.typeSignature
)
val klassSymbolOption = klass.members
.filter(_.isConstructor)
.flatMap(_.asMethod.paramLists.flatten)
.headOption
klassSymbolOption match {
case Some(symbol) => fromType(symbol.typeSignature)
case None => fallBackDataTypeFromName(t)
}
} else if (isObjectEnum(klass.typeSymbol)) {
EnumDataType(klass.typeSymbol.asClass.knownDirectSubclasses.map(_.name.toString))
} else {
val stt = if (t.isOption) t.typeArgs.head else t
ComplexDataType("string", qualifiedName = Option(stt.fullName))
fallBackDataTypeFromName(t)
}
}

private def fallBackDataTypeFromName(t: Type)(implicit st: ShowType): DataType = {
val stt = if (t.isOption) t.typeArgs.head else t
ComplexDataType("string", qualifiedName = Option(stt.fullName))
}

private[this] val IntTypes =
Set[Type](
typeOf[Int],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.http4s.rho.swagger

import java.sql.Timestamp
import java.util.Date

import cats.effect.IO
import cats.syntax.all._
import fs2.Stream
Expand All @@ -29,13 +28,15 @@ package object model {
case class FooWithMap(l: Map[String, Int])
case class FooVal(foo: Foo) extends AnyVal
case class BarWithFooVal(fooVal: FooVal)
case class AnyValClass(anyVal: AnyVal)
type AnyValType = AnyVal
case class ClassWithAnyValType(anyVal: AnyValType)
@DiscriminatorField("foobar")
sealed trait Sealed {
def foo: String
}
case class FooSealed(a: Int, foo: String, foo2: Foo) extends Sealed
case class BarSealed(str: String, foo: String) extends Sealed

sealed trait SealedEnum
case object FooEnum extends SealedEnum
case object BarEnum extends SealedEnum
Expand Down Expand Up @@ -413,6 +414,16 @@ class TypeBuilderSuite extends FunSuite {
assertEquals(model, modelOf[Sealed])
}

test("A TypeBuilder should fall back to the class name for a class containing an AnyVal") {
val m = modelOf[AnyValClass].head
assertEquals(m.description, "AnyValClass".some)
}

test("A TypeBuilder should fall back to the class name for a custom type containing an AnyVal") {
val m = modelOf[ClassWithAnyValType].head
assertEquals(m.description, "ClassWithAnyValType".some)
}

test("A TypeBuilder should build a model for two-level sealed trait hierarchy") {
val ms = modelOf[TopLevelSealedTrait]
assertEquals(ms.size, 5)
Expand Down
0