rabbah commented on a change in pull request #3187: reduce rule activation 
records
URL: 
https://github.com/apache/incubator-openwhisk/pull/3187#discussion_r165063674
 
 

 ##########
 File path: core/controller/src/main/scala/whisk/core/controller/Triggers.scala
 ##########
 @@ -390,46 +300,130 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
   }
 
   /**
-   * Create JSON object containing the pertinent rule activation details
+   * Iterates through each active rule and invoke each mapped action.
+   */
+  private def activateRules(user: Identity,
+                            args: JsObject,
+                            rulesToActivate: Map[FullyQualifiedEntityName, 
ReducedRule])(
+    implicit transid: TransactionId): Iterable[Future[JsObject]] = {
+    rulesToActivate.map {
+      case (ruleName, rule) =>
+        // Invoke the action. Retain action results for inclusion in the 
trigger activation record
+        val actionActivationResult: Future[JsObject] = postActivation(user, 
rule, args)
+          .flatMap { response =>
+            response.status match {
+              case OK | Accepted =>
+                Unmarshal(response.entity).to[JsObject].map { 
activationResponse =>
+                  val activationId: JsValue = 
activationResponse.fields("activationId")
+                  logging.debug(this, s"trigger-fired action '${rule.action}' 
invoked with activation $activationId")
+                  ruleResult(ActivationResponse.Success, ruleName, 
rule.action, Some(activationId))
+                }
+
+              // all proper controller responses are JSON objects that 
deserialize to an ErrorResponse instance
+              case code if (response.entity.contentType == 
ContentTypes.`application/json`) =>
+                Unmarshal(response.entity).to[ErrorResponse].map { e =>
+                  val statusCode =
+                    if (code != InternalServerError) {
+                      logging
+                        .debug(
+                          this,
+                          s"trigger-fired action '${rule.action}' failed to 
invoke with ${e.error}, ${e.code}")
+                      ActivationResponse.ApplicationError
+                    } else {
+                      logging
+                        .error(
+                          this,
+                          s"trigger-fired action '${rule.action}' failed to 
invoke with ${e.error}, ${e.code}")
+                      ActivationResponse.WhiskError
+                    }
+                  ruleResult(statusCode, ruleName, rule.action, errorMsg = 
Some(e.error))
+                }
+
+              case code =>
+                logging.error(this, s"trigger-fired action '${rule.action}' 
failed to invoke with status code $code")
+                Unmarshal(response.entity).to[String].map { error =>
+                  ruleResult(ActivationResponse.WhiskError, ruleName, 
rule.action, errorMsg = Some(error))
+                }
+            }
+          }
+          .recover {
+            case t =>
+              logging.error(this, s"trigger-fired action '${rule.action}' 
failed to invoke with $t")
+              ruleResult(
+                ActivationResponse.WhiskError,
+                ruleName,
+                rule.action,
+                errorMsg = Some(InternalServerError.defaultMessage))
+          }
+
+        actionActivationResult
+    }
+  }
+
+  /**
+   * Posts an action activation. Currently done by posting internally to the 
controller.
+   * TODO: use a poper path that does not route through HTTP.
    *
-   * @param statusCode
-   * @param ruleName
-   * @param actionName
-   * @param actionActivationId
-   * @param msg
-   * @return
+   * @param rule the name of the rule that is activated
+   * @param args the arguments to post to the action
+   * @return a future with the HTTP response from the action activation
+   */
+  private def postActivation(user: Identity, rule: ReducedRule, args: 
JsObject): Future[HttpResponse] = {
+    // Build the url to invoke an action mapped to the rule
+    val actionUrl = baseControllerPath / rule.action.path.root.asString / 
"actions"
+
+    val actionPath = {
+      rule.action.path.relativePath.map { pkg =>
+        (Path.SingleSlash + pkg.namespace) / rule.action.name.asString
+      } getOrElse {
+        Path.SingleSlash + rule.action.name.asString
+      }
+    }.toString
+
+    val request = HttpRequest(
+      method = POST,
+      uri = url.withPath(actionUrl + actionPath),
+      headers = 
List(Authorization(BasicHttpCredentials(user.authkey.uuid.asString, 
user.authkey.key.asString))),
+      entity = HttpEntity(MediaTypes.`application/json`, args.compactPrint))
+
+    Http().singleRequest(request)
+  }
+
+  /**
+   * Create JSON object containing the pertinent rule activation details.
+   * {
+   *   "rule": "my-rule",
+   *   "action": "my-action",
+   *   "statusCode": 0,
+   *   "status": "success",
+   *   "activationId": "...",                              // either this 
field, ...
+   *   "error": "The requested resource does not exist."   // ... or this 
field will be present
+   * }
+   *
+   * @param statusCode one of ActivationResponse values
+   * @param ruleName the name of the rule that was activated
+   * @param actionName the name of the action activated by the rule
+   * @param actionActivationId the activation id, if there is one
+   * @param errorMsg the rror messages otherwise
+   * @return JsObject as formatted above
    */
   private def ruleResult(statusCode: Int,
-                         ruleName: String,
-                         actionName: String,
-                         actionActivationId: Option[String] = None,
+                         ruleName: FullyQualifiedEntityName,
+                         actionName: FullyQualifiedEntityName,
+                         actionActivationId: Option[JsValue] = None,
                          errorMsg: Option[String] = None): JsObject = {
-    val objMap: Map[String, JsValue] = Map(
-      "rule" -> JsString(ruleName),
-      "action" -> JsString(actionName),
+    JsObject(
+      "rule" -> JsString(ruleName.asString),
+      "action" -> JsString(actionName.asString),
       "statusCode" -> JsNumber(statusCode),
-      "success" -> JsBoolean(statusCode == ActivationResponse.Success)) ++ {
-      actionActivationId map { id =>
-        Seq("activationId" -> JsString(id))
-      } getOrElse Seq()
-    } ++ {
-      errorMsg map { err =>
-        Seq("error" -> JsString(err))
-      } getOrElse Seq()
-    }
-
-    // Final rule result looks like
-    // {
-    //   "rule": "my-rule",
-    //   "action": "my-action",
-    //   "statusCode": 0,
-    //   "status": "success",
-    //   "activationId": "90c84e7b33c84ceb884e7b33c8ecebf6",  // Optional
-    //   "error": "The requested resource does not exist."  // Optional
-    // }
-    JsObject(objMap)
+      "success" -> JsBoolean(statusCode == ActivationResponse.Success),
+      "activationId" -> actionActivationId.toJson,
 
 Review comment:
   note: the unit tests (or integration tests) are not tight enough and don't 
enforce a schema on the response; the activationId and error properties here 
will emit as `null` if not present which changes the behavior relative to the 
previous commit.
   
   I suggest a unit test which checks the expected schema exactly; this can be 
done for the intergration test as well; parse the array to json and check for 
equality.

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to