Skip to content

Commit 72bc486

Browse files
rabbahVincent
authored andcommitted
Refactor action invoke functions (#319)
* Refactor invoke functions. This permits invoking an action without access to global pamareters and bypassing parameter passing. Caller will be responsible for passing in the appropriate paramters, as in the case of a feed. * Strip references to Flags.common in utility methods for actions. * Add exit on error utility function. * update git ignore for tests/out. * Use refactored action invoke function but preserve semantics. * Pull out getParameters method to utils and use it in action invoke and trigger feed. * Refactor method printing activation response.
1 parent a4ca79a commit 72bc486

5 files changed

Lines changed: 182 additions & 110 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ incubator-openwhisk-cli.iml
1818
wski18n/i18n_resources.go
1919
bin/
2020
tests/build/
21+
tests/out/

commands/action.go

Lines changed: 66 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,8 @@ var actionInvokeCmd = &cobra.Command{
155155
PreRunE: SetupClientConfig,
156156
RunE: func(cmd *cobra.Command, args []string) error {
157157
var err error
158-
var parameters interface{}
159158
var qualifiedName = new(QualifiedName)
160-
var paramArgs []string
159+
var parameters interface{}
161160

162161
if whiskErr := CheckArgs(
163162
args,
@@ -172,65 +171,79 @@ var actionInvokeCmd = &cobra.Command{
172171
return NewQualifiedNameError(args[0], err)
173172
}
174173

175-
Client.Namespace = qualifiedName.GetNamespace()
176-
paramArgs = Flags.common.param
174+
parameters = getParameters(Flags.common.param, false, false)
175+
blocking := Flags.common.blocking || Flags.action.result
176+
resultOnly := Flags.action.result
177+
header := !resultOnly
177178

178-
if len(paramArgs) > 0 {
179-
if parameters, err = getJSONFromStrings(paramArgs, false); err != nil {
180-
return getJSONFromStringsParamError(paramArgs, false, err)
181-
}
182-
}
183-
if Flags.action.result {
184-
Flags.common.blocking = true
185-
}
186-
187-
res, _, err := Client.Actions.Invoke(
188-
qualifiedName.GetEntityName(),
179+
res, err := invokeAction(
180+
*qualifiedName,
189181
parameters,
190-
Flags.common.blocking,
191-
Flags.action.result)
182+
blocking,
183+
resultOnly)
192184

193-
return handleInvocationResponse(*qualifiedName, parameters, res, err)
185+
return printInvocationResponse(*qualifiedName, blocking, header, res, err)
194186
},
195187
}
196188

197-
func handleInvocationResponse(
189+
func invokeAction(
198190
qualifiedName QualifiedName,
199191
parameters interface{},
192+
blocking bool,
193+
result bool) (map[string]interface{}, error) {
194+
// TODO remove all global modifiers
195+
Client.Namespace = qualifiedName.GetNamespace()
196+
res, _, err := Client.Actions.Invoke(
197+
qualifiedName.GetEntityName(),
198+
parameters,
199+
blocking,
200+
result)
201+
return res, err
202+
}
203+
204+
func printInvocationResponse(
205+
qualifiedName QualifiedName,
206+
blocking bool,
207+
header bool,
200208
result map[string]interface{},
201209
err error) error {
202210
if err == nil {
203-
printInvocationMsg(
204-
qualifiedName.GetNamespace(),
205-
qualifiedName.GetEntityName(),
206-
getValueFromJSONResponse(ACTIVATION_ID, result),
207-
result,
208-
color.Output)
211+
printInvocationMsg(qualifiedName, blocking, header, result, color.Output)
209212
} else {
210-
if !Flags.common.blocking {
211-
return handleInvocationError(err, qualifiedName.GetEntityName(), parameters)
213+
if !blocking {
214+
return handleInvocationError(err, qualifiedName.GetEntityName())
212215
} else {
213-
if isBlockingTimeout(err) {
214-
printBlockingTimeoutMsg(
215-
qualifiedName.GetNamespace(),
216-
qualifiedName.GetEntityName(),
217-
getValueFromJSONResponse(ACTIVATION_ID, result))
218-
} else if isApplicationError(err) {
219-
printInvocationMsg(
220-
qualifiedName.GetNamespace(),
221-
qualifiedName.GetEntityName(),
222-
getValueFromJSONResponse(ACTIVATION_ID, result),
223-
result,
224-
colorable.NewColorableStderr())
225-
} else {
226-
return handleInvocationError(err, qualifiedName.GetEntityName(), parameters)
227-
}
216+
return printFailedBlockingInvocationResponse(qualifiedName, header, result, err)
228217
}
229218
}
230219

231220
return err
232221
}
233222

223+
func printFailedBlockingInvocationResponse(
224+
qualifiedName QualifiedName,
225+
header bool,
226+
result map[string]interface{},
227+
err error) error {
228+
if isBlockingTimeout(err) {
229+
printBlockingTimeoutMsg(
230+
qualifiedName.GetNamespace(),
231+
qualifiedName.GetEntityName(),
232+
getValueFromJSONResponse(ACTIVATION_ID, result))
233+
return err
234+
} else if isApplicationError(err) {
235+
printInvocationMsg(
236+
qualifiedName,
237+
true,
238+
header,
239+
result,
240+
colorable.NewColorableStderr())
241+
return err
242+
} else {
243+
return handleInvocationError(err, qualifiedName.GetEntityName())
244+
}
245+
}
246+
234247
var actionGetCmd = &cobra.Command{
235248
Use: "get ACTION_NAME [FIELD_FILTER | --summary | --url]",
236249
Short: wski18n.T("get action"),
@@ -987,12 +1000,11 @@ func actionGetError(entityName string, fetchCode bool, err error) error {
9871000
return nestedError(errMsg, err)
9881001
}
9891002

990-
func handleInvocationError(err error, entityName string, parameters interface{}) error {
1003+
func handleInvocationError(err error, entityName string) error {
9911004
whisk.Debug(
9921005
whisk.DbgError,
993-
"Client.Actions.Invoke(%s, %s, %t) error: %s\n",
994-
entityName, parameters,
995-
Flags.common.blocking,
1006+
"Client.Actions.Invoke(%s, %t) error: %s\n",
1007+
entityName,
9961008
err)
9971009

9981010
errMsg := wski18n.T(
@@ -1113,25 +1125,25 @@ func printBlockingTimeoutMsg(namespace string, entityName string, activationID i
11131125
}
11141126

11151127
func printInvocationMsg(
1116-
namespace string,
1117-
entityName string,
1118-
activationID interface{},
1128+
qualifiedName QualifiedName,
1129+
blocking bool,
1130+
header bool,
11191131
response map[string]interface{},
11201132
outputStream io.Writer) {
1121-
if !Flags.action.result {
1133+
if header {
11221134
fmt.Fprintf(
11231135
outputStream,
11241136
wski18n.T(
11251137
"{{.ok}} invoked /{{.namespace}}/{{.name}} with id {{.id}}\n",
11261138
map[string]interface{}{
11271139
"ok": color.GreenString("ok:"),
1128-
"namespace": boldString(namespace),
1129-
"name": boldString(entityName),
1130-
"id": boldString(activationID),
1140+
"namespace": boldString(qualifiedName.GetNamespace()),
1141+
"name": boldString(qualifiedName.GetEntityName()),
1142+
"id": boldString(getValueFromJSONResponse(ACTIVATION_ID, response)),
11311143
}))
11321144
}
11331145

1134-
if Flags.common.blocking {
1146+
if blocking {
11351147
printJSON(response, outputStream)
11361148
}
11371149
}

commands/trigger.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ var triggerCreateCmd = &cobra.Command{
193193

194194
// Invoke the specified feed action to configure the trigger feed
195195
if feedArgPassed {
196-
err := configureFeed(trigger.Name, fullFeedName)
196+
err := configureFeed(trigger.Name, fullFeedName, getParameters(Flags.common.param, false, false))
197197
if err != nil {
198198
whisk.Debug(whisk.DbgError, "configureFeed(%s, %s) failed: %s\n", trigger.Name, Flags.common.feed,
199199
err)
@@ -286,7 +286,7 @@ var triggerUpdateCmd = &cobra.Command{
286286
Flags.common.param = append(Flags.common.param, getFormattedJSON(FEED_AUTH_KEY, Client.Config.AuthToken))
287287

288288
// Invoke the specified feed action to configure the trigger feed
289-
err = configureFeed(qualifiedName.GetEntityName(), fullFeedName)
289+
err = configureFeed(qualifiedName.GetEntityName(), fullFeedName, getParameters(Flags.common.param, false, false))
290290
if err != nil {
291291
whisk.Debug(whisk.DbgError, "configureFeed(%s, %s) failed: %s\n", qualifiedName.GetEntityName(), Flags.common.feed,
292292
err)
@@ -372,7 +372,7 @@ var triggerGetCmd = &cobra.Command{
372372
Flags.common.param = append(Flags.common.param, getFormattedJSON(FEED_TRIGGER_NAME, fullTriggerName))
373373
Flags.common.param = append(Flags.common.param, getFormattedJSON(FEED_AUTH_KEY, Client.Config.AuthToken))
374374

375-
err = configureFeed(qualifiedName.GetEntityName(), fullFeedName)
375+
err = configureFeed(qualifiedName.GetEntityName(), fullFeedName, getParameters(Flags.common.param, false, false))
376376
if err != nil {
377377
whisk.Debug(whisk.DbgError, "configureFeed(%s, %s) failed: %s\n", qualifiedName.GetEntityName(), fullFeedName, err)
378378
}
@@ -441,7 +441,7 @@ var triggerDeleteCmd = &cobra.Command{
441441
Flags.common.param = append(Flags.common.param, getFormattedJSON(FEED_TRIGGER_NAME, fullTriggerName))
442442
Flags.common.param = append(Flags.common.param, getFormattedJSON(FEED_AUTH_KEY, Client.Config.AuthToken))
443443

444-
err = configureFeed(qualifiedName.GetEntityName(), fullFeedName)
444+
err = configureFeed(qualifiedName.GetEntityName(), fullFeedName, getParameters(Flags.common.param, false, false))
445445
if err != nil {
446446
whisk.Debug(whisk.DbgError, "configureFeed(%s, %s) failed: %s\n", qualifiedName.GetEntityName(), fullFeedName, err)
447447
}
@@ -515,17 +515,24 @@ var triggerListCmd = &cobra.Command{
515515
},
516516
}
517517

518-
func configureFeed(triggerName string, FullFeedName string) error {
519-
feedArgs := []string{FullFeedName}
520-
Flags.common.blocking = true
521-
err := actionInvokeCmd.RunE(nil, feedArgs)
518+
func configureFeed(triggerName string, feedName string, parameters interface{}) error {
519+
var fullFeedName *QualifiedName
520+
var err error
521+
522+
if fullFeedName, err = NewQualifiedName(feedName); err != nil {
523+
return NewQualifiedNameError(feedName, err)
524+
}
525+
526+
res, err := invokeAction(*fullFeedName, parameters, true, false)
527+
err = printInvocationResponse(*fullFeedName, true, false, res, err)
528+
522529
if err != nil {
523-
whisk.Debug(whisk.DbgError, "Invoke of action '%s' failed: %s\n", FullFeedName, err)
530+
whisk.Debug(whisk.DbgError, "Invoke of action '%s' failed: %s\n", feedName, err)
524531
errStr := wski18n.T("Unable to invoke trigger '{{.trigname}}' feed action '{{.feedname}}'; feed is not configured: {{.err}}",
525-
map[string]interface{}{"trigname": triggerName, "feedname": FullFeedName, "err": err})
532+
map[string]interface{}{"trigname": triggerName, "feedname": feedName, "err": err})
526533
err = whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
527534
} else {
528-
whisk.Debug(whisk.DbgInfo, "Successfully configured trigger feed via feed action '%s'\n", FullFeedName)
535+
whisk.Debug(whisk.DbgInfo, "Successfully configured trigger feed via feed action '%s'\n", feedName)
529536
}
530537

531538
return err

commands/util.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727
"github.com/apache/incubator-openwhisk-client-go/whisk"
2828

2929
"github.com/fatih/color"
30+
"github.com/mattn/go-colorable"
31+
3032
//prettyjson "github.com/hokaccha/go-prettyjson" // See prettyjson comment below
3133
"archive/tar"
3234
"archive/zip"
@@ -51,6 +53,45 @@ func csvToQualifiedActions(artifacts string) []string {
5153
return res
5254
}
5355

56+
/**
57+
* Processes command line to retrieve pairs of key-value pairs, where the value must be valid JSON.
58+
*
59+
* Parameters and annotations are handled the same way. The flag here is only for generating an error messages
60+
* specific to one or the other.
61+
*
62+
* NOTE: this function will exit in case of a processing error since it indicates a problem parsing parameters.
63+
*
64+
* @return either an array or a JSON object (map) formatted representation of the key-value pairs.
65+
*/
66+
func getParameters(params []string, keyValueFormat bool, annotation bool) interface{} {
67+
var parameters interface{}
68+
var err error
69+
70+
if !annotation {
71+
whisk.Debug(whisk.DbgInfo, "Parsing parameters: %#v\n", params)
72+
} else {
73+
whisk.Debug(whisk.DbgInfo, "Parsing annotations: %#v\n", params)
74+
}
75+
76+
parameters, err = getJSONFromStrings(params, keyValueFormat)
77+
if err != nil {
78+
whisk.Debug(whisk.DbgError, "getJSONFromStrings(%#v, %s) failed: %s\n", params, keyValueFormat, err)
79+
var errStr string
80+
81+
if !annotation {
82+
errStr = wski18n.T("Invalid parameter argument '{{.param}}': {{.err}}",
83+
map[string]interface{}{"param": fmt.Sprintf("%#v", params), "err": err})
84+
} else {
85+
errStr = wski18n.T("Invalid annotation argument '{{.annotation}}': {{.err}}",
86+
map[string]interface{}{"annotation": fmt.Sprintf("%#v", params), "err": err})
87+
}
88+
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
89+
ExitOnError(werr)
90+
}
91+
92+
return parameters
93+
}
94+
5495
func getJSONFromStrings(content []string, keyValueFormat bool) (interface{}, error) {
5596
var data map[string]interface{}
5697
var res interface{}
@@ -68,6 +109,10 @@ func getJSONFromStrings(content []string, keyValueFormat bool) (interface{}, err
68109
whisk.Debug(whisk.DbgInfo, "Created map '%v' from '%v'\n", data, content[i])
69110
}
70111

112+
if data == nil {
113+
data = make(map[string]interface{})
114+
}
115+
71116
if keyValueFormat {
72117
res = getKeyValueFormattedJSON(data)
73118
} else {
@@ -1133,3 +1178,54 @@ func contains(arr []string, element string) bool {
11331178
}
11341179
return false
11351180
}
1181+
1182+
func ExitOnError(err error) {
1183+
if err == nil {
1184+
return
1185+
}
1186+
1187+
whisk.Debug(whisk.DbgInfo, "err object type: %s\n", reflect.TypeOf(err).String())
1188+
1189+
T := wski18n.T
1190+
var exitCode int = 0
1191+
var displayUsage bool = false
1192+
var displayMsg bool = false
1193+
var msgDisplayed bool = true
1194+
var displayPrefix bool = true
1195+
1196+
werr, isWskError := err.(*whisk.WskError) // Is the err a WskError?
1197+
if isWskError {
1198+
whisk.Debug(whisk.DbgError, "Got a *whisk.WskError error: %#v\n", werr)
1199+
displayUsage = werr.DisplayUsage
1200+
displayMsg = werr.DisplayMsg
1201+
msgDisplayed = werr.MsgDisplayed
1202+
displayPrefix = werr.DisplayPrefix
1203+
exitCode = werr.ExitCode
1204+
} else {
1205+
whisk.Debug(whisk.DbgError, "Got some other error: %s\n", err)
1206+
fmt.Fprintf(os.Stderr, "%s\n", err)
1207+
1208+
displayUsage = false // Cobra already displayed the usage message
1209+
exitCode = 1
1210+
}
1211+
1212+
outputStream := colorable.NewColorableStderr()
1213+
1214+
// If the err msg should be displayed to the console and it has not already been
1215+
// displayed, display it now.
1216+
if displayMsg && !msgDisplayed && displayPrefix && exitCode != 0 {
1217+
fmt.Fprintf(outputStream, "%s%s\n", color.RedString(T("error: ")), err)
1218+
} else if displayMsg && !msgDisplayed && !displayPrefix && exitCode != 0 {
1219+
fmt.Fprintf(outputStream, "%s\n", err)
1220+
} else if displayMsg && !msgDisplayed && exitCode == 0 {
1221+
fmt.Fprintf(outputStream, "%s\n", err)
1222+
}
1223+
1224+
// Displays usage
1225+
if displayUsage {
1226+
fmt.Fprintf(outputStream, T("Run '{{.Name}} --help' for usage.\n",
1227+
map[string]interface{}{"Name": WskCmd.CommandPath()}))
1228+
}
1229+
1230+
os.Exit(exitCode)
1231+
}

0 commit comments

Comments
 (0)