Skip to content

Commit

Permalink
Pass request headers to the JS enrichment (close #901)
Browse files Browse the repository at this point in the history
  • Loading branch information
stanch committed Jun 25, 2024
1 parent b681011 commit 1e8b01b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,9 @@ object EnrichmentManager {
_ <- getHttpHeaderContexts // Execute header extractor enrichment
_ <- getYauaaContext[F](registry.yauaa, raw.context.headers) // Runs YAUAA enrichment (gets info thanks to user agent)
_ <- extractSchemaFields[F](unstructEvent) // Extract the event vendor/name/format/version
_ <- registry.javascriptScript.traverse(getJsScript[F](_)) // Execute the JavaScript scripting enrichment
_ <- registry.javascriptScript.traverse( // Execute the JavaScript scripting enrichment
getJsScript[F](_, raw.context.headers)
)
_ <- getCurrency[F](raw.context.timestamp, registry.currencyConversion) // Finalize the currency conversion
_ <- getWeatherContext[F](registry.weather) // Fetch weather context
_ <- geoLocation[F](registry.ipLookups) // Execute IP lookup enrichment
Expand Down Expand Up @@ -347,7 +349,9 @@ object EnrichmentManager {
_ <- getYauaaContext[F](registry.yauaa, raw.context.headers) // Runs YAUAA enrichment (gets info thanks to user agent)
_ <- extractSchemaFields[F](unstructEvent) // Extract the event vendor/name/format/version
_ <- geoLocation[F](registry.ipLookups) // Execute IP lookup enrichment
_ <- registry.javascriptScript.traverse(getJsScript[F](_)) // Execute the JavaScript scripting enrichment
_ <- registry.javascriptScript.traverse( // Execute the JavaScript scripting enrichment
getJsScript[F](_, raw.context.headers)
)
_ <- sqlContexts // Derive some contexts with custom SQL Query enrichment
_ <- apiContexts // Derive some contexts with custom API Request enrichment
_ <- anonIp[F](registry.anonIp) // Anonymize the IP
Expand Down Expand Up @@ -788,12 +792,13 @@ object EnrichmentManager {

// Execute the JavaScript scripting enrichment
def getJsScript[F[_]: Applicative](
javascriptScript: JavascriptScriptEnrichment
javascriptScript: JavascriptScriptEnrichment,
headers: List[String]
): EStateT[F, Unit] =
EStateT.fromEither {
case (event, derivedContexts) =>
ME.formatContexts(derivedContexts).foreach(c => event.derived_contexts = c)
javascriptScript.process(event).leftMap(NonEmptyList.one)
javascriptScript.process(event, headers).leftMap(NonEmptyList.one)
}

def headerContexts[F[_]: Applicative, A](
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*/
package com.snowplowanalytics.snowplow.enrich.common.enrichments.registry

import scala.collection.JavaConverters._

import cats.data.{NonEmptyList, ValidatedNel}
import cats.implicits._

Expand Down Expand Up @@ -73,8 +75,8 @@ final case class JavascriptScriptEnrichment(
private val stringified = rawFunction + s"""
var getJavascriptContexts = function() {
const params = ${params.asJson.noSpaces};
return function(event) {
const result = process(event, params);
return function(event, headers) {
const result = process(event, params, headers);
if (result == null) {
return "[]"
} else {
Expand All @@ -95,11 +97,11 @@ final case class JavascriptScriptEnrichment(
* The event can be updated in-place by the JS function.
* @return either a JSON array of contexts on Success, or an error String on Failure
*/
def process(event: EnrichedEvent): Either[FailureDetails.EnrichmentFailure, List[SelfDescribingData[Json]]] =
def process(event: EnrichedEvent, headers: List[String]): Either[FailureDetails.EnrichmentFailure, List[SelfDescribingData[Json]]] =
invocable
.flatMap(_ =>
Either
.catchNonFatal(engine.invokeFunction("getJavascriptContexts", event).asInstanceOf[String])
.catchNonFatal(engine.invokeFunction("getJavascriptContexts", event, headers.asJava).asInstanceOf[String])
.leftMap(e => s"Error during execution of JavaScript function: [${e.getMessage}]")
)
.flatMap(contexts =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ class JavascriptScriptEnrichmentSpec extends Specification {
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
Javascript enrichment should be able to utilize the headers $e13
"""

val schemaKey =
SchemaKey("com.snowplowanalytics.snowplow", "javascript_script_config", "jsonschema", SchemaVer.Full(1, 0, 0))

def e1 =
JavascriptScriptEnrichment(schemaKey, "[").process(buildEnriched()) must beLeft(
JavascriptScriptEnrichment(schemaKey, "[").process(buildEnriched(), List.empty) must beLeft(
failureContains(_: FailureDetails.EnrichmentFailure, "Error compiling")
)

Expand All @@ -51,7 +52,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
function process(event) {
return { foo: "bar" }
}"""
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched()) must beLeft(
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched(), List.empty) must beLeft(
failureContains(_: FailureDetails.EnrichmentFailure, "not read as an array")
)
}
Expand All @@ -61,7 +62,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
function process(event) {
return [ { foo: "bar" } ]
}"""
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched()) must beLeft(
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched(), List.empty) must beLeft(
failureContains(_: FailureDetails.EnrichmentFailure, "not self-desribing")
)
}
Expand All @@ -74,7 +75,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
data: { appId: event.getApp_id() }
} ];
}"""
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched(appId)) must beRight.like {
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched(appId), List.empty) must beRight.like {
case List(sdj) if sdj.data.noSpaces.contains(appId) => true
case _ => false
}
Expand All @@ -91,7 +92,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
data: { foo: "bar" }
} ];
}"""
JavascriptScriptEnrichment(schemaKey, function).process(enriched)
JavascriptScriptEnrichment(schemaKey, function).process(enriched, List.empty)
enriched.app_id must beEqualTo(newAppId)
}

Expand All @@ -100,7 +101,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
function process(event) {
throw "Error"
}"""
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched()) must beLeft(
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched(), List.empty) must beLeft(
failureContains(_: FailureDetails.EnrichmentFailure, "Error during execution")
)
}
Expand All @@ -110,7 +111,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
function process(event) {
return [ ];
}"""
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched()) must beRight
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched(), List.empty) must beRight
}

def e8 = {
Expand All @@ -133,7 +134,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
SchemaKey("com.acme", "bar", "jsonschema", SchemaVer.Full(1, 0, 0)),
json"""{"hello":"world"}"""
)
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched()) must beRight.like {
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched(), List.empty) must beRight.like {
case List(c1, c2) if c1 == context1 && c2 == context2 => true
case _ => false
}
Expand All @@ -145,7 +146,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
var a = 42 // no-op
}"""

JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched()) must beRight(Nil)
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched(), List.empty) must beRight(Nil)
}

def e10 = {
Expand All @@ -154,7 +155,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
return null
}"""

JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched()) must beRight(Nil)
JavascriptScriptEnrichment(schemaKey, function).process(buildEnriched(), List.empty) must beRight(Nil)
}

def e11 = {
Expand All @@ -165,7 +166,7 @@ class JavascriptScriptEnrichmentSpec extends Specification {
function process(event) {
event.setApp_id("$newAppId")
}"""
JavascriptScriptEnrichment(schemaKey, function).process(enriched)
JavascriptScriptEnrichment(schemaKey, function).process(enriched, List.empty)
enriched.app_id must beEqualTo(newAppId)
}

Expand All @@ -178,7 +179,28 @@ class JavascriptScriptEnrichmentSpec extends Specification {
function process(event, params) {
event.setApp_id(params.nested.foo)
}"""
JavascriptScriptEnrichment(schemaKey, function, params).process(enriched)
JavascriptScriptEnrichment(schemaKey, function, params).process(enriched, List.empty)
enriched.app_id must beEqualTo("newId")
}

def e13 = {
val appId = "greatApp"
val enriched = buildEnriched(appId)
val function =
s"""
function process(event, params, headers) {
for (header of headers) {
const jwt = header.match(/X-JWT:(.+)/i)
if (jwt) {
event.setApp_id(jwt[1].trim())
}
}
}"""

JavascriptScriptEnrichment(schemaKey, function).process(enriched, List.empty)
enriched.app_id must beEqualTo("greatApp")

JavascriptScriptEnrichment(schemaKey, function).process(enriched, List("x-jwt: newId"))
enriched.app_id must beEqualTo("newId")
}

Expand Down

0 comments on commit 1e8b01b

Please sign in to comment.