Skip to content

Commit

Permalink
Allow passing an object of parameters to the JS enrichment
Browse files Browse the repository at this point in the history
  • Loading branch information
stanch authored and spenes committed Feb 9, 2024
1 parent 57cb773 commit aa18625
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import cats.data.EitherT

import cats.effect.kernel.{Async, Sync}

import io.circe.JsonObject

import org.joda.money.CurrencyUnit

import com.snowplowanalytics.iglu.core.SchemaKey
Expand Down Expand Up @@ -192,8 +194,12 @@ object EnrichmentConf {
)
}

final case class JavascriptScriptConf(schemaKey: SchemaKey, rawFunction: String) extends EnrichmentConf {
def enrichment: JavascriptScriptEnrichment = JavascriptScriptEnrichment(schemaKey, rawFunction)
final case class JavascriptScriptConf(
schemaKey: SchemaKey,
rawFunction: String,
params: JsonObject
) extends EnrichmentConf {
def enrichment: JavascriptScriptEnrichment = JavascriptScriptEnrichment(schemaKey, rawFunction, params)
}

final case class RefererParserConf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import cats.implicits._

import io.circe._
import io.circe.parser._
import io.circe.syntax._

import javax.script._

Expand Down Expand Up @@ -52,27 +53,35 @@ object JavascriptScriptEnrichment extends ParseableEnrichment {
_ <- isParseable(c, schemaKey)
encoded <- CirceUtils.extract[String](c, "parameters", "script").toEither
script <- ConversionUtils.decodeBase64Url(encoded)
params <- CirceUtils.extract[Option[JsonObject]](c, "parameters", "config").toEither
_ <- if (script.isEmpty) Left("Provided script for JS enrichment is empty") else Right(())
} yield JavascriptScriptConf(schemaKey, script)).toValidatedNel
} yield JavascriptScriptConf(schemaKey, script, params.getOrElse(JsonObject.empty))).toValidatedNel
}

final case class JavascriptScriptEnrichment(schemaKey: SchemaKey, rawFunction: String) extends Enrichment {
final case class JavascriptScriptEnrichment(
schemaKey: SchemaKey,
rawFunction: String,
params: JsonObject = JsonObject.empty
) extends Enrichment {
private val enrichmentInfo =
FailureDetails.EnrichmentInformation(schemaKey, "Javascript enrichment").some

private val engine = new ScriptEngineManager(null)
.getEngineByMimeType("text/javascript")
.asInstanceOf[ScriptEngine with Invocable with Compilable]

private val stringified = rawFunction + """
function getJavascriptContexts(event) {
var result = process(event);
if (result == null) {
return "[]"
} else {
return JSON.stringify(result);
private val stringified = rawFunction + s"""
var getJavascriptContexts = function() {
const params = ${params.asJson.noSpaces};
return function(event) {
const result = process(event, params);
if (result == null) {
return "[]"
} else {
return JSON.stringify(result);
}
}
}
}()
"""

private val invocable =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,39 @@ class EnrichmentConfigsSpec extends Specification with ValidatedMatchers with Da
val result = JavascriptScriptEnrichment.parse(javascriptScriptEnrichmentJson, schemaKey)
result must beValid // TODO: check the result's contents by evaluating some JavaScript
}
"parse the additional arguments" in {
val params = json"""{"foo": 3, "nested": {"bar": 42}}""".asObject.get
val script =
s"""|function process(event, params) {
| return [];
|}
|""".stripMargin
val javascriptScriptEnrichmentJson = {
val encoder = new Base64(true)
val encoded = new String(encoder.encode(script.getBytes)).trim // Newline being appended by some Base64 versions
parse(s"""{
"enabled": true,
"parameters": {
"script": "$encoded",
"config": {
"foo": 3,
"nested": {
"bar": 42
}
}
}
}""").toOption.get
}
val schemaKey = SchemaKey(
"com.snowplowanalytics.snowplow",
"javascript_script_config",
"jsonschema",
SchemaVer.Full(1, 0, 0)
)
val result = JavascriptScriptEnrichment.parse(javascriptScriptEnrichmentJson, schemaKey)
result must beValid
result.map(_.params).toOption mustEqual Some(params)
}
}

"Parsing a valid event_fingerprint_config enrichment JSON" should {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
Javascript enrichment should be able to proceed without return statement $e9
Javascript enrichment should be able to proceed with return null $e10
Javascript enrichment should be able to update the fields without return statement $e11
Javascript enrichment should be able to utilize the passed parameters $e12
"""

val schemaKey =
Expand Down Expand Up @@ -168,6 +169,19 @@ class JavascriptScriptEnrichmentSpec extends Specification {
enriched.app_id must beEqualTo(newAppId)
}

def e12 = {
val appId = "greatApp"
val enriched = buildEnriched(appId)
val params = json"""{"foo": "bar", "nested": {"foo": "newId"}}""".asObject.get
val function =
s"""
function process(event, params) {
event.setApp_id(params.nested.foo)
}"""
JavascriptScriptEnrichment(schemaKey, function, params).process(enriched)
enriched.app_id must beEqualTo("newId")
}

def buildEnriched(appId: String = "my super app"): EnrichedEvent = {
val e = new EnrichedEvent()
e.platform = "server"
Expand Down
5 changes: 4 additions & 1 deletion project/BuildSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,10 @@ object BuildSettings {
// Build and publish
publishSettings ++
// Tests
scoverageSettings ++ noParallelTestExecution
scoverageSettings ++ noParallelTestExecution ++ Seq(
Test / fork := true,
Test / javaOptions := Seq("-Dnashorn.args=--language=es6")
)
}

lazy val commonFs2BuildSettings = {
Expand Down

0 comments on commit aa18625

Please sign in to comment.