This is an automated email from the ASF dual-hosted git repository.

mrutkowski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-client-go.git


The following commit(s) were added to refs/heads/master by this push:
     new d272b2b  Parse an action's application error (#133)
d272b2b is described below

commit d272b2b1129269d384c1dbba6c39156abc10ad3c
Author: Mark Deuser <[email protected]>
AuthorDate: Wed Dec 11 11:53:14 2019 -0500

    Parse an action's application error (#133)
    
    * Generically parse the application error
    
    * Update vendor.json to include all dependent packages
    
    * gofmt
    
    * don't display the application error as the action's error
    
    * put back original vendor.json
---
 whisk/client.go      | 99 +++++++++++++++++++++++++++++++++++++++++++++++-----
 whisk/client_test.go | 19 ++++++++++
 2 files changed, 110 insertions(+), 8 deletions(-)

diff --git a/whisk/client.go b/whisk/client.go
index 91b8a79..9ea01bc 100644
--- a/whisk/client.go
+++ b/whisk/client.go
@@ -586,9 +586,13 @@ func parseApplicationError(resp *http.Response, data 
[]byte, v interface{}) (*ht
        // Handle application errors that occur when --result option is false 
(#5)
        if err == nil && whiskErrorResponse != nil && 
whiskErrorResponse.Response != nil && whiskErrorResponse.Response.Status != nil 
{
                Debug(DbgInfo, "Detected response status `%s` that a 
whisk.error(\"%#v\") was returned\n",
-                       *whiskErrorResponse.Response.Status, 
*whiskErrorResponse.Response.Result)
+                       *whiskErrorResponse.Response.Status, 
whiskErrorResponse.Response.Result)
+
+               errStr := 
getApplicationErrorMessage(whiskErrorResponse.Response.Result)
+               Debug(DbgInfo, "Application error received: %s\n", errStr)
+
                errMsg := wski18n.T("The following application error was 
received: {{.err}}",
-                       map[string]interface{}{"err": 
*whiskErrorResponse.Response.Result})
+                       map[string]interface{}{"err": errStr})
                whiskErr := MakeWskError(errors.New(errMsg), 
resp.StatusCode-256, NO_DISPLAY_MSG, NO_DISPLAY_USAGE,
                        NO_MSG_DISPLAYED, DISPLAY_PREFIX, APPLICATION_ERR)
                return parseSuccessResponse(resp, data, v), whiskErr
@@ -600,10 +604,10 @@ func parseApplicationError(resp *http.Response, data 
[]byte, v interface{}) (*ht
        // Handle application errors that occur with blocking invocations when 
--result option is true (#5)
        if err == nil && appErrResult.Error != nil {
                Debug(DbgInfo, "Error code is null, blocking with result 
invocation error has occurred\n")
-               errMsg := fmt.Sprintf("%v", *appErrResult.Error)
-               Debug(DbgInfo, "Application error received: %s\n", errMsg)
+               errStr := getApplicationErrorMessage(*appErrResult.Error)
+               Debug(DbgInfo, "Application error received: %s\n", errStr)
 
-               whiskErr := MakeWskError(errors.New(errMsg), 
resp.StatusCode-256, NO_DISPLAY_MSG, NO_DISPLAY_USAGE,
+               whiskErr := MakeWskError(errors.New(errStr), 
resp.StatusCode-256, NO_DISPLAY_MSG, NO_DISPLAY_USAGE,
                        NO_MSG_DISPLAYED, DISPLAY_PREFIX, APPLICATION_ERR)
                return parseSuccessResponse(resp, data, v), whiskErr
        }
@@ -616,6 +620,85 @@ func parseApplicationError(resp *http.Response, data 
[]byte, v interface{}) (*ht
        return resp, whiskErr
 }
 
+func getApplicationErrorMessage(errResp interface{}) string {
+       var errStr string
+
+       // Handle error results that looks like:
+       //
+       //   {
+       //     "error": {
+       //       "error": "An error string",
+       //       "message": "An error message",
+       //       "another-message": "Another error message"
+       //     }
+       //   }
+       //   Returns "An error string; An error message; Another error message"
+       //
+       // OR
+       //   {
+       //     "error": "An error string"
+       //   }
+       //   Returns "An error string"
+       //
+       // OR
+       //   {
+       //     "error": {
+       //       "custom-err": {
+       //         "error": "An error string",
+       //         "message": "An error message"
+       //       }
+       //     }
+       //   }
+       //   Returns "{"error": { "custom-err": { "error": "An error string", 
"message": "An error message" } } }"
+
+       errMapIntf, errMapIntfOk := errResp.(map[string]interface{})
+       if !errMapIntfOk {
+               errStr = fmt.Sprintf("%v", errResp)
+       } else {
+               // Check if the "error" field in the response JSON
+               errObjIntf, errObjIntfOk := errMapIntf["error"]
+               if !errObjIntfOk {
+                       errStr = fmt.Sprintf("%v", errMapIntf)
+               } else {
+                       // Check if the "error" field value is a JSON object
+                       errObj, errObjOk := errObjIntf.(map[string]interface{})
+                       if !errObjOk {
+                               // The "error" field value is not JSON; check 
if it's a string
+                               errorStr, errorStrOk := errObjIntf.(string)
+                               if !errorStrOk {
+                                       errStr = fmt.Sprintf("%v", errObjIntf)
+                               } else {
+                                       errStr = errorStr
+                               }
+                       } else {
+                               Debug(DbgInfo, "Application failure error json: 
%+v\n", errObj)
+
+                               // Concatenate all string field values into a 
single error string
+                               msgSeparator := ""
+                               for _, val := range errObj {
+                                       valStr, valStrOk := val.(string)
+                                       if valStrOk {
+                                               errStr = errStr + msgSeparator 
+ valStr
+                                               msgSeparator = "; "
+                                       }
+                               }
+
+                               // If no top level string fields exist, return 
the entire error object
+                               // Return a nice JSON string if possible; 
otherwise let Go try it's best
+                               if len(errStr) == 0 {
+                                       jsonBytes, err := json.Marshal(errObj)
+                                       if err != nil {
+                                               errStr = fmt.Sprintf("%v", 
errObj)
+                                       } else {
+                                               errStr = string(jsonBytes)
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return errStr
+}
 func parseSuccessResponse(resp *http.Response, data []byte, v interface{}) 
*http.Response {
        Debug(DbgInfo, "Parsing HTTP response into struct type: %s\n", 
reflect.TypeOf(v))
 
@@ -660,9 +743,9 @@ type WhiskErrorResponse struct {
 }
 
 type WhiskResponse struct {
-       Result  *WhiskResult `json:"result"`
-       Success bool         `json:"success"`
-       Status  *interface{} `json:"status"`
+       Result  map[string]interface{} `json:"result"`
+       Success bool                   `json:"success"`
+       Status  *interface{}           `json:"status"`
 }
 
 type WhiskResult struct {
diff --git a/whisk/client_test.go b/whisk/client_test.go
index ec27b63..b580c97 100644
--- a/whisk/client_test.go
+++ b/whisk/client_test.go
@@ -168,3 +168,22 @@ func TestAdditionalHeaders(t *testing.T) {
        assert.Equal(t, "Value1", newRequestUrl.Header.Get("Key1"))
        assert.Equal(t, "Value2", newRequestUrl.Header.Get("Key2"))
 }
+
+func TestParseApplicationError(t *testing.T) {
+       appErr1 := map[string]interface{}{
+               "error": map[string]interface{}{
+                       "error":   "An error string",
+                       "message": "An error message",
+               },
+       }
+
+       appErr2 := map[string]interface{}{
+               "error": "Another error string",
+       }
+
+       errStr := getApplicationErrorMessage(appErr1)
+       assert.Equal(t, "An error string; An error message", errStr)
+
+       errStr = getApplicationErrorMessage(appErr2)
+       assert.Equal(t, "Another error string", errStr)
+}

Reply via email to