diff --git a/frontend/package.json b/frontend/package.json
index 86ac08b..4abf4ac 100755
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -3,6 +3,7 @@
"@fortawesome/fontawesome-free": "^5.12.0",
"bootstrap": "^4.4.1",
"jquery": "^3.4.1",
+ "js-uuid": "0.0.6",
"linkstate": "^1.1.1",
"popper.js": "^1.16.1",
"preact": "^8.2.9",
diff --git a/frontend/rollup.config.js b/frontend/rollup.config.js
index b1c184b..b250a14 100755
--- a/frontend/rollup.config.js
+++ b/frontend/rollup.config.js
@@ -41,6 +41,7 @@ export default {
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf': 'dist/webfonts/fa-brands-400.ttf',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff': 'dist/webfonts/fa-brands-400.woff',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2': 'dist/webfonts/fa-brands-400.woff2',
+ 'node_modules/js-uuid/js-uuid.js': 'dist/js/js-uuid.js',
verbose: true
}),
buble({jsx: 'h'}),
diff --git a/frontend/src/css/index.css b/frontend/src/css/index.css
index e69de29..f0cac02 100755
--- a/frontend/src/css/index.css
+++ b/frontend/src/css/index.css
@@ -0,0 +1,7 @@
+pre.word-wrap {
+ white-space: pre-wrap; /* Since CSS 2.1 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
diff --git a/frontend/src/html/index.html b/frontend/src/html/index.html
index 8f02e32..79f83d9 100755
--- a/frontend/src/html/index.html
+++ b/frontend/src/html/index.html
@@ -3,9 +3,11 @@
+
+
diff --git a/frontend/src/js/component/wb-common-fields.js b/frontend/src/js/component/wb-common-fields.js
index 2fd1223..54a6486 100644
--- a/frontend/src/js/component/wb-common-fields.js
+++ b/frontend/src/js/component/wb-common-fields.js
@@ -69,7 +69,11 @@ class WBCommonFields extends Component {
) ],
[ 'Modified by Client', item.modified_by_client_uuid ],
- [ 'API Url', 'https://' + app.state.arvHost + '/arvados/v1' + item.href ],
+ [ 'API Url', (
+
+ { 'https://' + app.state.arvHost + '/arvados/v1' + item.href }
+
+ ) ],
[ 'ETag', item.etag ]
];
this.setState({ 'rows': rows });
@@ -78,13 +82,12 @@ class WBCommonFields extends Component {
render({}, { rows }) {
return (
-
-
Common Fields
- { rows ? (
-
- ) :
Loading...
}
-
+ rows ? (
+
+ ) : (
+ Loading...
+ )
);
}
}
diff --git a/frontend/src/js/component/wb-container-request-fields.js b/frontend/src/js/component/wb-container-request-fields.js
new file mode 100644
index 0000000..d2b500b
--- /dev/null
+++ b/frontend/src/js/component/wb-container-request-fields.js
@@ -0,0 +1,124 @@
+import { h, Component } from 'preact';
+import WBTable from 'wb-table';
+import makeArvadosRequest from 'make-arvados-request';
+import arvadosTypeName from 'arvados-type-name';
+import arvadosObjectName from 'arvados-object-name';
+import urlForObject from 'url-for-object';
+import wbFormatDate from 'wb-format-date';
+import WBNameAndUuid from 'wb-name-and-uuid';
+import WBAccordion from 'wb-accordion';
+
+class WBContainerRequestFields extends Component {
+ componentDidMount() {
+ this.prepareRows();
+ }
+
+ componentWillReceiveProps(nextProps) {
+ this.props = nextProps;
+ this.prepareRows();
+ }
+
+ prepareRows() {
+ let { uuid, app } = this.props;
+ let { arvHost, arvToken } = app.state;
+
+ let item;
+ let prom = makeArvadosRequest(arvHost, arvToken,
+ '/arvados/v1/container_requests/' + uuid);
+ prom = prom.then(xhr => (item = xhr.response));
+
+ prom = prom.then(() => {
+ let rows = [
+ [ 'Name', item.name ],
+ [ 'Description', item.description || ({ String(item.description) }) ],
+ [ 'Properties', (
+
+ { JSON.stringify(item.properties, null, 2) }
+
+ ) ],
+ [ 'State', item.state ],
+ [ 'Requesting Container', (
+
+ ) ],
+ [ 'Container', (
+
+ ) ],
+ [ 'Container Count Max', item.container_count_max ],
+ [ 'Mounts', (
+
+ { Object.keys(item.mounts).map(k => (
+ { JSON.stringify(item.mounts[k], null, 2) }
+ )) }
+
+ ) ],
+ [ 'Runtime Constraints', (
+
+ { JSON.stringify(item.runtime_constraints, null, 2) }
+
+ ) ],
+ [ 'Scheduling Parameters', (
+
+ { JSON.stringify(item.scheduling_parameters, null, 2) }
+
+ ) ],
+ [ 'Container Image', (
+
+ ) ],
+ [ 'Environment', (
+
+ { JSON.stringify(item.environment, null, 2) }
+
+ ) ],
+ [ 'Working Directory', item.cwd ],
+ [ 'Command', (
+ { JSON.stringify(item.command) }
+ ) ],
+ [ 'Output Path', item.output_path ],
+ [ 'Output Name', item.output_name ],
+ [ 'Output TTL', item.output_ttl ],
+ [ 'Priority', item.priority ],
+ [ 'Expires At', wbFormatDate(item.expires_at) ],
+ [ 'Use Existing', String(item.use_existing) ],
+ [ 'Log', (
+
+ ) ],
+ [ 'Output', (
+
+ ) ],
+ [ 'Filters', (
+ item.filters ? ({ item.filters }
) : ({ String(item.filters) })
+ ) ],
+ [ 'Runtime Token', item.runtime_token || ({ String(item.runtime_token) }) ],
+ [ 'Runtime User', (
+
+ ) ],
+ [ 'Runtime Auth Scopes', (
+ item.runtime_auth_scopes ? (
+ { JSON.stringify(item.runtime_auth_scopes, null, 2) }
+ ) : (
+ { String(item.runtime_auth_scopes) }
+ )
+ ) ]
+ ];
+ this.setState({ 'rows': rows });
+ });
+ }
+
+ render({}, { rows }) {
+ return (
+ rows ? (
+
+ ) : (
+ Loading...
+ )
+ );
+ }
+}
+
+export default WBContainerRequestFields;
diff --git a/frontend/src/js/component/wb-name-and-uuid.js b/frontend/src/js/component/wb-name-and-uuid.js
new file mode 100644
index 0000000..d3d414d
--- /dev/null
+++ b/frontend/src/js/component/wb-name-and-uuid.js
@@ -0,0 +1,78 @@
+import { h, Component } from 'preact';
+import makeArvadosRequest from 'make-arvados-request';
+import urlForObject from 'url-for-object';
+import arvadosObjectName from 'arvados-object-name';
+import arvadosTypeName from 'arvados-type-name';
+
+class WBNameAndUuid extends Component {
+ componentDidMount() {
+ let { uuid, app } = this.props;
+ let { arvHost, arvToken } = app.state;
+
+ if (!uuid)
+ return;
+
+ let prom = new Promise(accept => accept());
+
+ if (/[0-9a-f]{32}\+[0-9]+/g.exec(uuid)) {
+ let filters = [
+ ['portable_data_hash', '=', uuid]
+ ];
+ prom = prom.then(() => makeArvadosRequest(arvHost, arvToken,
+ '/arvados/v1/collections?filters=' +
+ encodeURIComponent(JSON.stringify(filters))));
+ prom = prom.then(xhr => {
+ if (xhr.response.items.length === 0) {
+ this.setState({
+ 'item': {
+ 'uuid': uuid,
+ 'name': 'Collection with portable data hash ' + uuid
+ }
+ });
+ return;
+ }
+ let item = xhr.response.items[0];
+ if (xhr.response.items.length > 1)
+ item.name += ' +' + (xhr.response.items.length - 1) + ' others';
+ this.setState({ item });
+ });
+
+ } else if (/[a-z0-9]{5}-[a-z0-9]{5}-[a-z0-9]{15}/.exec(uuid)) {
+ let typeName = arvadosTypeName(uuid);
+ let prom = makeArvadosRequest(arvHost, arvToken,
+ '/arvados/v1/' + typeName + 's/' + uuid );
+ prom = prom.then(xhr => this.setState({
+ 'item': xhr.response
+ }));
+
+ } else {
+ this.setState({
+ 'item': {
+ 'uuid': uuid
+ }
+ });
+ }
+ }
+
+ render({ uuid }, { item }) {
+ if (!uuid)
+ return (
+ { String(uuid) }
+ );
+
+ return (
+
+ );
+ }
+}
+
+export default WBNameAndUuid;
diff --git a/frontend/src/js/misc/arvados-object-name.js b/frontend/src/js/misc/arvados-object-name.js
index ba670ec..7aad7fd 100644
--- a/frontend/src/js/misc/arvados-object-name.js
+++ b/frontend/src/js/misc/arvados-object-name.js
@@ -4,6 +4,8 @@ function arvadosObjectName(item) {
let typeName = arvadosTypeName(item['uuid']);
if (typeName === 'user')
return (item.first_name + ' ' + item.last_name);
+ else if (typeName === 'container')
+ return ('Container running image ' + item.container_image);
else
return item.name;
}
diff --git a/frontend/src/js/misc/arvados-type-name.js b/frontend/src/js/misc/arvados-type-name.js
index af77515..f547dc1 100644
--- a/frontend/src/js/misc/arvados-type-name.js
+++ b/frontend/src/js/misc/arvados-type-name.js
@@ -9,6 +9,9 @@ const typeIdToName = {
};
function arvadosTypeName(id) {
+ if (!id)
+ return;
+
if (id.length === 5)
return typeIdToName[id];
else
diff --git a/frontend/src/js/page/wb-process-view.js b/frontend/src/js/page/wb-process-view.js
index 1c65a80..254635f 100644
--- a/frontend/src/js/page/wb-process-view.js
+++ b/frontend/src/js/page/wb-process-view.js
@@ -7,6 +7,7 @@ import arvadosTypeName from 'arvados-type-name';
import urlForObject from 'url-for-object';
import detectHashes from 'detect-hashes';
import WBCommonFields from 'wb-common-fields';
+import WBContainerRequestFields from 'wb-container-request-fields';
class WBProcessView extends Component {
constructor(...args) {
@@ -88,8 +89,12 @@ class WBProcessView extends Component {
This is the process view for { uuid }
+ Common Fields
+ Container Request Fields
+
+
diff --git a/frontend/src/js/widget/wb-accordion.js b/frontend/src/js/widget/wb-accordion.js
new file mode 100644
index 0000000..ca44a0b
--- /dev/null
+++ b/frontend/src/js/widget/wb-accordion.js
@@ -0,0 +1,40 @@
+import { h, Component } from 'preact';
+
+class WBAccordion extends Component {
+ constructor(...args) {
+ super(...args);
+ this.state.domId = 'accordion-' + uuid.v4();
+ this.state.headerDomIds = this.props.names.map(() => ('accordion-' + uuid.v4()));
+ this.state.collapseDomIds = this.props.names.map(() => ('accordion-' + uuid.v4()));
+ }
+
+ render({ children, names, cardHeaderClass }, { domId, headerDomIds, collapseDomIds }) {
+ return (
+
+ { children.map((_, i) => (
+
+
+
+
+
+
+
+
+
+ )) }
+
+ );
+ }
+};
+
+WBAccordion.defaultProps = {
+ 'cardHeaderClass': 'card-header'
+};
+
+export default WBAccordion;
diff --git a/frontend/src/js/widget/wb-table.js b/frontend/src/js/widget/wb-table.js
index c698135..fda9bcf 100644
--- a/frontend/src/js/widget/wb-table.js
+++ b/frontend/src/js/widget/wb-table.js
@@ -1,12 +1,12 @@
import { h, Component } from 'preact';
class WBTable extends Component {
- render({ columns, rows }) {
+ render({ columns, rows, headerClasses }) {
return (
- { columns.map(c => { c } | ) }
+ { columns.map((c, i) => { c } | ) }
@@ -23,4 +23,8 @@ class WBTable extends Component {
}
}
+WBTable.defaultProps = {
+ 'headerClasses': []
+};
+
export default WBTable;