Skip to content

Commit

Permalink
Upgrade scala to 2.13
Browse files Browse the repository at this point in the history
  • Loading branch information
pondzix committed Feb 14, 2024
1 parent 4b26619 commit 99ad846
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import cats.data.ValidatedNel
import cats.implicits._

import io.circe.{Json => JSON, DecodingFailure, Decoder}
import io.gatling.jsonpath.{JsonPath => GatlingJsonPath}
import com.jayway.jsonpath.{JsonPath => JaywayJsonPath}

import com.snowplowanalytics.iglu.core.{SchemaCriterion, SelfDescribingData}
import com.snowplowanalytics.snowplow.badrows.igluSchemaCriterionDecoder
Expand All @@ -34,7 +34,7 @@ sealed trait Input extends Product with Serializable {

// We could short-circuit enrichment process on invalid JSONPath,
// but it won't give user meaningful error message
def validatedJsonPath: Either[String, GatlingJsonPath] =
def validatedJsonPath: Either[String, JaywayJsonPath] =
this match {
case json: Input.Json => compileQuery(json.jsonPath)
case _ => "No JSON Path given".asLeft
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,66 @@
package com.snowplowanalytics.snowplow.enrich.common.utils

import cats.syntax.either._
import com.fasterxml.jackson.databind.node.ArrayNode
import com.fasterxml.jackson.databind.{ObjectMapper, SerializationFeature}
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider
import com.jayway.jsonpath.{Configuration, JsonPath => JaywayJsonPath, Option => JOption}
import io.circe._
import io.gatling.jsonpath.{JsonPath => GatlingJsonPath}
import io.circe.jackson.{circeToJackson, jacksonToCirce}

/** Wrapper for `io.gatling.jsonpath` for circe and scalaz */
import scala.jdk.CollectionConverters.asScalaIteratorConverter

/** Wrapper for `com.jayway.jsonpath` for circe */
object JsonPath {

/**
* Wrapper method for not throwing an exception on JNothing, representing it as invalid JSON
* @param json JSON value, possibly JNothing
* @return successful POJO on any JSON except JNothing
*/
def convertToJson(json: Json): Object =
io.circe.jackson.mapper.convertValue(json, classOf[Object])
private val JacksonNodeJsonObjectMapper = {
val objectMapper = new ObjectMapper()
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
objectMapper
}
private val JsonPathConf =
Configuration
.builder()
.options(JOption.SUPPRESS_EXCEPTIONS)
.options(JOption.ALWAYS_RETURN_LIST)
.jsonProvider(new JacksonJsonNodeJsonProvider(JacksonNodeJsonObjectMapper))
.build()

/**
* Pimp-up JsonPath class to work with JValue
* Unlike `query(jsonPath, json)` it gives empty list on any error (like JNothing)
* @param jsonPath precompiled with [[compileQuery]] JsonPath object
*/
implicit class CirceExtractor(jsonPath: GatlingJsonPath) {
def circeQuery(json: Json): List[Json] = {
val pojo = convertToJson(json)
jsonPath.query(pojo).map(anyToJson).toList
}
implicit class CirceExtractor(jsonPath: JaywayJsonPath) {
def circeQuery(json: Json): List[Json] =
arrayNodeToCirce(jsonPath.read[ArrayNode](circeToJackson(json), JsonPathConf))
}

/**
* Query some JSON by `jsonPath`. It always return List, even for single match.
* Unlike `json.circeQuery(stringPath)` it gives error if JNothing was given
*/
def query(jsonPath: String, json: Json): Either[String, List[Json]] = {
val pojo = convertToJson(json)
GatlingJsonPath.query(jsonPath, pojo) match {
case Right(iterator) => iterator.map(anyToJson).toList.asRight
case Left(error) => error.reason.asLeft
def query(jsonPath: String, json: Json): Either[String, List[Json]] =
Either.catchNonFatal {
JaywayJsonPath
.using(JsonPathConf)
.parse(circeToJackson(json))
.read[ArrayNode](jsonPath)
} match {
case Right(jacksonArrayNode) =>
arrayNodeToCirce(jacksonArrayNode).asRight
case Left(error) =>
error.getMessage.asLeft
}
}

/**
* Precompile JsonPath query
*
* @param query JsonPath query as a string
* @return valid JsonPath object either error message
*/
def compileQuery(query: String): Either[String, GatlingJsonPath] =
GatlingJsonPath.compile(query).leftMap(_.reason)
def compileQuery(query: String): Either[String, JaywayJsonPath] =
Either.catchNonFatal(JaywayJsonPath.compile(query)).leftMap(_.getMessage)

/**
* Wrap list of values into JSON array if several values present
Expand All @@ -70,12 +85,6 @@ object JsonPath {
case many => Json.fromValues(many)
}

/**
* Convert POJO to JValue with `jackson` mapper
* @param any raw JVM type representing JSON
* @return Json
*/
private[utils] def anyToJson(any: Any): Json =
if (any == null) Json.Null
else CirceUtils.mapper.convertValue(any, classOf[Json])
private def arrayNodeToCirce(jacksonArrayNode: ArrayNode): List[Json] =
jacksonArrayNode.elements().asScala.toList.map(jacksonToCirce)
}
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ class InputSpec extends Specification with ValidatedMatchers with CatsEffect {
unstructEvent = None
)
templateContext must beInvalid.like {
case errors => errors.toList must have length 3
case errors =>
errors.toList must have length 2 // TODO it's not 3 anymore because `"*.invalidJsonPath"` path doesn't fail during jsonpath compilation
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class JsonPathSpec extends Specification {
test query of non-exist value $e2
test query of empty array $e3
test primitive JSON type (JString) $e6
invalid JSONPath (JQ syntax) must fail $e4
test invalid JSONPath (JQ syntax) $e4
invalid JSONPath must fail $e5
test query of long $e7
test query of integer $e8
Expand Down Expand Up @@ -81,13 +81,12 @@ class JsonPathSpec extends Specification {
JsonPath.query("$.store.unicorns", someJson) must beRight(Nil)

def e4 =
JsonPath.query(".notJsonPath", someJson) must beLeft.like {
case f => f must beEqualTo("'$' expected but '.' found")
}
//TODO it's not failure anymore because `.notJsonPath` is not treated as invalid jsonpath by jayway
JsonPath.query(".notJsonPath", someJson) must beRight(Nil)

def e5 =
JsonPath.query("$.store.book[a]", someJson) must beLeft.like {
case f => f must beEqualTo("':' expected but 'a' found")
case f => f must beEqualTo("Could not parse token starting at position 12. Expected ?, ', 0-9, * ")
}

def e6 =
Expand Down
2 changes: 1 addition & 1 deletion project/BuildSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ object BuildSettings {

lazy val projectSettings = Seq(
organization := "com.snowplowanalytics",
scalaVersion := "2.12.15",
scalaVersion := "2.13.12",
licenses += ("Apache-2.0", url("http://www.apache.org/licenses/LICENSE-2.0.html"))
)

Expand Down
3 changes: 0 additions & 3 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ object Dependencies {
val circeJackson = "0.14.0"
val scalaForex = "3.0.0"
val scalaWeather = "2.0.0"
val gatlingJsonpath = "0.6.14"
val scalaUri = "1.5.1"
val badRows = "2.3.0"
val igluClient = "3.1.0"
Expand Down Expand Up @@ -132,7 +131,6 @@ object Dependencies {
val circeOptics = "io.circe" %% "circe-optics" % V.circeOptics
val circeJackson = "io.circe" %% "circe-jackson210" % V.circeJackson
val scalaUri = "io.lemonlabs" %% "scala-uri" % V.scalaUri
val gatlingJsonpath = "io.gatling" %% "jsonpath" % V.gatlingJsonpath
val scalaForex = "com.snowplowanalytics" %% "scala-forex" % V.scalaForex
val refererParser = "com.snowplowanalytics" %% "scala-referer-parser" % V.refererParser
val maxmindIplookups = "com.snowplowanalytics" %% "scala-maxmind-iplookups" % V.maxmindIplookups
Expand Down Expand Up @@ -232,7 +230,6 @@ object Dependencies {
scalaUri,
scalaForex,
scalaWeather,
gatlingJsonpath,
badRows,
igluClient,
snowplowRawEvent,
Expand Down

0 comments on commit 99ad846

Please sign in to comment.