PadMessageHandler: Factor out common USER_CHANGES error message formatting

pull/5285/head
Richard Hansen 2021-11-16 15:55:01 -05:00
parent 35b2aeb3b1
commit 1447ab8899
1 changed files with 46 additions and 55 deletions

View File

@ -601,53 +601,49 @@ const handleUserChanges = async (socket, message) => {
// create the changeset // create the changeset
try { try {
try { // Verify that the changeset has valid syntax and is in canonical form
// Verify that the changeset has valid syntax and is in canonical form Changeset.checkRep(changeset);
Changeset.checkRep(changeset);
// Verify that the attribute indexes used in the changeset are all // Verify that the attribute indexes used in the changeset are all
// defined in the accompanying attribute pool. // defined in the accompanying attribute pool.
Changeset.eachAttribNumber(changeset, (n) => { Changeset.eachAttribNumber(changeset, (n) => {
if (!wireApool.getAttrib(n)) { if (!wireApool.getAttrib(n)) {
throw new Error(`Attribute pool is missing attribute ${n} for changeset ${changeset}`); throw new Error(`Attribute pool is missing attribute ${n} for changeset ${changeset}`);
}
});
// Validate all added 'author' attribs to be the same value as the current user
const iterator = Changeset.opIterator(Changeset.unpack(changeset).ops);
let op;
while (iterator.hasNext()) {
op = iterator.next();
// + can add text with attribs
// = can change or add attribs
// - can have attribs, but they are discarded and don't show up in the attribs -
// but do show up in the pool
op.attribs.split('*').forEach((attr) => {
if (!attr) return;
attr = wireApool.getAttrib(attr);
if (!attr) return;
// the empty author is used in the clearAuthorship functionality so this
// should be the only exception
if ('author' === attr[0] && (attr[1] !== thisSession.author && attr[1] !== '')) {
throw new Error(`Author ${thisSession.author} tried to submit changes as author ` +
`${attr[1]} in changeset ${changeset}`);
} }
}); });
// Validate all added 'author' attribs to be the same value as the current user
const iterator = Changeset.opIterator(Changeset.unpack(changeset).ops);
let op;
while (iterator.hasNext()) {
op = iterator.next();
// + can add text with attribs
// = can change or add attribs
// - can have attribs, but they are discarded and don't show up in the attribs -
// but do show up in the pool
op.attribs.split('*').forEach((attr) => {
if (!attr) return;
attr = wireApool.getAttrib(attr);
if (!attr) return;
// the empty author is used in the clearAuthorship functionality so this
// should be the only exception
if ('author' === attr[0] && (attr[1] !== thisSession.author && attr[1] !== '')) {
throw new Error(`Author ${thisSession.author} tried to submit changes as author ` +
`${attr[1]} in changeset ${changeset}`);
}
});
}
// ex. adoptChangesetAttribs
// Afaik, it copies the new attributes from the changeset, to the global Attribute Pool
changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool);
} catch (e) {
throw new Error(`Can't apply USER_CHANGES from Socket ${socket.id} because: ${e.message}`);
} }
// ex. adoptChangesetAttribs
// Afaik, it copies the new attributes from the changeset, to the global Attribute Pool
changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool);
// ex. applyUserChanges // ex. applyUserChanges
const apool = pad.pool; const apool = pad.pool;
let r = baseRev; let r = baseRev;
@ -665,24 +661,18 @@ const handleUserChanges = async (socket, message) => {
// rebases "changeset" so that it is relative to revision r // rebases "changeset" so that it is relative to revision r
// and can be applied after "c". // and can be applied after "c".
try { // a changeset can be based on an old revision with the same changes in it
// a changeset can be based on an old revision with the same changes in it // prevent eplite from accepting it TODO: better send the client a NEW_CHANGES
// prevent eplite from accepting it TODO: better send the client a NEW_CHANGES // of that revision
// of that revision if (baseRev + 1 === r && c === changeset) throw new Error('Changeset already accepted');
if (baseRev + 1 === r && c === changeset) {
throw new Error("Won't apply USER_CHANGES, as it contains an already accepted changeset");
}
changeset = Changeset.follow(c, changeset, false, apool); changeset = Changeset.follow(c, changeset, false, apool);
} catch (e) {
throw new Error(`Can't apply USER_CHANGES, because ${e.message}`);
}
} }
const prevText = pad.text(); const prevText = pad.text();
if (Changeset.oldLen(changeset) !== prevText.length) { if (Changeset.oldLen(changeset) !== prevText.length) {
throw new Error(`Can't apply USER_CHANGES ${changeset} with oldLen ` + throw new Error(`Can't apply changeset ${changeset} with oldLen ` +
`${Changeset.oldLen(changeset)} to document of length ${prevText.length}`); `${Changeset.oldLen(changeset)} to document of length ${prevText.length}`);
} }
@ -703,7 +693,8 @@ const handleUserChanges = async (socket, message) => {
} catch (err) { } catch (err) {
socket.json.send({disconnect: 'badChangeset'}); socket.json.send({disconnect: 'badChangeset'});
stats.meter('failedChangesets').mark(); stats.meter('failedChangesets').mark();
console.warn(err.stack || err); console.warn(`Failed to apply USER_CHANGES from author ${thisSession.author} ` +
`(socket ${socket.id}) on pad ${thisSession.padId}: ${err.stack || err}`);
} }
stopWatch.end(); stopWatch.end();