diff --git a/projects/sys/default.wmf.yaml b/projects/sys/default.wmf.yaml index 9f9f9ef7..6f51e13d 100644 --- a/projects/sys/default.wmf.yaml +++ b/projects/sys/default.wmf.yaml @@ -11,7 +11,6 @@ paths: - path: sys/parsoid.js options: host: '{{options.parsoid.host}}' - disabled_storage: '{{options.parsoid.disabled_storage}}' response_cache_control: '{{options.purged_cache_control}}' grace_ttl: '{{default(options.parsoid.grace_ttl, 86400)}}' # A list of pages that we don't want to re-render on each edit. diff --git a/sys/parsoid.js b/sys/parsoid.js index 653e6d7e..057617b4 100644 --- a/sys/parsoid.js +++ b/sys/parsoid.js @@ -6,23 +6,11 @@ const URI = HyperSwitch.URI; const HTTPError = HyperSwitch.HTTPError; const uuidv1 = require('uuid').v1; -const uuidUtils = require('../lib/uuidUtils'); const mwUtil = require('../lib/mwUtil'); const spec = HyperSwitch.utils.loadSpec(`${__dirname}/parsoid.yaml`); -// Temporary work-around for Parsoid issue -// https://phabricator.wikimedia.org/T93715 -function normalizeHtml(html) { - return html && html.toString && - html.toString() - .replace(/ about="[^"]+"(?=[/> ])|]{0,128}>/g, ''); -} -function sameHtml(a, b) { - return normalizeHtml(a) === normalizeHtml(b); -} - /** * Makes sure we have a meta tag for the tid in our output * @param {string} html original HTML content @@ -45,26 +33,6 @@ function extractTidMeta(html) { return tidMatch && (tidMatch[1] || tidMatch[2]); } -/** - * Checks whether the content has been modified since the timestamp - * in `if-unmodified-since` header of the request - * @param {Object} req the request - * @param {Object} res the response - * @return {boolean} true if content has beed modified - */ -function isModifiedSince(req, res) { - try { - if (req.headers['if-unmodified-since']) { - const jobTime = Date.parse(req.headers['if-unmodified-since']); - const revInfo = mwUtil.parseETag(res.headers.etag); - return revInfo && uuidUtils.getDate(revInfo.tid) >= jobTime; - } - } catch (e) { - // Ignore errors from date parsing - } - return false; -} - /** HTML resource_change event emission * @param {HyperSwitch} hyper the hyperswitch router object * @param {Object} req the request @@ -72,6 +40,7 @@ function isModifiedSince(req, res) { * @return {Object} update response */ function _dependenciesUpdate(hyper, req, newContent = true) { + // FIXME: we still need to do this purging! const rp = req.params; return mwUtil.getSiteInfo(hyper, req) .then((siteInfo) => { @@ -90,15 +59,6 @@ function _dependenciesUpdate(hyper, req, newContent = true) { }); } -function compileReRenderBlacklist(blacklist) { - const result = {}; - blacklist = blacklist || {}; - Object.keys(blacklist).forEach((domain) => { - result[domain] = mwUtil.constructRegex(blacklist[domain]); - }); - return result; -} - class ParsoidService { constructor(options) { this._initOpts(options); @@ -120,9 +80,7 @@ class ParsoidService { _initOpts(opts = {}) { this.options = opts; this.parsoidUri = opts.host || opts.parsoidHost; - this.options.stash_ratelimit = opts.stash_ratelimit || 5; delete this.options.parsoidHost; - this._blacklist = compileReRenderBlacklist(opts.rerenderBlacklist); if (!this.parsoidUri) { throw new Error('Parsoid module: the option host must be provided!'); } @@ -132,47 +90,6 @@ class ParsoidService { } } - _isStorageDisabled(domain) { - if (!this.options.disabled_storage) { - return false; - } - - if (this.options.disabled_storage === true) { - return true; - } - - return this.options.disabled_storage[domain] || this.options.disabled_storage.default; - } - - _checkStashRate(hyper, req) { - if (!hyper.ratelimiter) { - return; - } - if (hyper._rootReq.headers['x-request-class'] !== 'external') { - return; - } - if (!((req.query && req.query.stash) || (req.body && req.body.stash))) { - return; - } - const key = `${hyper.config.service_name}.parsoid_stash|` + - `${hyper._rootReq.headers['x-client-ip']}`; - if (hyper.ratelimiter.isAboveLimit(key, this.options.stash_ratelimit)) { - hyper.logger.log('warn/parsoid/stashlimit', { - key, - rate_limit_per_second: this.options.stash_ratelimit, - message: 'Stashing rate limit exceeded' - }); - throw new HTTPError({ - status: 429, - body: { - type: 'request_rate_exceeded', - title: 'Stashing rate limit exceeded', - rate_limit_per_second: this.options.stash_ratelimit - } - }); - } - } - /** * Assembles the request that is to be used to call the Parsoid service * @@ -190,35 +107,6 @@ class ParsoidService { }; } - /** - * Gets the URI of a bucket for the latest Parsoid content - * - * @param {string} domain the domain name - * @param {string} title the article title - * @return {HyperSwitch.URI} - */ - _getLatestBucketURI(domain, title) { - return new URI([ - domain, 'sys', 'key_value', 'parsoidphp', title - ]); - } - - /** - * Gets the URI of a bucket for stashing Parsoid content. Used both for stashing - * original HTML/Data-Parsoid for normal edits as well as for stashing transforms - * - * @param {string} domain the domain name - * @param {string} title the article title - * @param {number} revision the revision of the article - * @param {string} tid the TID of the content - * @return {HyperSwitch.URI} - */ - _getStashBucketURI(domain, title, revision, tid) { - return new URI([ - domain, 'sys', 'key_value', 'parsoidphp-stash', `${title}:${revision}:${tid}` - ]); - } - getFormatAndCheck(format, hyper, req) { return this.getFormat(format, hyper, req) .tap((res) => { @@ -234,174 +122,6 @@ class ParsoidService { }); } - /** - * Get full content from the stash bucket. - * @param {HyperSwitch} hyper the hyper object to route requests - * @param {string} domain the domain name - * @param {string} title the article title - * @param {number} revision the article revision - * @param {string} tid the render TID - * @return {Promise} the promise resolving to full stashed Parsoid - * response or a stashed transform - * @private - */ - _getStashedContent(hyper, domain, title, revision, tid) { - return hyper.get({ - uri: this._getStashBucketURI(domain, title, revision, tid) - }) - .then((res) => { - res = res.body; - res.revid = revision; - return res; - }); - } - - _saveParsoidResultToFallback(hyper, req, parsoidResp) { - const rp = req.params; - const dataParsoidResponse = parsoidResp.body['data-parsoid']; - const htmlResponse = parsoidResp.body.html; - const etag = mwUtil.parseETag(parsoidResp.headers.etag); - return hyper.put({ - uri: this._getStashBucketURI(rp.domain, rp.title, etag.rev, etag.tid), - // Note. The headers we are storing here are for the whole pagebundle response. - // The individual components of the pagebundle contain their own headers that - // which are used to generate actual responses. - headers: { - 'x-store-etag': parsoidResp.headers.etag, - 'content-type': 'application/octet-stream', - 'x-store-content-type': 'application/json' - }, - body: Buffer.from(JSON.stringify({ - 'data-parsoid': dataParsoidResponse, - html: htmlResponse - })) - }); - } - - /** - * Saves the Parsoid pagebundle result to the latest bucket. - * // TODO: Optimization opportunity. We look what's in the - * // latest bucket yet again and make the store request a no-op if - * // it's not really the latest content, but we have already looked into - * // the latest bucket, thus we can somehow pass the data over here - * // so that we don't do several checks. - * @param {HyperSwitch} hyper the hyper object for request routing - * @param {string} domain the domain name - * @param {string} title the page title - * @param {Object} parsoidResp the response received from Parsoid. - * @return {Promise} - */ - saveParsoidResultToLatest(hyper, domain, title, parsoidResp) { - const dataParsoidResponse = parsoidResp.body['data-parsoid']; - const htmlResponse = parsoidResp.body.html; - return hyper.get({ uri: this._getLatestBucketURI(domain, title) }) - .then((existingRes) => { - // TODO: This is a race condition and we're doing a write after read - // in a distributed concurrent environment. For revisions this should - // not be a big problem, but once(if) we start supporting lightweight transactions - // in the storage component, we might want to rethink this. - const existingRev = mwUtil.parseETag(existingRes.headers.etag).rev; - const newRev = mwUtil.parseETag(parsoidResp.headers.etag).rev; - if (Number.parseInt(newRev, 10) >= Number.parseInt(existingRev, 10)) { - throw new HTTPError({ status: 412 }); - } - return existingRes; - }) - .catch({ status: 404 }, { status: 412 }, () => hyper.put({ - uri: this._getLatestBucketURI(domain, title), - // Note. The headers we are storing here are for the whole pagebundle response. - // The individual components of the pagebundle contain their own headers that - // which are used to generate actual responses. - headers: { - 'x-store-etag': htmlResponse.headers.etag, - 'content-type': 'application/octet-stream', - 'x-store-content-type': 'application/json' - }, - body: Buffer.from(JSON.stringify({ - 'data-parsoid': dataParsoidResponse, - html: htmlResponse - })) - })); - } - - stashTransform(hyper, req, transformPromise) { - // A stash has been requested. We need to store the wikitext sent by - // the client together with the page bundle returned by Parsoid, so it - // can be later reused when transforming back from HTML to wikitext - // cf https://phabricator.wikimedia.org/T114548 - const rp = req.params; - const tid = uuidv1(); - const etag = mwUtil.makeETag(rp.revision, tid, 'stash'); - const wtType = req.original && req.original.headers['content-type'] || 'text/plain'; - return transformPromise.then((original) => hyper.put({ - uri: this._getStashBucketURI(rp.domain, rp.title, rp.revision, tid), - headers: { - 'x-store-etag': etag, - 'content-type': 'application/octet-stream', - 'x-store-content-type': 'application/json' - }, - body: Buffer.from(JSON.stringify({ - 'data-parsoid': original.body['data-parsoid'], - wikitext: { - headers: { 'content-type': wtType }, - body: req.body.wikitext - }, - html: original.body.html - })) - }) - // Add the ETag to the original response so it can be propagated back to the client - .then(() => { - original.body.html.headers.etag = etag; - return original; - })); - } - - /** - * Returns the content with fallback to the stash. Revision and TID are optional. - * If only 'title' is provided, only 'latest' bucket is checked. - * If 'title' and 'revision' are provided, first the 'latest' bucket is checked. - * Then, the stored revision is compared, if they do not equal, 404 is returned - * as we can not check the stash with no tid provided. - * If all the 'title', 'revision' and 'tid' are provided, - * we check the latest bucket first, and then the stash bucket. - * @param {HyperSwitch} hyper the hyper object to rout requests - * @param {string} domain the domain name - * @param {string} title the article title - * @param {number} [revision] the article revision - * @param {string} [tid] the render TID - * @return {Promise} the promise that resolves to full stashed Parsoid response - * @private - */ - _getContentWithFallback(hyper, domain, title, revision, tid) { - if (!revision && !tid) { - return hyper.get({ uri: this._getLatestBucketURI(domain, title) }); - } else if (!tid) { - return hyper.get({ uri: this._getLatestBucketURI(domain, title) }) - .then((res) => { - const resEtag = mwUtil.parseETag(res.headers.etag); - if (revision !== resEtag.rev) { - throw new HTTPError({ status: 404 }); - } - return res; - }); - } else { - return hyper.get({ - uri: this._getStashBucketURI(domain, title, revision, tid) - }) - .catch({ status: 404 }, () => - hyper.get({ uri: this._getLatestBucketURI(domain, title) }) - .then((res) => { - const resEtag = mwUtil.parseETag(res.headers.etag); - if (revision !== resEtag.rev || tid !== resEtag.tid) { - throw new HTTPError({ status: 404 }); - } - res.headers['x-restbase-cache'] = 'miss'; - return res; - }) - ); - } - } - _getPageBundleFromParsoid(hyper, req) { const rp = req.params; let path = `page/pagebundle/${encodeURIComponent(rp.title)}`; @@ -423,167 +143,8 @@ class ParsoidService { }); } - /** - * Generate content and store it in the latest bucket if the content is indeed - * newer then the original content we have fetched. - * @param {HyperSwitch} hyper the hyper object for request routing - * @param {Object} req the original request - * @param {Object} currentContentRes the pagebundle received from latest or fallback bucket. - * @return {Promise} - */ - generateAndSave(hyper, req, currentContentRes) { - // Try to generate HTML on the fly by calling Parsoid - const rp = req.params; - return this.getRevisionInfo(hyper, req) - .then((revInfo) => { - rp.revision = revInfo.rev; - }) - .then(() => P.join(this._getPageBundleFromParsoid(hyper, req), currentContentRes) - .spread((res, currentContentRes) => { - const tid = uuidv1(); - const etag = mwUtil.makeETag(rp.revision, tid); - res.body.html.body = insertTidMeta(res.body.html.body, tid); - res.body.html.headers.etag = res.headers.etag = etag; - - if (currentContentRes && - currentContentRes.status === 200 && - sameHtml(res.body.html.body, currentContentRes.body.html.body) && - currentContentRes.body.html.headers['content-type'] === - res.body.html.headers['content-type']) { - // New render is the same as the previous one, no need to store it. - hyper.metrics.makeMetric({ - type: 'Counter', - name: 'unchanged_rev_render', // shared with sys/key_value - prometheus: { - name: 'restbase_unchanged_rev_render_total', - help: 'unchanged rev render count' - }, - labels: { - names: ['path', 'bucket'], - omitLabelNames: true - } - }).increment(1, ['parsoid', 'generateAndSave']); - return currentContentRes; - } else if (res.status === 200) { - let newContent = false; - return this.saveParsoidResultToLatest(hyper, rp.domain, rp.title, res) - .then((saveRes) => { - if (saveRes.status === 201) { - newContent = true; - } - // Extract redirect target, if any - const redirectTarget = mwUtil.extractRedirect(res.body.html.body); - if (redirectTarget) { - // This revision is actually a redirect. Pass redirect target - // to caller, and let it rewrite the location header. - res.status = 302; - res.headers.location = encodeURIComponent(redirectTarget) - .replace(/%23/, '#'); - } - }) - .then(() => { - let dependencyUpdate = P.resolve(); - if (!this.options.skip_updates) { - dependencyUpdate = _dependenciesUpdate(hyper, req, newContent); - } - if (mwUtil.isNoCacheRequest(req)) { - // Finish background updates before returning - return dependencyUpdate.thenReturn(res); - } else { - return res; - } - }); - } else { - return res; - } - })); - } - - /** - * Internal check to see if it's okay to re-render a particular title in - * response to a no-cache request. - * - * TODO: Remove this temporary code once - * https://phabricator.wikimedia.org/T120171 and - * https://phabricator.wikimedia.org/T120972 are resolved / resource - * consumption for these articles has been reduced to a reasonable level. - * @param {Request} req the request being processed - * @return {boolean} Whether re-rendering this title is okay. - */ - _okayToRerender(req) { - if (mwUtil.isNoCacheRequest(req) && this._blacklist[req.params.domain]) { - return !this._blacklist[req.params.domain].test(req.params.title); - } - return true; - } - getFormat(format, hyper, req) { - const rp = req.params; - const generateContent = (storageRes) => { - if (!rp.tid && (storageRes.status === 404 || storageRes.status === 200)) { - return this.generateAndSave(hyper, req, storageRes).then((res) => { - res.headers['x-restbase-cache'] = 'miss'; - return res; - }); - } else { - // Don't generate content if there's some other error. - throw storageRes; - } - }; - - if (!this._okayToRerender(req)) { - // Still update the revision metadata. - return this.getRevisionInfo(hyper, req) - .then(() => { - throw new HTTPError({ - status: 403, - body: { - type: 'bad_request#rerenders_disabled', - description: 'Rerenders for this article are blacklisted in the config.' - } - }); - }); - } - - // check the rate limit for stashing requests - this._checkStashRate(hyper, req); - - let contentReq; - - const disabledStorage = this._isStorageDisabled(req.params.domain); - const isNoCacheRequest = mwUtil.isNoCacheRequest(req); - - if (disabledStorage && !isNoCacheRequest) { - contentReq = this._getPageBundleFromParsoid(hyper, req); - } else { - contentReq = this._getContentWithFallback( - hyper, rp.domain, rp.title, rp.revision, rp.tid - ).then((res) => { - res.headers['x-restbase-cache'] = res.headers['x-restbase-cache'] || 'hit'; // FIXME: miss? - return res; - }); - - if (isNoCacheRequest) { - // Check content generation either way - contentReq = contentReq.then((res) => { - res.headers['x-restbase-cache'] = 'nocache'; - - if (isModifiedSince(req, res)) { // Already up to date, nothing to do. - throw new HTTPError({ - status: 412, - body: { - type: 'precondition_failed', - detail: 'The precondition failed' - } - }); - } - return generateContent(res); - }, generateContent); - } else { - // Only (possibly) generate content if there was an error - contentReq = contentReq.catch(generateContent); - } - } + const contentReq = this._getPageBundleFromParsoid(hyper, req); return contentReq .then((res) => { @@ -592,59 +153,25 @@ class ParsoidService { res.headers.etag = res.body.html.headers && res.body.html.headers.etag; } if (!res.headers.etag || /^null$/.test(res.headers.etag)) { - if (disabledStorage) { - // Generate an ETag, for consistency - const tid = uuidv1(); - const revid = res.headers['content-revision-id'] || '0'; - const etag = mwUtil.makeETag(revid, tid); - res.body.html.body = insertTidMeta(res.body.html.body, tid); - res.body.html.headers.etag = res.headers.etag = etag; - res.headers['x-restbase-cache'] = 'disabled'; - } else { - // if there is no ETag, we *could* create one here, but this - // would mean at least cache pollution, and would hide the - // fact that we have incomplete data in storage, so error out - hyper.logger.log('error/parsoid/response_etag_missing', { - msg: 'Detected a null etag in the response!' - }); - throw new HTTPError({ - status: 500, - body: { - title: 'no_etag', - description: 'No ETag has been provided in the response' - } - }); - } - } - if (req.query.stash) { - return this._saveParsoidResultToFallback(hyper, req, res) - .thenReturn(res); + // Generate an ETag, for consistency + const tid = uuidv1(); + const revid = res.headers['content-revision-id'] || '0'; + const etag = mwUtil.makeETag(revid, tid); + res.body.html.body = insertTidMeta(res.body.html.body, tid); + res.body.html.headers.etag = res.headers.etag = etag; } return res; }) .then((res) => { const etag = res.headers.etag; - const cacheInfo = res.headers['x-restbase-cache']; // Chop off the correct format to return. res = Object.assign({ status: res.status }, res.body[format]); res.headers = res.headers || {}; res.headers.etag = etag; - if (cacheInfo) { - res.headers['x-restbase-cache'] = cacheInfo; - } - mwUtil.normalizeContentType(res); - if (req.query.stash) { - // The stash is used by clients that want further support - // for transforming the content. If the content is stored in caches, - // subsequent requests might not even reach RESTBase and the stash - // will expire, thus no-cache. - res.headers['cache-control'] = 'no-cache'; - } else if (this.options.response_cache_control) { - res.headers['cache-control'] = this.options.response_cache_control; - } + res.headers['cache-control'] = this.options.response_cache_control; return res; }); @@ -701,29 +228,7 @@ class ParsoidService { // reject with 404. In this case we would just not provide it. contentPromise = P.resolve(undefined); } else { - if (etag && etag.suffix === 'stash' && from === 'html' && to === 'wikitext') { - // T235465: RB should trust its own ETag over the client-supplied revision, but - // allow for the client to be right, so provide their revision as a fall-back - const revMismatch = etag.rev !== rp.revision; - if (revMismatch) { - // the ETag and URI parameter do not agree, log this (for now?) - hyper.logger.log('warn/parsoid/etag_rev', { - msg: 'The revisions in If-Match and URI differ' - }); - } - contentPromise = this._getStashedContent(hyper, rp.domain, - rp.title, etag.rev, etag.tid) - .catch({ status: 404 }, (e) => { - if (!revMismatch) { - // the revisions match, so this is a genuine 404 - throw e; - } - return this._getStashedContent( - hyper, rp.domain, rp.title, rp.revision, tid); - }); - } else { - contentPromise = this._getOriginalContent(hyper, req, rp.revision, tid); - } + contentPromise = this._getOriginalContent(hyper, req, rp.revision, tid); contentPromise = contentPromise .tap((original) => { // Check if parsoid metadata is present as it's required by parsoid. @@ -759,8 +264,7 @@ class ParsoidService { original, [from]: req.body[from], scrub_wikitext: req.body.scrub_wikitext, - body_only: req.body.body_only, - stash: req.body.stash + body_only: req.body.body_only } }; return this.callParsoidTransform(hyper, newReq, from, to); @@ -810,12 +314,7 @@ class ParsoidService { req.body ); - const transformPromise = hyper.post(parsoidReq); - if (req.body.stash && from === 'wikitext' && to === 'html') { - return this.stashTransform(hyper, req, transformPromise); - } - return transformPromise; - + return hyper.post(parsoidReq); } getLintErrors(hyper, req) { @@ -855,23 +354,6 @@ class ParsoidService { } }); } - // check if we have all the info for stashing - if (req.body.stash) { - if (!rp.title) { - throw new HTTPError({ - status: 400, - body: { - type: 'bad_request', - description: 'Data can be stashed only for a specific title.' - } - }); - } - if (!rp.revision) { - rp.revision = '0'; - } - // check the rate limit for stashing requests - this._checkStashRate(hyper, req); - } let transform; if (rp.revision && rp.revision !== '0') { @@ -931,7 +413,7 @@ class ParsoidService { 'cache-control': req.headers && req.headers['cache-control'] } }) - .then((res) => res.body.items[0]); + .then((res) => res.body.items[0]); } _getOriginalContent(hyper, req, revision, tid) { @@ -949,28 +431,6 @@ module.exports = (options = {}) => { const ps = new ParsoidService(options); return { spec, - operations: ps.operations, - // Dynamic resource dependencies, specific to implementation - resources: [ - { - uri: '/{domain}/sys/key_value/parsoidphp', - headers: { - 'content-type': 'application/json' - }, - body: { - valueType: 'blob' - } - }, - { - uri: '/{domain}/sys/key_value/parsoidphp-stash', - headers: { - 'content-type': 'application/json' - }, - body: { - valueType: 'blob', - default_time_to_live: options.grace_ttl || 86400 - } - } - ] + operations: ps.operations }; }; diff --git a/test/features/pagecontent/pagecontent.js b/test/features/pagecontent/pagecontent.js index 0fef6302..58b35571 100644 --- a/test/features/pagecontent/pagecontent.js +++ b/test/features/pagecontent/pagecontent.js @@ -69,7 +69,7 @@ describe('item requests', function() { }); }); - it('should transparently create a new HTML revision for Main_Page', function () { + it('should fetch for Main_Page', function () { return preq.get({ uri: `${server.config.bucketURL()}/html/Main_Page`, }) @@ -77,53 +77,12 @@ describe('item requests', function() { assert.deepEqual(res.status, 200); assert.validateListHeader(res.headers.vary, { require: ['Accept'], disallow: [''] }); - // NOTE: We have to accept "hit" here, because the test setup has a persistent cache. - assert.deepEqual(res.headers['x-restbase-cache'], 'hit|miss'); - return preq.get({ uri: `${server.config.bucketURL()}/html/Main_Page` }); }) - .then((res) => { - assert.deepEqual(res.status, 200); - assert.deepEqual(res.headers['x-restbase-cache'], 'hit'); - assert.validateListHeader(res.headers.vary, { require: ['Accept'], disallow: [''] }); - }); - }); - it('should not cache HTML for Main_Page when storage is disabled', function () { - return preq.get({ - uri: `${server.config.bucketURL(domainWithStorageDisabled)}/html/Página_principal`, - }) - .then((res) => { - assert.deepEqual(res.status, 200); - assert.deepEqual(res.headers['x-restbase-cache'], 'disabled'); - - // should still not be cached - return sysGet( domainWithStorageDisabled, `key_value/parsoidphp/Página_principal` ); - }).then((res) => { - // if this mails, make sure you are resetting the database between tests - assert.deepEqual(res.status, 404); - }).catch((res) => { - assert.deepEqual(res.status, 404); - }) - }); - it('should cache HTML for Main_Page even when storage is disabled (cache-control: no-cache)', function () { - return preq.get({ - uri: `${server.config.bucketURL(domainWithStorageDisabled)}/html/Página_principal`, - headers: { - 'cache-control': 'no-cache' - } - }) - .then((res) => { - assert.deepEqual(res.status, 200); - - // should now be cached - return sysGet( domainWithStorageDisabled, `key_value/parsoidphp/Página_principal` ); - }).then((res) => { - assert.deepEqual(res.status, 200); - }) }); - it(`should transparently create a new HTML revision with id ${prevRevisions[0]}`, () => { + it(`should fetch HTML revision with id ${prevRevisions[0]}`, () => { return preq.get({ uri: `${server.config.bucketURL()}/html/${title}/${prevRevisions[0]}`, }) @@ -132,17 +91,8 @@ describe('item requests', function() { assert.validateListHeader(res.headers.vary, { require: ['Accept'], disallow: [''] }); }); }); - it('should not allow to frontend cache HTML if requested a stash', () => { - return preq.get({ - uri: `${server.config.bucketURL()}/html/${title}?stash=true`, - }) - .then((res) => { - assert.deepEqual(res.status, 200); - assert.deepEqual(res.headers['cache-control'], 'no-cache'); - }); - }); - it('should request page lints. no revision', () => { + it('should fetch page lints. no revision', () => { return preq.get({ uri: `${server.config.bucketURL()}/lint/User%3APchelolo%2FLintTest` }) @@ -152,7 +102,7 @@ describe('item requests', function() { }); }); - it('should request page lints. with revision', () => { + it('should fetch page lints. with revision', () => { return preq.get({ uri: `${server.config.bucketURL()}/lint/User%3APchelolo%2FLintTest/409437` }) @@ -162,44 +112,13 @@ describe('item requests', function() { }); }); - let rev2Etag; - it(`should transparently create data-parsoid with id ${prevRevisions[1]}, rev 2`, () => { + it(`should return data-parsoid with id ${prevRevisions[1]}, rev 2`, () => { return preq.get({ - uri: `${server.config.bucketURL()}/html/${title}/${prevRevisions[1]}?stash=true` + uri: `${server.config.bucketURL()}/html/${title}/${prevRevisions[1]}` }) .then((res) => { assert.deepEqual(res.status, 200); assert.validateListHeader(res.headers.vary, { require: ['Accept'], disallow: [''] }); - rev2Etag = res.headers.etag.replace(/^"(.*)"$/, '$1'); - }); - }); - - it(`should return data-parsoid just created with revision ${rev2Etag}, rev 2`, () => { - return preq.get({ - uri: `${server.config.bucketURL()}/data-parsoid/${title}/${rev2Etag}` - }) - .then((res) => { - assert.deepEqual(res.status, 200); - assert.contentType(res, contentTypes['data-parsoid']); - }); - }); - - it(`should return HTML and data-parsoid just created by revision ${prevRevisions[2]}`, () => { - return preq.get({ - uri: `${server.config.bucketURL()}/html/${title}/${prevRevisions[2]}?stash=true` - }) - .then((res) => { - assert.deepEqual(res.status, 200); - assert.contentType(res, contentTypes.html); - assert.validateListHeader(res.headers.vary, { require: ['Accept'], disallow: [''] }); - return preq.get({ - uri: `${server.config.bucketURL()}/data-parsoid/${title}/${ - res.headers.etag.replace(/^"(.*)"$/, '$1')}` - }); - }) - .then((res) => { - assert.deepEqual(res.status, 200); - assert.contentType(res, contentTypes['data-parsoid']); }); });