Some cleanup on the Node-related Go<->javascript API. (#2221)

- Make the first value of enums (the one that is equal to 0) to be the unknown
  one, so that it was considered a programming error.
- Remove "omitempty" tag from fields where change from some value to empty value
  must be passed in an update to the web UI.
- Add "omitempty" to Children because that's an important special use case.
- Modify javascript to assume that name, state, style and message of an action
  is always there.
- Modify javascript to work when 'children' value was not received.
This commit is contained in:
Pavel Ivanov 2016-11-04 23:02:18 -07:00 коммит произвёл GitHub
Родитель ab58e1113e
Коммит cac9ce496f
11 изменённых файлов: 40 добавлений и 31 удалений

Просмотреть файл

@ -68,7 +68,8 @@ func TestLongPolling(t *testing.T) {
if err != nil {
t.Fatalf("/poll/1 reading failed: %v", err)
}
if string(tree) != `{"nodes":[{"name":"name","path":"/uuid1","children":[],"lastChanged":143,"actions":null}]}` {
if !strings.Contains(string(tree), `"name":"name"`) ||
!strings.Contains(string(tree), `"path":"/uuid1"`) {
t.Errorf("unexpected first result: %v", string(tree))
}

Просмотреть файл

@ -27,47 +27,56 @@ import (
type NodeDisplay int
const (
// NodeDisplayUnknown is an unknown value and should never be set.
NodeDisplayUnknown NodeDisplay = 0
// NodeDisplayIndeterminate is a progress bar that doesn't have
// a current value, but just shows movement.
NodeDisplayIndeterminate NodeDisplay = 0
NodeDisplayIndeterminate NodeDisplay = 1
// NodeDisplayDeterminate is a progress bar driven by the
// Progress field.
NodeDisplayDeterminate NodeDisplay = 1
NodeDisplayDeterminate NodeDisplay = 2
// NodeDisplayNone shows no progress bar or status.
NodeDisplayNone NodeDisplay = 2
NodeDisplayNone NodeDisplay = 3
)
// ActionState constants need to match node.ts.ActionState.
type ActionState int
const (
// ActionStateUnknown is an unknown value and should never be set.
ActionStateUnknown ActionState = 0
// ActionStateEnabled is for when the action is enabled.
ActionStateEnabled ActionState = 0
ActionStateEnabled ActionState = 1
// ActionStateDisabled is for when the action is disabled.
ActionStateDisabled ActionState = 1
ActionStateDisabled ActionState = 2
)
// ActionStyle constants need to match node.ts.ActionStyle.
type ActionStyle int
const (
// ActionStyleUnknown is an unknown value and should never be set.
ActionStyleUnknown ActionStyle = 0
// ActionStyleNormal will just trigger the action.
ActionStyleNormal ActionStyle = 0
ActionStyleNormal ActionStyle = 1
// ActionStyleWarning will display a warning dialog to confirm
// action with Action.Message.
ActionStyleWarning ActionStyle = 1
ActionStyleWarning ActionStyle = 2
// ActionStyleWaiting highlights to the user that the process
// is waiting on the execution of the action.
ActionStyleWaiting ActionStyle = 2
ActionStyleWaiting ActionStyle = 3
// ActionStyleTriggered is a state where the button is greyed
// out and cannot be pressed.
ActionStyleTriggered ActionStyle = 3
ActionStyleTriggered ActionStyle = 4
)
// Node is the UI representation of a Workflow toplevel object, or of
@ -102,24 +111,24 @@ type Node struct {
Name string `json:"name"`
Path string `json:"path"`
Children []*Node `json:"children"`
Children []*Node `json:"children,omitempty"`
LastChanged int64 `json:"lastChanged"`
Progress int `json:"progress,omitempty"`
ProgressMessage string `json:"progressMsg,omitempty"`
State workflowpb.WorkflowState `json:"state,omitempty"`
Progress int `json:"progress"`
ProgressMessage string `json:"progressMsg"`
State workflowpb.WorkflowState `json:"state"`
Display NodeDisplay `json:"display,omitempty"`
Message string `json:"message,omitempty"`
Log string `json:"log,omitempty"`
Disabled bool `json:"disabled,omitempty"`
Message string `json:"message"`
Log string `json:"log"`
Disabled bool `json:"disabled"`
Actions []*Action `json:"actions"`
}
// Action must match node.ts Action.
type Action struct {
Name string `json:"name,omitempty"`
Name string `json:"name"`
State ActionState `json:"state,omitempty"`
Style ActionStyle `json:"style,omitempty"`
Message string `json:"message,omitempty"`
Message string `json:"message"`
}
// Update is the data structure we send on the websocket or on the

Просмотреть файл

@ -73,7 +73,7 @@ func TestNodeManagerWithRoot(t *testing.T) {
result, ok = <-notifications
if !ok ||
!strings.Contains(string(result), `"name":"name2"`) ||
!strings.Contains(string(result), `"children":[]`) || // FIXME(alainjobart) this should be true, we're not changing the children.
strings.Contains(string(result), `"children":[]`) ||
strings.Contains(string(result), `"fullUpdate":true`) {
t.Errorf("unexpected notification: %v %v", ok, string(result))
}

Просмотреть файл

@ -61,7 +61,8 @@ func TestWebSocket(t *testing.T) {
if err != nil {
t.Fatalf("WebSocket first read failed: %v", err)
}
if string(tree) != `{"nodes":[{"name":"name","path":"/uuid1","children":[],"lastChanged":143,"actions":null}]}` {
if !strings.Contains(string(tree), `"name":"name"`) ||
!strings.Contains(string(tree), `"path":"/uuid1"`) {
t.Errorf("unexpected first result: %v", string(tree))
}

Просмотреть файл

@ -28,5 +28,5 @@
</head>
<body class="flex-column">
<vt-app-root class="flex-column flex-grow">Loading...</vt-app-root>
<script type="text/javascript" src="inline.js"></script><script type="text/javascript" src="styles.07f8743f5392cfdfbcb5.bundle.js"></script><script type="text/javascript" src="main.b02cdae8978945277038.bundle.js"></script></body>
<script type="text/javascript" src="inline.js"></script><script type="text/javascript" src="styles.07f8743f5392cfdfbcb5.bundle.js"></script><script type="text/javascript" src="main.ab14518241846dfc91ba.bundle.js"></script></body>
</html>

Просмотреть файл

@ -1 +1 @@
!function(e){function __webpack_require__(r){if(t[r])return t[r].exports;var n=t[r]={i:r,l:!1,exports:{}};return e[r].call(n.exports,n,n.exports,__webpack_require__),n.l=!0,n.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,o,c){for(var _,a,i,u=0,p=[];u<t.length;u++)a=t[u],n[a]&&p.push(n[a][0]),n[a]=0;for(_ in o)if(Object.prototype.hasOwnProperty.call(o,_)){var f=o[_];switch(typeof f){case"object":e[_]=function(r){var t=r.slice(1),n=r[0];return function(r,o,c){e[n].apply(this,[r,o,c].concat(t))}}(f);break;case"function":e[_]=f;break;default:e[_]=e[f]}}for(r&&r(t,o,c);p.length;)p.shift()();if(c)for(u=0;u<c.length;u++)i=__webpack_require__(__webpack_require__.s=c[u]);return i};var t={},n={2:0};__webpack_require__.e=function(e){function onScriptComplete(){t.onerror=t.onload=null,clearTimeout(o);var r=n[e];0!==r&&(r&&r[1](new Error("Loading chunk "+e+" failed.")),n[e]=void 0)}if(0===n[e])return Promise.resolve();if(n[e])return n[e][2];var r=document.getElementsByTagName("head")[0],t=document.createElement("script");t.type="text/javascript",t.charset="utf-8",t.async=!0,t.timeout=12e4,t.src=__webpack_require__.p+""+e+"."+{0:"b02cdae8978945277038",1:"07f8743f5392cfdfbcb5"}[e]+".chunk.js";var o=setTimeout(onScriptComplete,12e4);t.onerror=t.onload=onScriptComplete,r.appendChild(t);var c=new Promise(function(r,t){n[e]=[r,t]});return n[e][2]=c},__webpack_require__.m=e,__webpack_require__.c=t,__webpack_require__.i=function(e){return e},__webpack_require__.d=function(e,r,t){Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},__webpack_require__.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return __webpack_require__.d(r,"a",r),r},__webpack_require__.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},__webpack_require__.p="",__webpack_require__.oe=function(e){throw console.error(e),e}}(function(e){for(var r in e)if(Object.prototype.hasOwnProperty.call(e,r))switch(typeof e[r]){case"function":break;case"object":e[r]=function(r){var t=r.slice(1),n=e[r[0]];return function(e,r,o){n.apply(this,[e,r,o].concat(t))}}(e[r]);break;default:e[r]=e[e[r]]}return e}([]));
!function(e){function __webpack_require__(r){if(t[r])return t[r].exports;var n=t[r]={i:r,l:!1,exports:{}};return e[r].call(n.exports,n,n.exports,__webpack_require__),n.l=!0,n.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,o,c){for(var _,a,i,u=0,p=[];u<t.length;u++)a=t[u],n[a]&&p.push(n[a][0]),n[a]=0;for(_ in o)if(Object.prototype.hasOwnProperty.call(o,_)){var f=o[_];switch(typeof f){case"object":e[_]=function(r){var t=r.slice(1),n=r[0];return function(r,o,c){e[n].apply(this,[r,o,c].concat(t))}}(f);break;case"function":e[_]=f;break;default:e[_]=e[f]}}for(r&&r(t,o,c);p.length;)p.shift()();if(c)for(u=0;u<c.length;u++)i=__webpack_require__(__webpack_require__.s=c[u]);return i};var t={},n={2:0};__webpack_require__.e=function(e){function onScriptComplete(){t.onerror=t.onload=null,clearTimeout(o);var r=n[e];0!==r&&(r&&r[1](new Error("Loading chunk "+e+" failed.")),n[e]=void 0)}if(0===n[e])return Promise.resolve();if(n[e])return n[e][2];var r=document.getElementsByTagName("head")[0],t=document.createElement("script");t.type="text/javascript",t.charset="utf-8",t.async=!0,t.timeout=12e4,t.src=__webpack_require__.p+""+e+"."+{0:"ab14518241846dfc91ba",1:"07f8743f5392cfdfbcb5"}[e]+".chunk.js";var o=setTimeout(onScriptComplete,12e4);t.onerror=t.onload=onScriptComplete,r.appendChild(t);var c=new Promise(function(r,t){n[e]=[r,t]});return n[e][2]=c},__webpack_require__.m=e,__webpack_require__.c=t,__webpack_require__.i=function(e){return e},__webpack_require__.d=function(e,r,t){Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},__webpack_require__.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return __webpack_require__.d(r,"a",r),r},__webpack_require__.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},__webpack_require__.p="",__webpack_require__.oe=function(e){throw console.error(e),e}}(function(e){for(var r in e)if(Object.prototype.hasOwnProperty.call(e,r))switch(typeof e[r]){case"function":break;case"object":e[r]=function(r){var t=r.slice(1),n=e[r[0]];return function(e,r,o){n.apply(this,[e,r,o].concat(t))}}(e[r]);break;default:e[r]=e[e[r]]}return e}([]));

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Двоичные данные
web/vtctld2/app/main.ab14518241846dfc91ba.bundle.js.gz Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -1,9 +1,11 @@
export const enum ActionState {
UNKNOWN,
ENABLED,
DISABLED,
}
export const enum ActionStyle {
UNKNOWN,
NORMAL,
WARNING, // Display warning dialog to confirm action with message
WAITING, // Highlight to user that the process is waiting on action.
@ -59,6 +61,7 @@ export const enum State {
}
export const enum Display { // Only relevant if State is RUNNING.
UNKNOWN,
INDETERMINATE, // Only relevant if State is RUNNING.
DETERMINATE,
NONE // Even if Display is NONE progressMsg will still be shown.
@ -95,12 +98,7 @@ export class Node {
this.actions = [];
if (changes.actions !== null) {
for (let actionData of changes.actions) {
let message = actionData.message ? actionData.message : '';
let state = actionData.state ? actionData.state : ActionState.ENABLED;
let style = actionData.style ? actionData.style : ActionStyle.NORMAL;
if ('name' in actionData) {
this.actions.push(new Action(actionData.name, state, style, message));
}
this.actions.push(new Action(actionData.name, actionData.state, actionData.style, actionData.message));
}
}
}

Просмотреть файл

@ -256,7 +256,7 @@ dolore magnam aliquam quaerat voluptatem.'});
// Need to update the target now.
target.update(workflowData);
if (workflowData.children !== null) {
if ('children' in workflowData && workflowData.children !== null) {
target.children = [];
for (let childData of workflowData.children) {
let child = this.recursiveWorkflowBuilder(childData);