From 73addf562fdfbae40ffb68b2261baf05e263cf7a Mon Sep 17 00:00:00 2001 From: picklesrus Date: Fri, 22 Nov 2019 12:15:44 -0500 Subject: [PATCH] Hack to work around replication lag problems on project creation. This makes sure api can get the project before considering the project create to be complete. We ought to refactor gui and www so that we can use the project data we get back from the project create instead. --- package-lock.json | 25 +++++++-- package.json | 1 + src/views/preview/presentation.jsx | 3 ++ src/views/preview/project-view.jsx | 84 ++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f09de5985..305f8501d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12032,6 +12032,24 @@ "prepend-http": "^1.0.0", "query-string": "^4.1.0", "sort-keys": "^1.0.0" + }, + "dependencies": { + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + } } }, "npm-run-path": { @@ -13907,11 +13925,12 @@ "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "dev": true, "requires": { + "decode-uri-component": "^0.2.0", "object-assign": "^4.1.0", "strict-uri-encode": "^1.0.0" } diff --git a/package.json b/package.json index 273993c99..fb3e7dad1 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,7 @@ "po2icu": "0.0.2", "postcss-loader": "2.0.10", "prop-types": "15.6.0", + "query-string": "^5.1.1", "react": "16.2.0", "react-dom": "16.2.0", "react-intl": "2.8.0", diff --git a/src/views/preview/presentation.jsx b/src/views/preview/presentation.jsx index bba60660e..bc423ec9e 100644 --- a/src/views/preview/presentation.jsx +++ b/src/views/preview/presentation.jsx @@ -111,6 +111,7 @@ const PreviewPresentation = ({ onSocialClosed, onToggleComments, onToggleStudio, + onUpdateProjectData, onUpdateProjectId, onUpdateProjectThumbnail, originalInfo, @@ -345,6 +346,7 @@ const PreviewPresentation = ({ onProjectLoaded={onProjectLoaded} onRemixing={onRemixing} onSetProjectThumbnailer={onSetProjectThumbnailer} + onUpdateProjectData={onUpdateProjectData} onUpdateProjectId={onUpdateProjectId} onUpdateProjectThumbnail={onUpdateProjectThumbnail} /> @@ -725,6 +727,7 @@ PreviewPresentation.propTypes = { onSocialClosed: PropTypes.func, onToggleComments: PropTypes.func, onToggleStudio: PropTypes.func, + onUpdateProjectData: PropTypes.func, onUpdateProjectId: PropTypes.func, onUpdateProjectThumbnail: PropTypes.func, originalInfo: projectShape, diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index c41ec84ee..b9471eeaf 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -8,7 +8,9 @@ const PropTypes = require('prop-types'); const connect = require('react-redux').connect; const injectIntl = require('react-intl').injectIntl; const parser = require('scratch-parser'); +const queryString = require('query-string'); +const api = require('../../lib/api'); const Page = require('../../components/page/www/page.jsx'); const storage = require('../../lib/storage.js').default; const log = require('../../lib/log'); @@ -36,6 +38,7 @@ const IntlGUI = injectIntl(GUI.default); const localStorageAvailable = 'localStorage' in window && window.localStorage !== null; const initSentry = require('../../lib/sentry.js'); +const xhr = require('xhr'); initSentry(); class Preview extends React.Component { @@ -73,6 +76,7 @@ class Preview extends React.Component { 'handleSeeInside', 'handleSetProjectThumbnailer', 'handleShare', + 'handleUpdateProjectData', 'handleUpdateProjectId', 'handleUpdateProjectTitle', 'handleToggleComments', @@ -219,6 +223,84 @@ class Preview extends React.Component { this.props.getRemixes(this.state.projectId); } } + + // This is copy of what is in save-project-to-server in GUI that adds + // an extra get of the project info from api. We do this to wait for replication + // lag to pass. This is intended to be a temporary fix until we use the data + // from the create request to fill the projectInfo state. + handleUpdateProjectData (projectId, vmState, params) { + const opts = { + body: vmState, + // If we set json:true then the body is double-stringified, so don't + headers: { + 'Content-Type': 'application/json' + }, + withCredentials: true + }; + const creatingProject = projectId === null || typeof projectId === 'undefined'; + const queryParams = {}; + if (params.hasOwnProperty('originalId')) queryParams.original_id = params.originalId; + if (params.hasOwnProperty('isCopy')) queryParams.is_copy = params.isCopy; + if (params.hasOwnProperty('isRemix')) queryParams.is_remix = params.isRemix; + if (params.hasOwnProperty('title')) queryParams.title = params.title; + let qs = queryString.stringify(queryParams); + if (qs) qs = `?${qs}`; + if (creatingProject) { + Object.assign(opts, { + method: 'post', + url: `${this.props.projectHost}/${qs}` + }); + } else { + Object.assign(opts, { + method: 'put', + url: `${this.props.projectHost}/${projectId}${qs}` + }); + } + return new Promise((resolve, reject) => { + xhr(opts, (err, response) => { + if (err) return reject(err); + if (response.statusCode !== 200) return reject(response.statusCode); + let body; + try { + // Since we didn't set json: true, we have to parse manually + body = JSON.parse(response.body); + } catch (e) { + return reject(e); + } + body.id = projectId; + if (creatingProject) { + body.id = body['content-name']; + } + resolve(body); + }); + }).then(body => { + const fetchProjectInfo = (count, resolve) => { + api({ + uri: `/projects/${body.id}`, + authentication: this.props.user.token + }, (err, projectInfo, response) => { + if (err) { + log.error(`Could not fetch project after creating: ${err}`); + return resolve(body); + } + if (typeof body === 'undefined' || response.statusCode === 404) { + // Retry after 500ms, 1.5s, 3.5s, 7.5s and then stop. + if (count > 4) { + return resolve(body); + } + return setTimeout( + fetchProjectInfo.bind(this, count + 1, resolve), + 500 * Math.pow(2, count)); + } + return resolve(body); + }); + }; + if (creatingProject) { + return new Promise((resolve, reject) => fetchProjectInfo(1, resolve, reject)); + } + return body; + }); + } setScreenFromOrientation () { /* * If the user is on a mobile device, switching to @@ -702,6 +784,7 @@ class Preview extends React.Component { onSocialClosed={this.handleSocialClose} onToggleComments={this.handleToggleComments} onToggleStudio={this.handleToggleStudio} + onUpdateProjectData={this.handleUpdateProjectData} onUpdateProjectId={this.handleUpdateProjectId} onUpdateProjectThumbnail={this.props.handleUpdateProjectThumbnail} /> @@ -739,6 +822,7 @@ class Preview extends React.Component { onSetLanguage={this.handleSetLanguage} onShare={this.handleShare} onToggleLoginOpen={this.props.handleToggleLoginOpen} + onUpdateProjectData={this.handleUpdateProjectData} onUpdateProjectId={this.handleUpdateProjectId} onUpdateProjectThumbnail={this.props.handleUpdateProjectThumbnail} onUpdateProjectTitle={this.handleUpdateProjectTitle}