Support array result for common action and sequence action#5290
Conversation
| rootControllerIndex: ControllerInstanceId, | ||
| blocking: Boolean, | ||
| content: Option[JsObject], | ||
| content: Option[JsValue], |
There was a problem hiding this comment.
- For common action to pass parameter as dict, it would use
Option[JsObject] - For sequence action to pass parameter as array, it would use
Option[JsArray], e.g. the first action's JsArray result as the next action's input param.
| val JsString(data) = r | ||
| data.parseJson.asJsObject | ||
| data.parseJson match { | ||
| case JsArray(elements) => JsArray(elements) |
There was a problem hiding this comment.
Support store the JsArray result to ElasticSearch.
| var payload = msg.payload.toString(); | ||
| var lines = payload.split(separator); | ||
| // return array as next action's input | ||
| return lines; |
There was a problem hiding this comment.
As comment said, return array as next action's input
This script file is used as test case for invoke sequence action which support array result
| activation.response.result shouldBe Some( | ||
| JsArray(JsObject("key1" -> JsString("value1")), JsObject("key2" -> JsString("value2")))) | ||
| } | ||
| } |
There was a problem hiding this comment.
Because nodejs runtime suports array result by default, add a system test case for return array result for it to test controller/invoker whether works well
| trait ActionContainer { | ||
| def init(value: JsValue): (Int, Option[JsObject]) | ||
| def run(value: JsValue): (Int, Option[JsObject]) | ||
| def runForJsArray(value: JsValue): (Int, Option[JsArray]) |
There was a problem hiding this comment.
Normally, need to change def run(...) as below
def run(value: JsValue): (Int, Option[JsValue]) // Option[JsObject] -> Option[JsValue]
But i just add another new method here, two reasons.
- If change
def rundirectly, there has a lot of code changes. - It is just for test, so i just add another new method, there has no need to introduce too many code modifications
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5290 +/- ##
===========================================
+ Coverage 45.04% 75.05% +30.00%
===========================================
Files 238 238
Lines 14179 14202 +23
Branches 617 608 -9
===========================================
+ Hits 6387 10659 +4272
+ Misses 7792 3543 -4249 ☔ View full report in Codecov by Sentry. |
style95
left a comment
There was a problem hiding this comment.
It generally looks good to me.
| JsHelpers | ||
| .getFieldPath(JsObject(fields), ERROR_FIELD, "statusCode") | ||
| .orElse(JsHelpers.getFieldPath(JsObject(fields), "statusCode")) | ||
| case _ => None |
There was a problem hiding this comment.
This is for the array result case and there could be no statusCode field in the array result, right?
There was a problem hiding this comment.
Yes, for array result, seems can't get the userDefined statusCode
| implicit logging: Logging, | ||
| as: ActorSystem, | ||
| ec: ExecutionContext, | ||
| tid: TransactionId): (Int, Option[JsArray]) = { |
There was a problem hiding this comment.
Is there any reason that we can't just change the result type to (Int, Option[JsValue])?
There was a problem hiding this comment.
def runForJsArrayis added for test case, the return value is(Int, Option[JsArray])- call chain here is:
def runForJsArray->private def syncPostForJsArray->def postForJsArray
Actually, in order to support test, we can change below method to support return (Int, Option[JsValue]) also

But if we change the run directly, there would be a lot of changes in openwhisk repo and all runtime codes should change as well.
So here, i added another extra method to test return array

This is just for impact the original code as little as possible: #5290 (comment)
| val sizeOpt = Option(str).map(_.length) | ||
| Try { str.parseJson.asJsObject } match { | ||
| Try { str.parseJson } match { | ||
| case scala.util.Success(result @ JsObject(fields)) => |
There was a problem hiding this comment.
So even without asJsObject, the result would match here?
Then, it was not necessary in the first place?
| val errorField = payloadContent.fields.get(ActivationResponse.ERROR_FIELD) | ||
| val errorField: Option[JsValue] = activation.response.result match { | ||
| case Some(JsObject(fields)) => fields.get(ActivationResponse.ERROR_FIELD) | ||
| case _ => None |
There was a problem hiding this comment.
So users can't define the explicit error response when the result is a JSON array.
But there would be no difference in the JSON-object-based action, right?
Sometimes, libraries return an error filed and OW considers that as an error of an action as well.
Since there would be no behavioral difference in the JSON-object-based actions, there would be no difference in existing semantics, right?
There was a problem hiding this comment.
- So users can't define the explicit error response when the result is a JSON array.
Yes, can't define the explicit error response for a JSON array,
but if we want to define the explicit error response, normally, user should return the JSON-object result. - But there would be no difference in the JSON-object-based action, right?
For the JSON-object-based action, have no any bad influences on these actions, have no any change. - Regarding
Sometimes, libraries return an error filed and OW considers that as an error of an action as well.
Since there would be no behavioral difference in the JSON-object-based actions, there would be no difference in existing semantics, right?
I am not sure whether i understand correctly, if libraries return an error or user's codes has some error, normally, there would has catch(....) statement in user's code, and in catch statement, user can return the error or some excetion error with JSON-object result.
If user codes run well without any error or exception, can return array result finally.
| case None => (Map.empty, JsObject.empty) | ||
| case Some(JsObject(fields)) if initArgs.isEmpty => (Map.empty, JsObject(fields)) | ||
| case Some(JsObject(fields)) => | ||
| val (env, args) = JsObject(fields).fields.partition(k => initArgs.contains(k._1)) | ||
| (env, JsObject(args)) | ||
| case Some(JsArray(elements)) => (Map.empty, JsArray(elements)) |
There was a problem hiding this comment.
| case None => (Map.empty, JsObject.empty) | |
| case Some(JsObject(fields)) if initArgs.isEmpty => (Map.empty, JsObject(fields)) | |
| case Some(JsObject(fields)) => | |
| val (env, args) = JsObject(fields).fields.partition(k => initArgs.contains(k._1)) | |
| (env, JsObject(args)) | |
| case Some(JsArray(elements)) => (Map.empty, JsArray(elements)) | |
| case None => (Map.empty, JsObject.empty) | |
| case Some(JsArray(elements)) => (Map.empty, JsArray(elements)) | |
| case Some(fields) if initArgs.isEmpty => (Map.empty, fields) | |
| case Some(fields) => | |
| val (env, args) = fields.fields.partition(k => initArgs.contains(k._1)) | |
| (env, JsObject(args)) | |
This one can be changed like this?
There was a problem hiding this comment.
No, would report error, but there has a optimize point here.
Change JsObject(fields).fields to fields
| } | ||
| } | ||
|
|
||
| it should "invoke a sequence which support array result" in withAssetCleaner(wskprops) { (wp, assetHelper) => |
There was a problem hiding this comment.
| it should "invoke a sequence which support array result" in withAssetCleaner(wskprops) { (wp, assetHelper) => | |
| it should "invoke a sequence which supports array result" in withAssetCleaner(wskprops) { (wp, assetHelper) => |
There was a problem hiding this comment.
Updated accordingly
| val params = Map("params" -> JsObject(wrappedParams ++ escapedParams)) | ||
| JsObject(params ++ action ++ state) | ||
| } | ||
| case _ => JsObject.empty |
There was a problem hiding this comment.
If we just do this, can we support conductor actions with JSON array results?
There was a problem hiding this comment.
hm..i am not sure, if want to support, i think we can submit in next pr.
Couchdb already suports
This test case is just for nodejs
460fdff to
84d220f
Compare
|
It generally looks good to me. |
Yes, has no issue. Should merge this first, because other runtime prs depend on this pr. |
|
Have any comment? due to there exist several runtime pr depend on this pr, so i just asked. |
style95
left a comment
There was a problem hiding this comment.
I am ok with this change if there is no side effect to existing features.
* Support array result * Make controller accept json array * Make elasticsearch support json array Couchdb already suports * Make go runtime test cases due to depend on this * Add test case for array result for nodejs runtime * Make sequence action to support array result * Optimize sequence action to support array result * Fix test case for sequence action feature * Add test case for sequence action This test case is just for nodejs * Add extra method runForJsArray for runtime tests * Fix build error * Fix review comment

Description
POEM document pr: #5244
This feature is: provide support array result feature for common action and sequence action. e.g.
The
response.resultmust usetextto store, because different activation's same filed may use different type, e.g.one activation's result's name filed value is string , e.g. {"name": "jack"}
another activation's result's name filed value is int, e.g. {"name": 12}
Elasticsearch doesn't support this, it store like this, it would report error.
As above example, we already know,
nodejsruntime supports this feature by default.But other runtimes(e.g. go, java, php, etc) don't support,
Related issue and scope
My changes affect the following components
Types of changes
Checklist: