chore: Auto reload frontend of client is out of date (#1270)

* Move editor version to header
Add editor version check for API endpoints

* fix: Editor update auto-reload
Bump RME

* fix: Only redirect if editor header exists

* lint
This commit is contained in:
Tom Moor 2020-05-16 14:05:51 -07:00 committed by GitHub
parent 82749ffbd8
commit e0b33ee576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 36 additions and 5 deletions

View File

@ -1,6 +1,5 @@
// @flow // @flow
import { action, set, observable, computed } from 'mobx'; import { action, set, observable, computed } from 'mobx';
import pkg from 'rich-markdown-editor/package.json';
import addDays from 'date-fns/add_days'; import addDays from 'date-fns/add_days';
import invariant from 'invariant'; import invariant from 'invariant';
import { client } from 'utils/ApiClient'; import { client } from 'utils/ApiClient';
@ -180,7 +179,6 @@ export default class Document extends BaseModel {
try { try {
if (isCreating) { if (isCreating) {
return await this.store.create({ return await this.store.create({
editorVersion: pkg.version,
parentDocumentId: this.parentDocumentId, parentDocumentId: this.parentDocumentId,
collectionId: this.collectionId, collectionId: this.collectionId,
title: this.title, title: this.title,
@ -194,7 +192,6 @@ export default class Document extends BaseModel {
title: this.title, title: this.title,
text: this.text, text: this.text,
lastRevision: this.revision, lastRevision: this.revision,
editorVersion: pkg.version,
...options, ...options,
}); });
} finally { } finally {

View File

@ -1,4 +1,5 @@
// @flow // @flow
import pkg from 'rich-markdown-editor/package.json';
import { map, trim } from 'lodash'; import { map, trim } from 'lodash';
import invariant from 'invariant'; import invariant from 'invariant';
import stores from 'stores'; import stores from 'stores';
@ -41,6 +42,7 @@ class ApiClient {
Accept: 'application/json', Accept: 'application/json',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'cache-control': 'no-cache', 'cache-control': 'no-cache',
'x-editor-version': pkg.version,
pragma: 'no-cache', pragma: 'no-cache',
}); });
if (stores.auth.authenticated) { if (stores.auth.authenticated) {
@ -100,6 +102,11 @@ class ApiClient {
// we're trying to parse an error so JSON may not be valid // we're trying to parse an error so JSON may not be valid
} }
if (response.status === 400 && error.error === 'editor_update_required') {
window.location.reload(true);
return;
}
throw error; throw error;
}; };

View File

@ -692,12 +692,13 @@ router.post('documents.create', auth(), async ctx => {
const { const {
title = '', title = '',
text = '', text = '',
editorVersion,
publish, publish,
collectionId, collectionId,
parentDocumentId, parentDocumentId,
index, index,
} = ctx.body; } = ctx.body;
const editorVersion = ctx.headers['x-editor-version'];
ctx.assertUuid(collectionId, 'collectionId must be an uuid'); ctx.assertUuid(collectionId, 'collectionId must be an uuid');
if (parentDocumentId) { if (parentDocumentId) {
ctx.assertUuid(parentDocumentId, 'parentDocumentId must be an uuid'); ctx.assertUuid(parentDocumentId, 'parentDocumentId must be an uuid');
@ -787,10 +788,11 @@ router.post('documents.update', auth(), async ctx => {
publish, publish,
autosave, autosave,
done, done,
editorVersion,
lastRevision, lastRevision,
append, append,
} = ctx.body; } = ctx.body;
const editorVersion = ctx.headers['x-editor-version'];
ctx.assertPresent(id, 'id is required'); ctx.assertPresent(id, 'id is required');
ctx.assertPresent(title || text, 'title or text is required'); ctx.assertPresent(title || text, 'title or text is required');
if (append) ctx.assertPresent(text, 'Text is required while appending'); if (append) ctx.assertPresent(text, 'Text is required while appending');

View File

@ -25,6 +25,7 @@ import validation from '../middlewares/validation';
import methodOverride from '../middlewares/methodOverride'; import methodOverride from '../middlewares/methodOverride';
import cache from './middlewares/cache'; import cache from './middlewares/cache';
import apiWrapper from './middlewares/apiWrapper'; import apiWrapper from './middlewares/apiWrapper';
import editor from './middlewares/editor';
const api = new Koa(); const api = new Koa();
const router = new Router(); const router = new Router();
@ -36,6 +37,7 @@ api.use(methodOverride());
api.use(cache()); api.use(cache());
api.use(validation()); api.use(validation());
api.use(apiWrapper()); api.use(apiWrapper());
api.use(editor());
// routes // routes
router.use('/', auth.routes()); router.use('/', auth.routes());

View File

@ -0,0 +1,17 @@
// @flow
import { type Context } from 'koa';
import pkg from 'rich-markdown-editor/package.json';
import { EditorUpdateError } from '../../errors';
export default function editor() {
return async function editorMiddleware(ctx: Context, next: () => Promise<*>) {
const editorVersion = ctx.headers['x-editor-version'];
// As the client can only ever be behind the server there's no need for a
// more strict check of version infront/behind here
if (editorVersion && editorVersion !== pkg.version) {
throw new EditorUpdateError();
}
return next();
};
}

View File

@ -45,3 +45,9 @@ export function ParamRequiredError(
export function ValidationError(message: string = 'Validation failed') { export function ValidationError(message: string = 'Validation failed') {
return httpErrors(400, message, { id: 'validation_error' }); return httpErrors(400, message, { id: 'validation_error' });
} }
export function EditorUpdateError(
message: string = 'The client editor is out of date and must be reloaded'
) {
return httpErrors(400, message, { id: 'editor_update_required' });
}