I am trying to create my gRPC payload by reading data from a csv file(it has guestID and category as columns). I followed the example here https://github.com/phiSgr/gatling-grpc/blob/244ab372da6773102d79c65a7e4086f409d3fe94/src/test/scala/com/github/phisgr/example/GrpcExample.scala but I see a type mismatch error when I try the same. (it expects Seq[ContextKey] but here i am able to form Seq[Expression[ContextKey]])
val scn2: ScenarioBuilder = scenario("gRPC call - 50 users repeated 100 times")
.feed(csv("testtext.csv"))
.exec(
grpc("gRPC request with test message")
.rpc(RecommenderGrpc.METHOD_GET_RECOMMENDATIONS)
.payload(RequestContext.of(Map("test" -> "test"),
Seq(ContextKey.defaultInstance.updateExpr(
_.id :~ $("guestID"),
_.`type` :~ Type.GUEST
), ContextKey.defaultInstance.updateExpr(
_.id :~ $("category"),
_.`type` :~ Type.CATEGORY
)),
Seq())
)
)
(payload is a RequestContext object which takes in metadata, keys and items. metadata is a map, keys is a Seq of ContextKey and items is empty Seq. ContextKey contains string guestID or category and type).
How to use the variables in the feeder here?
Skip to the bottom for the solution.
Expression[T]
is an alias for Session => Validation[T]
. In plain English, that is a function that constructs the payload from the session with a possibility of failure.
You can consider an Expression[T]
, abstractly, "contains" a T
.
Like how a Promise
in JavaScript "contains" a future value. You cannot give a Promise
of T
to a function that expects a T
. If one wants to transform or combine Promise
s, that code has to be turned inside out, and supplied as an argument to .then
.1
aPromise + 1 // wrong
aPromise.then(a => a + 1)
This is the same reason why your code sample does not compile.
Users of Gatling are not necessarily familiar with Scala, or functional programming in general. It will be counterproductive to make them understand this "wrapping" stuff.2 So there are code that help you combine the Expression
s.
For HTTP and other untyped stuff, the EL string is parsed and transformed in to an Expression
under the hood.
Protobuf messages are strongly typed, the payload cannot be easily constructed using string interpolation. So the :~
operators on lenses are used to handle the plumbing so that you do not have to manually handle the Expression
wrapping.
But now you have a function, RequestContext.of
, that constructs the payload. The lens magic cannot help if you need that function. You have to write the Expression
lambda yourself.3
.payload { session =>
for {
guestId <- session("guestId").validate[String]
category <- session("category").validate[String]
} yield RequestContext.of(
Map("test" -> "test"),
Seq(
ContextKey(id = guestID, `type` = Type.GUEST),
ContextKey(id = category, `type` = Type.CATEGORY)
),
Seq()
)
}
Needless to say this is very cumbersome, and people now use async-await with Promise
s.
An Expression
is just the Either
monad and the Reader
monad stacked together, what's the problem?
I may be able to write a version with lenses if I know what RequestContext.of
does.