Skip to content
This repository has been archived by the owner on Aug 13, 2024. It is now read-only.

Commit

Permalink
Refactor annotators to produce JNothing (close #65)
Browse files Browse the repository at this point in the history
  • Loading branch information
chuwy committed Jun 22, 2015
1 parent b11c708 commit ddbad4a
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,11 @@ object JsonSchemaGenerator {
* @return some format or none if nothing suites
*/
@tailrec
def guessFormat(value: String, suggestions: List[String => Option[String]]): String = {
def guessFormat(value: String, suggestions: List[String => Option[String]]): Option[String] = {
suggestions match {
case Nil => "none"
case Nil => None
case suggestion :: tail => suggestion(value) match {
case Some(format) => format
case Some(format) => Some(format)
case None => guessFormat(value, tail)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ object JsonSchemaMerger {
case ("maximum", JInt(m)) => ("maximum", JArray(List(m)))

case ("format", JString(f)) => ("format", JArray(List(f)))
case ("format", JNothing) => ("format", JArray(List(JNothing)))

case ("pattern", JString(f)) => ("pattern", JArray(List(f)))
case ("pattern", JNothing) => ("pattern", JArray(List(JNothing)))

case ("items", JArray(items)) => ("items", mergeJsonSchemas(items))
}
Expand Down Expand Up @@ -135,7 +138,8 @@ object JsonSchemaMerger {
map(reduceNumberFieldRange)
map(reduceStringFieldFormat)
map(reduceStringFieldPattern))
case ("enum", JArray(list)) if list.length > enumCardinality => ("enum", JNothing) // delete it
case ("enum", JArray(list)) if list.length > enumCardinality =>
("enum", JNothing) // delete it
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2015 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.snowplowanalytics.schemaguru
package json

import scala.language.implicitConversions

import org.json4s._

/**
* Often used additions to various json4s types
*/
object JValueImplicits {
implicit class JValueImproved(jValue: JValue) {
def removeKey(key: String): JValue = {
jValue.removeField {
case (key, _) => true
case _ => false
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,36 @@
package com.snowplowanalytics.schemaguru
package json

// json4s
import org.json4s._
import org.json4s.JsonDSL._

// This library
import JValueImplicits._

trait SchemaStringHelper extends SchemaHelper {
/**
* Holds information about merged JSON Schema String
*
* @param formats list of all encountered formats
* @param format list of all encountered formats
* @param pattern list of all encountered patterns
*/
private[json] case class StringFieldReducer(formats: List[String], patterns: List[String]) {
def getFormat: String = {
val formatSet = formats.toSet.toList
if (formatSet.size == 1) { formatSet.head }
else { "none" }
}
private[json] case class StringFieldReducer(format: JValue, pattern: JValue) {
/**
* Return format only if single format found
* @return possible format
*/
def getFormat: Option[String] = getProps(format)
def getPattern: Option[String] = getProps(pattern)

def getPattern: String = {
val patternSet = patterns.toSet.toList
if (patternSet.size == 1) { patternSet.head }
else { "none" }
private[json] def getProps(props: JValue): Option[String] = props match {
case JArray(fms) if fms.toSet.size == 1 =>
fms.headOption flatMap {
case JString(s) => Some(s)
case _ => None
}
case JString(s) => Some(s)
case _ => None
}
}

Expand All @@ -43,15 +54,16 @@ trait SchemaStringHelper extends SchemaHelper {
* JObject with type string, and format array
* @return reducer if it is really string field
*/
private[json] def extractStringWithFormat(jField: JValue): Option[StringFieldReducer] = {
def extractStringWithFormat(jField: JValue): Option[StringFieldReducer] = {
val list: List[StringFieldReducer] = for {
JObject(field) <- jField
JField("format", JArray(formats)) <- field
JField("format", formats) <- field
JField("type", types) <- field
if (contains(types, "string"))
} yield StringFieldReducer(formats, Nil)
} yield StringFieldReducer(formats, JNothing)
list.headOption
}

/**
* Tries to extract unreduced string field
* Unreduced state imply it has pattern field as array
Expand All @@ -63,10 +75,10 @@ trait SchemaStringHelper extends SchemaHelper {
private[json] def extractStringWithPattern(jField: JValue): Option[StringFieldReducer] = {
val list: List[StringFieldReducer] = for {
JObject(field) <- jField
JField("pattern", JArray(patterns)) <- field
JField("pattern", patterns) <- field
JField("type", types) <- field
if (contains(types, "string"))
} yield StringFieldReducer(Nil, patterns)
} yield StringFieldReducer(JNothing, patterns)
list.headOption
}

Expand All @@ -80,10 +92,11 @@ trait SchemaStringHelper extends SchemaHelper {
def reduceStringFieldFormat(original: JValue) = {
val stringField = extractStringWithFormat(original)
stringField match {
case Some(reducer) => original.merge(JObject("format" -> JString(reducer.getFormat))) // it may be removed further
.removeField { case JField("format", JString("none")) => true
case _ => false }
case None => original
case None => original // it's not a string field
case Some(reducer) => reducer.getFormat match {
case None => original removeKey("format")
case Some(format) => original merge(("format", format): JObject) // set new format
}
}
}

Expand All @@ -97,10 +110,11 @@ trait SchemaStringHelper extends SchemaHelper {
def reduceStringFieldPattern(original: JValue) = {
val stringField = extractStringWithPattern(original)
stringField match {
case Some(reducer) => original.merge(JObject("pattern" -> JString(reducer.getPattern))) // it may be removed further
.removeField { case JField("pattern", JString("none")) => true
case _ => false }
case None => original
case None => original // it's not a string field
case Some(reducer) => reducer.getFormat match {
case None => original removeKey("pattern")
case Some(pattern) => original merge(("pattern", pattern): JObject) // set new pattern
}
}
}
}
4 changes: 0 additions & 4 deletions src/test/scala/JsonGenValid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ class SelfValidSpecification extends Specification with ScalaCheck with JsonGen
val derivedSchema = asJsonNode(jss)
val schema: JsonSchema = factory.getJsonSchema(derivedSchema)

println(pretty(jss))
println(schema.validate(asJsonNode(json)))
println(pretty(json))

schema.validate(asJsonNode(json)).isSuccess must beTrue
}.set(maxSize = 20)

Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/MergeSpecification.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class MergeSpecification extends Specification { def is = s2"""

val jObjectWithUuid: JObject = ("properties", ("test_key", StringT ~ ("format", JString("uuid"))))
val jObjectWithDateTime: JObject = ("properties", ("test_key", StringT ~ ("format", JString("date-time"))))
val jObjectWithoutFormat: JObject = ("properties", ("test_key", StringT ~ ("format", JString("none"))))
val jObjectWithoutFormat: JObject = ("properties", ("test_key", StringT ~ ("format", None)))

def maintainTypesInArray = {
val merged = mergeJsonSchemas(List(StringT, StringT, StringT, IntegerT, StringT))
Expand Down

0 comments on commit ddbad4a

Please sign in to comment.