Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,21 @@ class SchemaMacroSpec extends WordSpec {
import `object`.Field

Json.schema[Foo3] shouldEqual `object`(
Field("name" , `string`[String](None, None), required = false),
Field("bar" , `integer`, required = false),
Field("active" , `boolean`, required = false))
Field("name" , `string`[String](None, None), required = false, default = "xxx"),
Field("bar" , `integer`, required = false, default = 5),
Field("active" , `boolean`, required = false, default = true))
}

"generate schema for case class of array fields with default values" in {
import `object`.Field

Json.schema[Bar9] shouldEqual `object`(
Field("seq" , `array`(`string`[String](None, None)), required = false, default = Seq.empty[String]),
Field("set" , `set`(`integer`), required = false, default = Set(1, 5, 9)),
Field("list" , `array`(`boolean`), required = false, default = List(true, false)),
Field("vector" , `array`(`number`[Long]), required = false, default = Vector(9, 7)),
Field("strMap" , `string-map`(`number`[Double]), required = false, default = Map("foo" -> .12)),
Field("intMap" , `int-map`(`string`[String](None, None)), required = false, default = Map(1 -> "1", 2 -> "2")))
}

"generate references for implicitly defined dependencies" in {
Expand Down Expand Up @@ -172,6 +184,14 @@ object SchemaMacroSpec {
case class Bar8(foo: String) extends AnyVal

case class Foo9(name: String)

case class Bar9(
seq: Seq[String] = Seq.empty,
set: Set[Int] = Set(1, 5, 9),
list: List[Boolean] = List(true, false),
vector: Vector[Long] = Vector(9L, 7L),
strMap: Map[String, Double] = Map("foo" -> .12),
intMap: Map[Int, String] = Map(1 -> "1", 2 -> "2"))
}

sealed trait FooBar
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.github.andyglow.jsonschema.model

import com.github.andyglow.json.{ToValue, Value}

// members goes without companion

sealed trait Active

case object On extends Active
case object Off extends Active
case object Suspended extends Active

object Active {

// ToValue is explicitly specified
implicit val ActiveV: ToValue[Active] = ToValue mk {
case On => Value.str("On")
case Off => Value.str("Off")
case Suspended => Value.str("Suspended")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.andyglow.jsonschema.model

sealed trait BetaFeature

case object F0 extends BetaFeature
case object F1 extends BetaFeature
case object F2 extends BetaFeature
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.andyglow.jsonschema.model


case class Credentials(login: String, password: String)

15 changes: 15 additions & 0 deletions api/src/test/scala/com/github/andyglow/jsonschema/model/Role.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.github.andyglow.jsonschema.model


// members goes inside companion

sealed trait Role

object Role {

case object User extends Role

case object Admin extends Role

case object Manager extends Role
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.andyglow.jsonschema.model

case class UserProfile(
firstName: String,
middleName: Option[String],
lastName: String,
age: Int,
enabledFeatures: Set[BetaFeature] = Set(F0, F1),
active: Active = On,
credentials: Credentials = Credentials("anonymous", "-"),
role: Role = Role.User)
26 changes: 15 additions & 11 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ lazy val commonSettings = Seq(

scalaVersion := "2.11.12",

crossScalaVersions := Seq("2.11.12", "2.12.8", "2.13.0"),
crossScalaVersions := Seq("2.11.12", "2.12.10", "2.13.1"),

scalacOptions ++= {
val options = Seq(
Expand All @@ -30,7 +30,8 @@ lazy val commonSettings = Seq(
"-Yno-adapted-args",
"-Ywarn-dead-code",
"-Ywarn-numeric-widen",
"-Xfuture")
"-Xfuture",
"-language:higherKinds")

// WORKAROUND https://github.com/scala/scala/pull/5402
CrossVersion.partialVersion(scalaVersion.value) match {
Expand Down Expand Up @@ -109,7 +110,7 @@ lazy val macros = project in file("macros") dependsOn core settings (
name := "scala-jsonschema-macros",

libraryDependencies ++= Seq(
(scalaVersion apply ("org.scala-lang" % "scala-reflect" % _ % Compile)).value)
(scalaVersion apply ("org.scala-lang" % "scala-reflect" % _ % Compile)).value.withSources.withJavadoc)
)

lazy val api = { project in file("api") }.dependsOn(core, macros).settings(
Expand All @@ -118,23 +119,23 @@ lazy val api = { project in file("api") }.dependsOn(core, macros).settings(
name := "scala-jsonschema-api"
)

lazy val `play-json` = { project in file("play-json") }.dependsOn(core, api).settings(
lazy val `play-json` = { project in file("play-json") }.dependsOn(core, api % "compile->compile;test->test").settings(
commonSettings,

name := "scala-jsonschema-play-json",

libraryDependencies += "com.typesafe.play" %% "play-json" % "2.7.4"
)

lazy val `spray-json` = { project in file("spray-json") }.dependsOn(core, api).settings(
lazy val `spray-json` = { project in file("spray-json") }.dependsOn(core, api % "compile->compile;test->test").settings(
commonSettings,

name := "scala-jsonschema-spray-json",

libraryDependencies += "io.spray" %% "spray-json" % "1.3.5"
)

lazy val `circe-json` = { project in file("circe-json") }.dependsOn(core, api).settings(
lazy val `circe-json` = { project in file("circe-json") }.dependsOn(core, api % "compile->compile;test->test").settings(
commonSettings,

name := "scala-jsonschema-circe-json",
Expand All @@ -150,26 +151,29 @@ lazy val `circe-json` = { project in file("circe-json") }.dependsOn(core, api).s
}
)

lazy val `json4s-json` = { project in file("json4s-json") }.dependsOn(core, api).settings(
lazy val `json4s-json` = { project in file("json4s-json") }.dependsOn(core, api % "compile->compile;test->test").settings(
commonSettings,

name := "scala-jsonschema-json4s-json",

libraryDependencies += "org.json4s" %% "json4s-ast" % "3.6.7"
libraryDependencies += "org.json4s" %% "json4s-core" % "3.6.7"
)

lazy val `u-json` = { project in file("u-json") }.dependsOn(core, api).settings(
lazy val `u-json` = { project in file("u-json") }.dependsOn(core, api % "compile->compile;test->test").settings(
commonSettings,

name := "scala-jsonschema-ujson",

libraryDependencies += {
libraryDependencies ++= {
val ujsonVersion = CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 11)) => "0.7.4"
case _ => "0.7.5"
}

"com.lihaoyi" %% "ujson" % ujsonVersion
Seq(
"com.lihaoyi" %% "ujson" % ujsonVersion,
"com.lihaoyi" %% "upickle" % ujsonVersion)

}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,21 @@ object AsCirce {

def asCirce[V <: Version](v: V)(implicit asValue: AsValueBuilder[V]): Json = AsCirce(AsValue.schema(x, v))
}

implicit def toValue[T](implicit w: Encoder[T]): ToValue[T] = new ToValue[T] {
override def apply(x: T): Value = {
val js = w.apply(x)
def translate(js: Json): Value = js match {
case Json.Null => `null`
case Json.True => `true`
case Json.False=> `false`
case x if x.isNumber => num(x.asNumber.get.toDouble)
case x if x.isString => str(x.asString.get)
case x if x.isArray => val a = x.asArray.get map translate; arr(a)
case x if x.isObject => val map = x.asObject.get.toMap mapV translate; obj(map)
}

translate(js)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import org.scalatest._
import org.scalatest.Matchers._
import org.scalatest.prop.TableDrivenPropertyChecks._
import com.github.andyglow.json.Value._
import com.github.andyglow.jsonschema.model.UserProfile
import io.circe._
import json.schema.Version.Draft04
import org.scalactic.Equality

class AsCirceSpec extends PropSpec {
import AsCirceSpec._
import UserProfileJson._

private val examples = Table(
("json" , "Circe"),
Expand Down Expand Up @@ -39,21 +41,22 @@ class AsCirceSpec extends PropSpec {
"middleName" -> Json.obj("type" -> Json.fromString("string")),
"lastName" -> Json.obj("type" -> Json.fromString("string")),
"age" -> Json.obj("type" -> Json.fromString("integer")),
"active" -> Json.obj("type" -> Json.fromString("boolean"))),
"role" -> Json.obj("type" -> Json.fromString("string"), "default" -> Json.fromString("e-user"), "enum" -> Json.arr(Json.fromString("e-admin"), Json.fromString("e-manager"), Json.fromString("e-user"))),
"active" -> Json.obj("type" -> Json.fromString("string"), "default" -> Json.fromString("On"), "enum" -> Json.arr(Json.fromString("On"), Json.fromString("Off"), Json.fromString("Suspended"))),
"enabledFeatures" -> Json.obj("type" -> Json.fromString("array"), "uniqueItems" -> Json.True, "default" -> Json.arr(Json.fromString("feature-0-name"), Json.fromString("feature-1-name")), "items" -> Json.obj("type" -> Json.fromString("string"), "enum" -> Json.arr(Json.fromString("feature-0-name"), Json.fromString("feature-1-name"), Json.fromString("feature-2-name")))),
"credentials" -> Json.obj("type" -> Json.fromString("object"),
"additionalProperties" -> Json.False,
"required" -> Json.arr(Json.fromString("login"), Json.fromString("password")),
"properties" -> Json.obj(
"login" -> Json.obj("type" -> Json.fromString("string")),
"password" -> Json.obj("type" -> Json.fromString("string"))),
"default" -> Json.obj("login" -> Json.fromString("anonymous"), "password" -> Json.fromString("-")))),
"required" -> Json.arr(Json.fromString("age"), Json.fromString("lastName"), Json.fromString("firstName")))
}
}

object AsCirceSpec {

case class UserProfile(
firstName: String,
middleName: Option[String],
lastName: String,
age: Int,
active: Boolean = true)


implicit val jsValEq: Equality[Json] = new Equality[Json] {
override def areEqual(a: Json, b: Any): Boolean = a match {
case Json.Null => b == Json.Null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.github.andyglow.jsonschema


import com.github.andyglow.jsonschema.model._
import io.circe._
import io.circe.generic.semiauto._

object UserProfileJson {

implicit val CredentialsW: Encoder[Credentials] = deriveEncoder[Credentials]

implicit val BetaFeatureW: Encoder[BetaFeature] = new Encoder[BetaFeature] {
override def apply(o: BetaFeature): Json = o match {
case F0 => Json.fromString("feature-0-name")
case F1 => Json.fromString("feature-1-name")
case F2 => Json.fromString("feature-2-name")
}
}

implicit val RoleW: Encoder[Role] = new Encoder[Role] {
import Role._

override def apply(o: Role): Json = o match {
case User => Json.fromString("e-user")
case Manager => Json.fromString("e-manager")
case Admin => Json.fromString("e-admin")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.andyglow.json


trait LowPriorityArrayImplicits { this: LowPriorityPrimitiveImplicits =>
import Value._
import ToValue._

implicit def ArrV[T, C[_] <: Traversable[_]](implicit
to: ToValue[T]): ToValue[C[T]] = {

mk { items =>
val v = items map { v: Any => to(v.asInstanceOf[T]) }
arr(v.toSeq)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.andyglow.json


trait LowPriorityArrayImplicits { this: LowPriorityPrimitiveImplicits =>
import Value._
import ToValue._

implicit def ArrV[T, C[_] <: Traversable[_]](implicit
to: ToValue[T]): ToValue[C[T]] = {

mk { items =>
val v = items map { v: Any => to(v.asInstanceOf[T]) }
arr(v.toSeq)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.andyglow.json


trait LowPriorityArrayImplicits { this: LowPriorityPrimitiveImplicits =>
import Value._
import ToValue._

implicit def ArrV[T, C[_] <: Iterable[_]](implicit
to: ToValue[T]): ToValue[C[T]] = {

mk { items =>
val v = items map { v: Any => to(v.asInstanceOf[T]) }
arr(v.toSeq)
}
}
}
Loading