Skip to content
This repository was archived by the owner on Dec 17, 2024. It is now read-only.

Commit 15ed539

Browse files
committed
improve error handling for non-conformant compositions, e.g. no FSM annotation
Fixes #925
1 parent ac0cbb2 commit 15ed539

File tree

7 files changed

+78
-34
lines changed

7 files changed

+78
-34
lines changed

app/content/css/ui.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,11 @@ sidecar.no-limits-data .activation-estimated-cost-container {
952952
}
953953

954954
/* generic */
955+
.normal-text {
956+
font-weight: 400;
957+
opacity: 1;
958+
font-size: 1em;
959+
}
955960
.deemphasize {
956961
font-weight: 300;
957962
font-size: 0.75em;

app/content/js/ui.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ const ui = (function() {
707707
container.innerText = beautify(raw, { wrap_line_length: 80 })
708708
setTimeout(() => hljs.highlightBlock(container), 0)
709709
}
710+
ui.prettyJSON = prettyJSON
710711

711712
/**
712713
* Beautify any kinds we know how to

app/content/js/usage-error.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ const format = (message, options={}) => {
344344
alias, numeric, aliases=[alias], hidden=false, advanced=false,
345345
available,
346346
example=numeric&&'N', dir:isDir=available||false,
347-
title, header, docs=title||header, partial=false, allowed, defaultValue } = rowData
347+
title, header, docs=header||title, partial=false, allowed, defaultValue } = rowData
348348

349349
// row is either hidden or only shown for advanced users
350350
if (hidden) return

app/plugins/modules/composer/lib/composer.js

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -343,11 +343,10 @@ exports.isValidFSM = maybe => {
343343
*
344344
*/
345345
exports.getFSM = entity => {
346-
const fsmPair = entity.parameters && entity.parameters.find( ({key}) => key === '$invoke') // parameter binding?
347-
|| entity.annotations && entity.annotations.find( ({key}) => key === 'fsm') // or annotation?
346+
const fsmAnnotation = exports.fsmAnnotation(entity)
348347

349-
if (fsmPair) {
350-
return openwhiskComposer.deserialize(fsmPair.value)
348+
if (fsmAnnotation) {
349+
return openwhiskComposer.deserialize(fsmAnnotation.value)
351350
}
352351
}
353352

@@ -398,29 +397,44 @@ exports.moveAside = (wsk, name) => repl.qexec(`mv "${name}" "${name}-orig"`)
398397
}).then(() => ({ ok: name }))*/
399398

400399
/**
401-
* Merge previous and current and internal annotations
400+
* Merge previous and current and internal annotations. Take all of
401+
* the new (A2) annotations and add in any old (A1) annotations that
402+
* are not replaced by new ones.
402403
*
403404
*/
404405
const mergeAnnotations = (A1=[], A2=[], type, fsm) => {
405-
const annotations = A1.concat(A2),
406-
fsmAnnotation = annotations.find(({key}) => key === 'fsm'),
407-
badgesAnnotation = annotations.find(({key}) => key === 'wskng.combinators'),
408-
badge = {"type":"composition","role":"replacement","badge":type}
406+
// map from annotation key to "is new"
407+
const inA2 = A2.reduce((M, {key}) => {
408+
M[key] = true
409+
return M
410+
}, {})
409411

410-
if (!fsmAnnotation) {
411-
annotations.push({ key: 'fsm', value: fsm })
412-
} else {
413-
fsmAnnotation.value = fsm
414-
}
412+
// any old annotations are are not replaced by new ones
413+
const filteredA1 = A1.filter(({key}) => !inA2[key])
415414

416-
if (!badgesAnnotation) {
417-
annotations.push({ key: 'wskng.combinators', value: [badge] })
418-
} else {
419-
const existing = badgesAnnotation.value.find(({type}) => type === 'composition')
420-
if (existing) {
421-
existing.badge = type
415+
// the union of old and new
416+
const annotations = A2.concat(filteredA1)
417+
418+
if (type && fsm) {
419+
const fsmAnnotation = annotations.find(({key}) => key === 'fsm') || annotations.find(({key}) => key === 'conductor'),
420+
badgesAnnotation = annotations.find(({key}) => key === 'wskng.combinators'),
421+
badge = {"type":"composition","role":"replacement","badge":type}
422+
423+
if (!fsmAnnotation) {
424+
annotations.push({ key: 'fsm', value: fsm })
422425
} else {
423-
badgesAnnotation.push(badge)
426+
fsmAnnotation.value = fsm
427+
}
428+
429+
if (!badgesAnnotation) {
430+
annotations.push({ key: 'wskng.combinators', value: [badge] })
431+
} else {
432+
const existing = badgesAnnotation.value.find(({type}) => type === 'composition')
433+
if (existing) {
434+
existing.badge = type
435+
} else {
436+
badgesAnnotation.push(badge)
437+
}
424438
}
425439
}
426440

@@ -452,7 +466,9 @@ exports.create = ({name, fsm, type, annotations=[], parameters=[], wsk, commandT
452466
// now we merge together the parameters and annotations
453467
const fsmAction = fsm.encode(fqnAppName).actions[0].action
454468
fsmAction.parameters = currentAction.parameters.concat(parameters).concat(fsmAction.parameters || []),
455-
fsmAction.annotations = mergeAnnotations(currentAction.annotations, annotations.concat(fsmAction.annotations||[]), type, fsm)
469+
fsmAction.annotations = mergeAnnotations(currentAction.annotations,
470+
mergeAnnotations(annotations||[], fsmAction.annotations||[]),
471+
type, fsm)
456472
return fsmAction
457473
})
458474
.then(fsmAction => wsk.owOpts({ // add common flags to the requewst
@@ -482,12 +498,28 @@ exports.update = ({name, entity, fsm, type, wsk, commandTree, execOptions}) => {
482498
*
483499
*/
484500
exports.isAnApp = action => {
485-
const allManagement = action.annotations && action.annotations.find(({key}) => key === 'wskng.combinators'),
486-
anyAppManagement = allManagement && util.isArray(allManagement.value) && allManagement.value.find(({type}) => type === 'composition')
501+
const anno = action && action.annotations && action.annotations.find(({key}) => key === 'conductor')
502+
return anno && anno.value
503+
}
504+
505+
/**
506+
* Return the annotation that stores the IR/fsm
507+
*
508+
*/
509+
exports.fsmAnnotation = action => {
510+
const anno = action.annotations.find(({key}) => key === 'fsm')
511+
|| action.annotations.find(({key}) => key === 'conductor')
487512

488-
return anyAppManagement
513+
// avoid conductor:true as indicating the presence of an FSM
514+
return anno && anno.value !== true ? anno : undefined
489515
}
490516

517+
/**
518+
* Does the given action have an IR/fsm associated with it?
519+
*
520+
*/
521+
exports.hasFSM = action => !!exports.fsmAnnotation(action)
522+
491523
/**
492524
* Helper method for kill and purge operations, which share enough code...
493525
*
@@ -693,7 +725,7 @@ exports.hasUnknownOptions = (options, expected) => {
693725
*/
694726
exports.decorateAsApp = ({action, viewName='app', commandPrefix='app get', doVisualize, options}) => {
695727
action.prettyType = appBadge
696-
action.fsm = action.annotations.find(({key}) => key === 'fsm').value
728+
action.fsm = exports.fsmAnnotation(action).value
697729

698730
if (action.exec) {
699731
action.exec.prettyKind = 'app'
@@ -714,7 +746,7 @@ exports.decorateAsApp = ({action, viewName='app', commandPrefix='app get', doVis
714746
.concat(exports.vizAndfsmViewModes(visualize, commandPrefix, undefined, options))
715747
.concat(exports.zoomToFitButtons(controller))
716748

717-
return view
749+
return view || action
718750

719751
} else {
720752
return action

app/plugins/modules/composer/lib/get.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const debug = require('debug')('app get')
1818
debug('loading')
1919

2020
const { app_get:usage } = require('./usage'),
21-
{ isAnApp, decorateAsApp } = require('./composer')
21+
{ hasFSM, decorateAsApp } = require('./composer')
2222

2323
const viewName = 'app'
2424

@@ -61,7 +61,7 @@ module.exports = (commandTree, prequire) => {
6161
const action = response.message || response,
6262
execOptions = arguments[5]
6363

64-
if (action && action.annotations && action.annotations.find(({key}) => key === 'fsm')) {
64+
if (hasFSM(action)) {
6565
const doVisualize = execOptions.override || !execOptions.nested,
6666
options = execOptions.originalOptions || {},
6767
content = decorateAsApp({ action, doVisualize, options }),

app/plugins/modules/composer/lib/list.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ module.exports = (commandTree, prequire) => {
6464

6565
return rawListImpl.apply(undefined, arguments)
6666
.then(response => response.map(action => {
67-
if (action && action.annotations && action.annotations.find(({key}) => key === 'fsm')) {
67+
if (isAnApp(action)) {
6868
decorateAsApp({action})
6969
action.prettyKind = 'composition'
7070
}

app/plugins/modules/wskflow/lib/flowCommand.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const PromisePool = require('es6-promise-pool'),
2121
usage = require('../usage'),
2222
path = require('path'),
2323
visualize = require('./visualize'),
24-
{ zoomToFitButtons } = require(path.join(__dirname, '../../composer/lib/composer'))
24+
{ zoomToFitButtons, fsmAnnotation } = require(path.join(__dirname, '../../composer/lib/composer'))
2525

2626
const viewName = 'session flow'
2727

@@ -94,13 +94,19 @@ module.exports = (commandTree, prequire) => {
9494
let fsm;
9595
if (action.wskflowErr) {
9696
// 1) if an app was deleted, the last promise item returns an error
97-
const error = new Error(`${viewName} unavailable, as the composition was deleted`);
97+
const error = new Error(`Sorry, this view is not available, as the composition was deleted`);
9898
error.code = 404
9999
throw error
100100

101101
} else {
102102
// extract the FSM
103-
fsm = action.annotations.find(({key}) => key === 'fsm').value
103+
const fsmAnno = fsmAnnotation(action)
104+
if (!fsmAnno) {
105+
const error = new Error(`Sorry, this view is not available, as the composition was improperly created`);
106+
error.code = 404
107+
throw error
108+
}
109+
fsm = fsmAnno && fsmAnno.value
104110
}
105111

106112
const {view, controller} = visualize(fsm, undefined, undefined, undefined, activations)

0 commit comments

Comments
 (0)