diff --git a/gui/package.json b/gui/package.json index 1e46c1cda78c745da58f541a7975f1afa5091395..f61187a1505a0e3ddd9c2529720bde1247657c08 100644 --- a/gui/package.json +++ b/gui/package.json @@ -20,6 +20,7 @@ "react-router-hash-link": "^1.2.0", "react-scripts": "1.1.4", "recompose": "^0.28.2", + "swagger-client": "^3.8.22", "url-parse": "^1.4.3" }, "scripts": { diff --git a/gui/src/api.js b/gui/src/api.js index dca128fa9baaf805ac58f7abcc0b3c38959a9725..f5c848cd961c1a48663a768dc28aaba739e57256 100644 --- a/gui/src/api.js +++ b/gui/src/api.js @@ -1,12 +1,25 @@ import { UploadRequest } from '@navjobs/upload' +import Swagger from 'swagger-client' import { apiBase, appStaticBase } from './config' const auth_headers = { Authorization: 'Basic ' + btoa('sheldon.cooper@nomad-fairdi.tests.de:password') } -const networkError = () => { - throw Error('Network related error, cannot reach API or object storage.') +const swaggerPromise = Swagger(`${apiBase}/swagger.json`, { + authorizations: { + // my_query_auth: new ApiKeyAuthorization('my-query', 'bar', 'query'), + // my_header_auth: new ApiKeyAuthorization('My-Header', 'bar', 'header'), + 'HTTP Basic': { + username: 'sheldon.cooper@nomad-fairdi.tests.de', + password: 'password' + } + // cookie_: new CookieAuthorization('one=two') + } +}) + +const networkError = (e) => { + throw Error('Network related error, cannot reach API: ' + e) } const handleJsonErrors = () => { @@ -27,19 +40,16 @@ const handleResponseErrors = (response) => { class Upload { constructor(json, created) { - this.uploading = null + this.uploading = 0 this._assignFromJson(json, created) } uploadFile(file) { - console.assert(this.upload_url) - this.uploading = 0 - const uploadFileWithProgress = async() => { - let { error, aborted } = await UploadRequest( + let uploadRequest = await UploadRequest( { request: { - url: this.upload_url, + url: `${apiBase}/uploads/?name=${this.name}`, method: 'PUT', headers: { 'Content-Type': 'application/gzip', @@ -53,12 +63,14 @@ class Upload { } } ) - if (error) { - networkError(error) + if (uploadRequest.error) { + networkError(uploadRequest.error) } - if (aborted) { + if (uploadRequest.aborted) { throw Error('User abort') } + this.uploading = 100 + this.upload_id = uploadRequest.response.upload_id } return uploadFileWithProgress() @@ -70,9 +82,7 @@ class Upload { if (this.current_task !== this.tasks[0]) { this.uploading = 100 this.waiting = false - } else if (!created && this.uploading === null) { - // if data came from server during a normal get (not create) and its still uploading - // and the uploading is also not controlled locally then it ought to be a manual upload + } else { this.waiting = true } } @@ -107,26 +117,16 @@ class Upload { } function createUpload(name) { - const fetchData = { - method: 'POST', - body: JSON.stringify({ - name: name - }), - headers: { - 'Content-Type': 'application/json', - ...auth_headers - } - } - return fetch(`${apiBase}/uploads`, fetchData) - .catch(networkError) - .then(handleResponseErrors) - .then(response => response.json()) - .then(uploadJson => new Upload(uploadJson, true)) + return new Upload({ + name: name, + tasks: ['UPLOADING'], + current_task: 'UPLOADING' + }, true) } function getUploads() { return fetch( - `${apiBase}/uploads`, + `${apiBase}/uploads/`, { method: 'GET', headers: auth_headers @@ -145,7 +145,7 @@ function archive(uploadHash, calcHash) { } function calcProcLog(archiveId) { - return fetch(`${apiBase}/logs/${archiveId}`) + return fetch(`${apiBase}/archive/logs/${archiveId}`) .catch(networkError) .then(response => { if (!response.ok) { @@ -173,7 +173,7 @@ function repo(uploadHash, calcHash) { function repoAll(page, perPage, owner) { return fetch( - `${apiBase}/repo?page=${page}&per_page=${perPage}&owner=${owner || 'all'}`, + `${apiBase}/repo/?page=${page}&per_page=${perPage}&owner=${owner || 'all'}`, { method: 'GET', headers: auth_headers @@ -246,7 +246,16 @@ async function getMetaInfo() { } } +async function getUploadCommand() { + const client = await swaggerPromise + return client.apis.uploads.get_upload_command() + .catch(networkError) + .then(handleResponseErrors) + .then(response => response.body.upload_command) +} + const api = { + getUploadCommand: getUploadCommand, createUpload: createUpload, deleteUpload: deleteUpload, unstageUpload: unstageUpload, diff --git a/gui/src/components/ArchiveCalc.js b/gui/src/components/ArchiveCalc.js index 81500cdcfb0c3a23bdd3bff90349c5495791bb3d..3c6ee666cc1f49dca5c797c6e6b9214bb6c9b067 100644 --- a/gui/src/components/ArchiveCalc.js +++ b/gui/src/components/ArchiveCalc.js @@ -108,7 +108,7 @@ class ArchiveCalc extends React.Component { see a *meta-info* description. `}</Markdown> <Typography className={classes.logLink}> - The processing logs are available <a href="#" onClick={() => this.setState({showLogs: true})}>here</a>. + The processing logs are available <a href="#logs" onClick={() => this.setState({showLogs: true})}>here</a>. </Typography> <Paper className={classes.calcData}> { diff --git a/gui/src/components/Documentation.js b/gui/src/components/Documentation.js index 5ed652a68df1da9f1c685dc191c7a4313c427055..7f452964d4b171a7110d8016154338fbbea52238 100644 --- a/gui/src/components/Documentation.js +++ b/gui/src/components/Documentation.js @@ -25,7 +25,7 @@ class Documentation extends Component { return ( <div className={classes.root}> <div className={classes.content}> - <iframe + <iframe title="documentation" frameBorder={0} width="700" height={window.innerHeight - 64} src={`${apiBase}/docs/index.html`} /> diff --git a/gui/src/components/Upload.js b/gui/src/components/Upload.js index 1b354ad97361369d7e95580bb0cfda57d4ae12d7..aac3d3ce65122ad24387707aa0cf8fc2376554c5 100644 --- a/gui/src/components/Upload.js +++ b/gui/src/components/Upload.js @@ -12,7 +12,6 @@ import CalcLinks from './CalcLinks' import { compose } from 'recompose' import { withErrors } from './errors' import { debug } from '../config' -import UploadCommand from './UploadCommand' import CalcProcLogPopper from './CalcProcLogPopper' class Upload extends React.Component { @@ -265,7 +264,7 @@ class Upload extends React.Component { renderCalcTable() { const { classes } = this.props const { page, perPage, orderBy, order } = this.state.params - const { calcs, status, waiting, upload_command } = this.state.upload + const { calcs, status, waiting } = this.state.upload const { pagination, results } = calcs if (pagination.total === 0) { @@ -278,7 +277,9 @@ class Upload extends React.Component { } else { if (waiting) { return ( - <UploadCommand uploadCommand={upload_command} /> + <Typography className={classes.detailsContent}> + Uploading ... + </Typography> ) } else { return ( @@ -323,7 +324,7 @@ class Upload extends React.Component { <Typography color={color}> {(status === 'SUCCESS' || status === 'FAILURE') ? - <a className={classes.logLink} href="#" onClick={() => this.setState({archiveLogs: archive_id})}> + <a className={classes.logLink} href="#logs" onClick={() => this.setState({archiveLogs: archive_id})}> {status.toLowerCase()} </a> : status.toLowerCase() diff --git a/gui/src/components/UploadCommand.js b/gui/src/components/UploadCommand.js deleted file mode 100644 index b09045d538a291cfc5ab24db1a03fd952d2da654..0000000000000000000000000000000000000000 --- a/gui/src/components/UploadCommand.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Typography, withStyles } from '@material-ui/core' - -class UploadCommand extends React.Component { - static propTypes = { - classes: PropTypes.object.isRequired, - uploadCommand: PropTypes.string.isRequired - } - - static styles = theme => ({ - root: { - margin: theme.spacing.unit * 2 - }, - uploadCommand: { - fontFamily: '\'Roboto mono\', monospace', - marginTop: theme.spacing.unit * 2 - } - }) - - render() { - const { classes, uploadCommand } = this.props - return ( - <div className={classes.root}> - <Typography>Copy and use the following command. Don't forget to replace the file name.:</Typography> - <Typography className={classes.uploadCommand}>{uploadCommand}</Typography> - </div> - ) - } -} - -export default withStyles(UploadCommand.styles)(UploadCommand) diff --git a/gui/src/components/Uploads.js b/gui/src/components/Uploads.js index 8ce6b3a0f826928fa60bbd4ee5640e6b18349e0d..b1c2e37c5acc694f0f575023169d010d2978214e 100644 --- a/gui/src/components/Uploads.js +++ b/gui/src/components/Uploads.js @@ -2,8 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import Markdown from './Markdown' import { withStyles, Paper, IconButton, FormGroup, Checkbox, FormControlLabel, FormLabel, - LinearProgress, InputLabel, Input, FormHelperText, Button, Popover, Grid, Typography, - DialogContent, DialogActions} from '@material-ui/core' + LinearProgress } from '@material-ui/core' import UploadIcon from '@material-ui/icons/CloudUpload' import Dropzone from 'react-dropzone' import api from '../api' @@ -12,8 +11,6 @@ import { withErrors } from './errors' import { compose } from 'recompose' import DeleteIcon from '@material-ui/icons/Delete' import CheckIcon from '@material-ui/icons/Check' -import AddIcon from '@material-ui/icons/Add' -import UploadCommand from './UploadCommand' import ConfirmDialog from './ConfirmDialog' class Uploads extends React.Component { @@ -62,44 +59,24 @@ class Uploads extends React.Component { }, uploads: { marginTop: theme.spacing.unit * 2 - }, - uploadFormControl: { - margin: theme.spacing.unit * 2 - }, - button: { - margin: theme.spacing.unit - }, - rightIcon: { - marginLeft: theme.spacing.unit - }, - uploadNameInput: { - width: '100%' - }, - uploadKindHeading: { - paddingBottom: theme.spacing.unit - }, - uploadKindDescription: { - paddingTop: theme.spacing.unit, - paddingBottom: theme.spacing.unit * 2 - }, - commandUpload: { - height: 192 } }) state = { uploads: null, + uploadCommand: null, selectedUploads: [], loading: true, - showAccept: false, - uploadName: '', - uploadCommand: null, - showUploadCommand: false, - uploadPopperAnchor: null + showAccept: false } componentDidMount() { this.update() + api.getUploadCommand() + .then(command => this.setState({uploadCommand: command})) + .catch(error => { + this.props.raiseError(error) + }) } update() { @@ -115,31 +92,6 @@ class Uploads extends React.Component { }) } - onCreateUploadCmdClicked(event) { - event.persist() - const existingUpload = this.state.uploads - .find(upload => upload.name === this.state.uploadName && upload.waiting) - if (existingUpload) { - const upload = existingUpload - this.setState({ - uploadCommand: upload.upload_command, - showUploadCommand: true, - uploadPopperAnchor: event.target}) - } else { - api.createUpload(this.state.uploadName) - .then(upload => { - this.setState({ - uploads: [...this.state.uploads, upload], - uploadCommand: upload.upload_command, - showUploadCommand: true, - uploadPopperAnchor: event.target}) - }) - .catch(error => { - this.props.raiseError(error) - }) - } - } - onDeleteClicked() { this.setState({loading: true}) Promise.all(this.state.selectedUploads.map(upload => api.deleteUpload(upload.upload_id))) @@ -169,13 +121,9 @@ class Uploads extends React.Component { onDrop(files) { files.forEach(file => { - api.createUpload(file.name) - .then(upload => { - this.setState({uploads: [...this.state.uploads, upload]}) - upload.uploadFile(file) - .catch(this.props.raiseError) - }) - .catch(this.props.raiseError) + const upload = api.createUpload(file.name) + this.setState({uploads: [...this.state.uploads, upload]}) + upload.uploadFile(file).catch(this.props.raiseError) }) } @@ -248,7 +196,7 @@ class Uploads extends React.Component { render() { const { classes } = this.props - const { showUploadCommand, uploadCommand, uploadPopperAnchor } = this.state + const { uploadCommand } = this.state return ( <div className={classes.root}> @@ -257,76 +205,32 @@ class Uploads extends React.Component { You can upload your own data. Have your code output ready in a popular archive format (e.g. \`*.zip\` or \`*.tar.gz\`). Your upload can comprise the output of multiple runs, even of different codes. Don't worry, nomad - will find it.`} + will find it, just drop it below:`} + </Markdown> + + <Paper className={classes.dropzoneContainer}> + <Dropzone + accept="application/zip" + className={classes.dropzone} + activeClassName={classes.dropzoneAccept} + rejectClassName={classes.dropzoneReject} + onDrop={this.onDrop.bind(this)} + > + <p>drop files here</p> + <UploadIcon style={{fontSize: 36}}/> + </Dropzone> + </Paper> + + <Markdown>{` + Alternatively, you can upload files via the following shell command. + Replace \`<local_file>\` with your file. After executing the command, + return here and reload. + + \`\`\` + ${uploadCommand} + \`\`\` + `} </Markdown> - <Grid container spacing={24}> - <Grid item xs> - <Typography variant="headline" className={classes.uploadKindHeading}> - Browser upload - </Typography> - <Paper className={classes.dropzoneContainer}> - <Dropzone - accept="application/zip" - className={classes.dropzone} - activeClassName={classes.dropzoneAccept} - rejectClassName={classes.dropzoneReject} - onDrop={this.onDrop.bind(this)} - > - <p>drop files here</p> - <UploadIcon style={{fontSize: 36}}/> - </Dropzone> - </Paper> - <Typography className={classes.uploadKindDescription}> - Just drop your file above. You know this from many other services in the internet. - </Typography> - </Grid> - <Grid item xs> - <Typography variant="headline" className={classes.uploadKindHeading}> - Command upload - </Typography> - <Paper className={classes.commandUpload}> - <DialogContent> - <InputLabel htmlFor="name-helper">Upload name</InputLabel> - <Input className={classes.uploadNameInput} - id="name-helper" value={this.state.uploadName} - onChange={(event) => this.setState({uploadName: event.target.value})} - /> - <FormHelperText id="name-helper-text">optional, helps to track the upload</FormHelperText> - </DialogContent> - <DialogActions> - <Button - color="primary" className={classes.button} variant="contained" - onClick={this.onCreateUploadCmdClicked.bind(this)} - > - add upload - <AddIcon className={classes.rightIcon}/> - </Button> - <Popover - id="upload-command-popper" - onClose={() => this.setState({showUploadCommand: false})} - open={showUploadCommand} - anchorEl={uploadPopperAnchor} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'center' - }} - transformOrigin={{ - vertical: 'top', - horizontal: 'center' - }} - > - <UploadCommand uploadCommand={uploadCommand} /> - </Popover> - </DialogActions> - </Paper> - <Typography className={classes.uploadKindDescription}> - You can upload your file via <strong>curl</strong>. Optionally, you - can provide a name that will help to track different uploads. - Without a name, you only have the upload time to follow your uploads. - You can find the command by unfolding the new upload element. - </Typography> - </Grid> - </Grid> {this.renderUploads()} {this.state.loading ? <LinearProgress/> : ''} diff --git a/gui/yarn.lock b/gui/yarn.lock index 0f25114eeda0f2a249bfbddb351e3cb519c6a351..a9a7488e76af6fafc8ed785b2993ba874e196770 100644 --- a/gui/yarn.lock +++ b/gui/yarn.lock @@ -90,6 +90,16 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" +"@kyleshockey/js-yaml@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@kyleshockey/js-yaml/-/js-yaml-1.0.1.tgz#5c036bb67caee77fa887738e695dc02949889bfd" + dependencies: + argparse "^1.0.7" + +"@kyleshockey/object-assign-deep@^0.4.0": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@kyleshockey/object-assign-deep/-/object-assign-deep-0.4.2.tgz#84900f0eefc372798f4751b5262830b8208922ec" + "@material-ui/core@^1.5.1": version "1.5.1" resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.5.1.tgz#cb00cb934447ae688e08129f1dab55f54d29d87a" @@ -474,7 +484,7 @@ async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.1.2, async@^2.1.4, async@^2.4.1: +async@^2.0.1, async@^2.1.2, async@^2.1.4, async@^2.4.1: version "2.6.1" resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" dependencies: @@ -1434,6 +1444,10 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" +btoa@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.1.2.tgz#3e40b81663f81d2dd6596a4cb714a8dc16cfabe0" + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -1454,6 +1468,13 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -1784,6 +1805,12 @@ combined-stream@1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +combined-stream@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" + dependencies: + delayed-stream "~1.0.0" + commander@2.17.x, commander@^2.11.0: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" @@ -1894,7 +1921,7 @@ cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" -cookie@0.3.1: +cookie@0.3.1, cookie@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" @@ -1966,6 +1993,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-fetch@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-0.0.8.tgz#01ed94dc407df2c00f1807fde700a7cfa48a205c" + dependencies: + node-fetch "1.7.3" + whatwg-fetch "2.0.3" + cross-spawn@5.1.0, cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -2160,6 +2194,10 @@ deep-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" +deep-extend@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f" + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -2448,6 +2486,10 @@ emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" +encode-3986@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/encode-3986/-/encode-3986-1.0.0.tgz#940d51498f8741ade184b75ad1439b317c0c7a60" + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -3031,6 +3073,12 @@ fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" +fast-json-patch@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-2.0.7.tgz#55864b08b1e50381d2f37fd472bb2e18fe54a733" + dependencies: + deep-equal "^1.0.1" + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -3233,6 +3281,14 @@ forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" +form-data@^1.0.0-rc3: + version "1.0.1" + resolved "http://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz#ae315db9a4907fa065502304a66d7733475ee37c" + dependencies: + async "^2.0.1" + combined-stream "^1.0.5" + mime-types "^2.1.11" + form-data@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" @@ -4170,6 +4226,12 @@ isomorphic-fetch@^2.1.1: node-fetch "^1.0.1" whatwg-fetch ">=0.10.0" +isomorphic-form-data@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-form-data/-/isomorphic-form-data-0.0.1.tgz#026f627e032b0cd8413ecc8755928b94a468b062" + dependencies: + form-data "^1.0.0-rc3" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -4841,6 +4903,10 @@ lodash.uniq@^4.5.0: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" +lodash@^4.16.2: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + loglevel@^1.4.1: version "1.6.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa" @@ -5021,12 +5087,22 @@ mime-db@~1.35.0: version "1.35.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47" +mime-db@~1.37.0: + version "1.37.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" + mime-types@2.1.18: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: mime-db "~1.33.0" +mime-types@^2.1.11: + version "2.1.21" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" + dependencies: + mime-db "~1.37.0" + mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.19: version "2.1.19" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0" @@ -5172,7 +5248,7 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" -node-fetch@^1.0.1: +node-fetch@1.7.3, node-fetch@^1.0.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" dependencies: @@ -6081,6 +6157,10 @@ qs@6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" +qs@^6.3.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.6.0.tgz#a99c0f69a8d26bf7ef012f871cdabb0aee4424c2" + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -6092,6 +6172,10 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" +querystring-browser@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/querystring-browser/-/querystring-browser-1.0.4.tgz#f2e35881840a819bc7b1bf597faf0979e6622dc6" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -7249,6 +7333,28 @@ sw-toolbox@^3.4.0: path-to-regexp "^1.0.1" serviceworker-cache-polyfill "^4.0.0" +swagger-client@^3.8.22: + version "3.8.22" + resolved "https://registry.yarnpkg.com/swagger-client/-/swagger-client-3.8.22.tgz#934809e19acd09d5070fdb7ae48bd405d0a18b69" + dependencies: + "@kyleshockey/js-yaml" "^1.0.1" + "@kyleshockey/object-assign-deep" "^0.4.0" + babel-runtime "^6.26.0" + btoa "1.1.2" + buffer "^5.1.0" + cookie "^0.3.1" + cross-fetch "0.0.8" + deep-extend "^0.5.1" + encode-3986 "^1.0.0" + fast-json-patch "^2.0.6" + isomorphic-form-data "0.0.1" + lodash "^4.16.2" + qs "^6.3.0" + querystring-browser "^1.0.4" + url "^0.11.0" + utf8-bytes "0.0.1" + utfstring "^2.0.0" + symbol-observable@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" @@ -7623,6 +7729,14 @@ use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" +utf8-bytes@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/utf8-bytes/-/utf8-bytes-0.0.1.tgz#116b025448c9b500081cdfbf1f4d6c6c37d8837d" + +utfstring@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/utfstring/-/utfstring-2.0.0.tgz#b331f7351e9be1c46334cc7518826cda3b44242a" + util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" diff --git a/nomad/api/app.py b/nomad/api/app.py index 1c81c9761c7e7f44f1f3df1f2277f63eee468bbd..0307ec515d935e88ac6a9c8d8c3e7c085b844dfc 100644 --- a/nomad/api/app.py +++ b/nomad/api/app.py @@ -29,7 +29,7 @@ base_path = config.services.api_base_path app = Flask( __name__, - static_url_path='%s/docs' % base_path, + static_url_path='/docs', static_folder=os.path.abspath(os.path.join(os.path.dirname(__file__), '../../docs/.build/html'))) """ The Flask app that serves all APIs. """ diff --git a/tests/test_api.py b/tests/test_api.py index cd5bbab839dd8923698a6f490c84445fe24a5773..eadce92c409e193f0cba2b2023278081a42cc7ba 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -380,6 +380,7 @@ class TestArchive: def test_docs(client): + rv = client.get('/docs/index.html') rv = client.get('/docs/introduction.html') assert rv.status_code == 200