From 259d122a74bba9ede5ad13e3ff30b84eb5592fd3 Mon Sep 17 00:00:00 2001 From: Frank Thompson Date: Mon, 22 Jul 2019 11:01:37 -0700 Subject: [PATCH] catch errors when encoding entity map Summary: We've had several reports of people's content failing to save in our product. The only error message we have is this (with no callstack, unfortunately): ``` Unknown DraftEntity key: undefined. ``` I poked around in the DraftJS code, and my guess is that this is due to some character having an entity key associated with it that for whatever reason does not appear in the entity map. This diff "fixes" the issue by not throwing an error in this situation. This is definitely not as good as figuring out the root cause of the entity mismatch, but it seems reasonable for `convertFromDraftStateToRaw` to work with slightly malformed input. Reviewed By: niveditc Differential Revision: D16421104 fbshipit-source-id: a2046ad0cb166dac97187fd3c524f9c8cb4017ea --- .../convertFromDraftStateToRaw-test.js.snap | 23 +++++++++++++++ .../convertFromDraftStateToRaw-test.js | 29 ++++++++++++++----- .../encoding/convertFromDraftStateToRaw.js | 17 +++++++---- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/model/encoding/__tests__/__snapshots__/convertFromDraftStateToRaw-test.js.snap b/src/model/encoding/__tests__/__snapshots__/convertFromDraftStateToRaw-test.js.snap index 6dd517e194..9c5ce8457f 100644 --- a/src/model/encoding/__tests__/__snapshots__/convertFromDraftStateToRaw-test.js.snap +++ b/src/model/encoding/__tests__/__snapshots__/convertFromDraftStateToRaw-test.js.snap @@ -1,5 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`must be able to convert from draft state entities not in map 1`] = ` +Object { + "blocks": Array [ + Object { + "data": Object {}, + "depth": 0, + "entityRanges": Array [ + Object { + "key": 0, + "length": 7, + "offset": 0, + }, + ], + "inlineStyleRanges": Array [], + "key": "a", + "text": "badlink", + "type": "unstyled", + }, + ], + "entityMap": Object {}, +} +`; + exports[`must be able to convert from draft state with ContentBlock to raw 1`] = ` Object { "blocks": Array [ diff --git a/src/model/encoding/__tests__/convertFromDraftStateToRaw-test.js b/src/model/encoding/__tests__/convertFromDraftStateToRaw-test.js index b13e857f48..ec45b45a76 100644 --- a/src/model/encoding/__tests__/convertFromDraftStateToRaw-test.js +++ b/src/model/encoding/__tests__/convertFromDraftStateToRaw-test.js @@ -61,8 +61,8 @@ const treeContentState = contentState.set( ]), ); -const getMetadata = entityKey => - Immutable.Repeat(CharacterMetadata.create({entity: entityKey}), 5); +const getMetadata = (entityKey, length) => + Immutable.Repeat(CharacterMetadata.create({entity: entityKey}), length); const getLink = entityKey => new DraftEntityInstance({ type: 'LINK', @@ -79,23 +79,23 @@ const contentStateWithNonContiguousEntities = ContentState.createFromBlockArray( key: 'a', type: 'unstyled', text: 'link2 link2 link3', - characterList: getMetadata('2') + characterList: getMetadata('2', 5) .toList() .push(CharacterMetadata.EMPTY) - .concat(getMetadata('2')) + .concat(getMetadata('2', 5)) .push(CharacterMetadata.EMPTY) - .concat(getMetadata('3')), + .concat(getMetadata('3', 5)), }), new ContentBlock({ key: 'b', type: 'unstyled', text: 'link4 link2 link5', - characterList: getMetadata('4') + characterList: getMetadata('4', 5) .toList() .push(CharacterMetadata.EMPTY) - .concat(getMetadata('2')) + .concat(getMetadata('2', 5)) .push(CharacterMetadata.EMPTY) - .concat(getMetadata('5')), + .concat(getMetadata('5', 5)), }), ], ) @@ -104,6 +104,15 @@ const contentStateWithNonContiguousEntities = ContentState.createFromBlockArray( .addEntity(getLink('4')) .addEntity(getLink('5')); +const contentStateWithEntitiesNotInMap = ContentState.createFromBlockArray([ + new ContentBlock({ + key: 'a', + type: 'unstyled', + text: 'badlink', + characterList: getMetadata('999', 7).toList(), + }), +]); + const assertConvertFromDraftStateToRaw = content => { expect(convertFromDraftStateToRaw(content)).toMatchSnapshot(); }; @@ -119,3 +128,7 @@ test('must be able to convert from draft state with ContentBlockNode to raw', () test('must be able to convert from draft state with noncontiguous entities to raw', () => { assertConvertFromDraftStateToRaw(contentStateWithNonContiguousEntities); }); + +test('must be able to convert from draft state entities not in map', () => { + assertConvertFromDraftStateToRaw(contentStateWithEntitiesNotInMap); +}); diff --git a/src/model/encoding/convertFromDraftStateToRaw.js b/src/model/encoding/convertFromDraftStateToRaw.js index f64a9dbb5c..720c437782 100644 --- a/src/model/encoding/convertFromDraftStateToRaw.js +++ b/src/model/encoding/convertFromDraftStateToRaw.js @@ -117,12 +117,17 @@ const encodeRawEntityMap = ( const rawEntityMap = {}; Object.keys(entityMap).forEach((key, index) => { - const entity = contentState.getEntity(DraftStringKey.unstringify(key)); - rawEntityMap[index] = { - type: entity.getType(), - mutability: entity.getMutability(), - data: entity.getData(), - }; + try { + const entity = contentState.getEntity(DraftStringKey.unstringify(key)); + rawEntityMap[index] = { + type: entity.getType(), + mutability: entity.getMutability(), + data: entity.getData(), + }; + } catch { + // keep contructing rawEntityMap even if one entity referenced in + // character metadata doesn't exist in contentState + } }); return {