From 07f6353f909031a2caea2ca2a11aaba4766c5a78 Mon Sep 17 00:00:00 2001 From: Aodhagan Murphy Date: Mon, 23 Oct 2023 15:25:44 +0100 Subject: [PATCH] fix: fixing circular ref updates by using store of already fetched data [] --- packages/live-preview-sdk/src/liveUpdates.ts | 9 +-- .../src/rest/__tests__/entities.test.ts | 4 +- .../live-preview-sdk/src/rest/entities.ts | 56 +++++++++---------- packages/live-preview-sdk/src/rest/index.ts | 1 + 4 files changed, 32 insertions(+), 38 deletions(-) diff --git a/packages/live-preview-sdk/src/liveUpdates.ts b/packages/live-preview-sdk/src/liveUpdates.ts index 2441c8a9..faeea11d 100644 --- a/packages/live-preview-sdk/src/liveUpdates.ts +++ b/packages/live-preview-sdk/src/liveUpdates.ts @@ -31,7 +31,6 @@ interface MergeEntityProps { contentType: ContentType; entityReferenceMap: EntityReferenceMap; gqlParams?: GraphQLParams; - visitedReferences: Set; } interface MergeArgumentProps extends Omit { @@ -60,7 +59,6 @@ export class LiveUpdates { locale, updateFromEntryEditor, gqlParams, - visitedReferences, }: Omit & { dataFromPreviewApp: EntityWithSys; }): Promise<{ @@ -89,6 +87,7 @@ export class LiveUpdates { if (this.isCfEntity(dataFromPreviewApp)) { // REST const depth = 0; + const visitedReferenceMap = new Map(); return { data: await rest.updateEntity( contentType, @@ -97,7 +96,7 @@ export class LiveUpdates { locale, entityReferenceMap, depth, - visitedReferences + visitedReferenceMap ), updated: true, }; @@ -199,9 +198,6 @@ export class LiveUpdates { await Promise.all( [...this.subscriptions].map(async ([, s]) => { try { - // Reset the visitedReferences set for each new update. - const visitedReferences = new Set(); - const { updated, data } = await this.merge({ // Clone the original data on the top level, // to prevent cloning multiple times (time) @@ -212,7 +208,6 @@ export class LiveUpdates { contentType: contentType, entityReferenceMap: entityReferenceMap, gqlParams: s.gqlParams, - visitedReferences, }); // Only if there was an update, trigger the callback to unnecessary re-renders diff --git a/packages/live-preview-sdk/src/rest/__tests__/entities.test.ts b/packages/live-preview-sdk/src/rest/__tests__/entities.test.ts index 5ad82585..b4656e90 100644 --- a/packages/live-preview-sdk/src/rest/__tests__/entities.test.ts +++ b/packages/live-preview-sdk/src/rest/__tests__/entities.test.ts @@ -6,7 +6,7 @@ import contentTypeAsset from '../../__tests__/fixtures/contentTypeAsset.json'; import { MAX_DEPTH } from '../../constants'; import { clone, resolveReference } from '../../helpers'; import { EntityReferenceMap } from '../../types'; -import { updateEntity } from '../entities'; +import { Reference, updateEntity } from '../entities'; import { EN, referenceWithRichTextId } from './constants'; import contentTypeEntryJSON from './fixtures/contentType.json'; import { @@ -67,7 +67,7 @@ describe('Update REST entry', () => { locale?: string; entityReferenceMap?: EntityReferenceMap; }) => { - const visitedReferences = new Set(); + const visitedReferences = new Map(); return updateEntity( contentType, diff --git a/packages/live-preview-sdk/src/rest/entities.ts b/packages/live-preview-sdk/src/rest/entities.ts index 339f3280..96cf5aa8 100644 --- a/packages/live-preview-sdk/src/rest/entities.ts +++ b/packages/live-preview-sdk/src/rest/entities.ts @@ -12,7 +12,7 @@ import { } from '../helpers/entities'; import { ContentType, EntityReferenceMap } from '../types'; -type Reference = Asset | Entry | WithResourceName; +export type Reference = Asset | Entry | WithResourceName; /** * Resolves the correct field name from the ContentType @@ -36,23 +36,20 @@ async function updateRef( locale: string, entityReferenceMap: EntityReferenceMap, depth: number, - visitedReferences: Set + visitedReferenceMap: Map ): Promise { let reference; + const id = + 'urn' in updateFromEntryEditor.sys + ? updateFromEntryEditor.sys.urn + : updateFromEntryEditor.sys.id; + // If the ID of the updateFromEntryEditor is in visitedReferences, then stop the recursion - if (visitedReferences.has(updateFromEntryEditor.sys.id)) { + // if (visitedReferences.has(updateFromEntryEditor.sys.id)) { + if (visitedReferenceMap.has(id)) { debug.warn('Detected a circular reference, stopping recursion'); - reference = entityReferenceMap.get( - 'urn' in updateFromEntryEditor.sys - ? updateFromEntryEditor.sys.urn - : updateFromEntryEditor.sys.id - ); + reference = visitedReferenceMap.get(id); } else { - visitedReferences.add( - 'urn' in updateFromEntryEditor.sys - ? updateFromEntryEditor.sys.urn - : updateFromEntryEditor.sys.id - ); const { reference: resolvedReference } = await resolveReference({ entityReferenceMap, referenceId: updateFromEntryEditor.sys.id, @@ -60,6 +57,7 @@ async function updateRef( locale, }); reference = resolvedReference; + visitedReferenceMap.set(id, resolvedReference); } if (!reference) { @@ -82,7 +80,7 @@ async function updateRef( key as keyof Reference['fields'], entityReferenceMap, depth + 1, - visitedReferences + visitedReferenceMap ); // multi ref fields } else if (Array.isArray(value) && isEntityLink(value[0]) && depth < MAX_DEPTH) { @@ -93,7 +91,7 @@ async function updateRef( key as keyof Reference['fields'], entityReferenceMap, depth + 1, - visitedReferences + visitedReferenceMap ); // rich text fields } else if (isRichText(value)) { @@ -104,7 +102,7 @@ async function updateRef( locale, entityReferenceMap, depth + 1, - visitedReferences + visitedReferenceMap ); // single and multi resource link fields } else if (isResourceLink(value) || (Array.isArray(value) && isResourceLink(value[0]))) { @@ -132,7 +130,7 @@ async function updateMultiRefField( name: keyof Reference['fields'], entityReferenceMap: EntityReferenceMap, depth: number, - visitedReferences: Set + visitedReferenceMap: Map ) { if (!updateFromEntryEditor.fields?.[name]) { delete dataFromPreviewApp.fields[name]; @@ -148,7 +146,7 @@ async function updateMultiRefField( locale, entityReferenceMap, depth + 1, - visitedReferences + visitedReferenceMap ) ) ).then((list) => list.filter(Boolean))) as Reference[]; @@ -164,7 +162,7 @@ async function updateSingleRefField( name: keyof Reference['fields'], entityReferenceMap: EntityReferenceMap, depth: number, - visitedReferences: Set + visitedReferenceMap: Map ) { const matchUpdateFromEntryEditor = updateFromEntryEditor?.fields?.[name] as Reference | undefined; @@ -181,7 +179,7 @@ async function updateSingleRefField( locale, entityReferenceMap, depth + 1, - visitedReferences + visitedReferenceMap ); return dataFromPreviewApp; @@ -192,7 +190,7 @@ async function resolveRichTextLinks( entityReferenceMap: EntityReferenceMap, locale: string, depth: number, - visitedReferences: Set + visitedReferenceMap: Map ) { if (SUPPORTED_RICHTEXT_EMBEDS.includes(node.nodeType)) { if (node.data && node.data.target && node.data.target.sys) { @@ -208,7 +206,7 @@ async function resolveRichTextLinks( locale, entityReferenceMap, depth + 1, - visitedReferences + visitedReferenceMap ); } } @@ -221,7 +219,7 @@ async function resolveRichTextLinks( entityReferenceMap, locale, depth + 1, - visitedReferences + visitedReferenceMap ); } } @@ -234,7 +232,7 @@ async function updateRichTextField( locale: string, entityReferenceMap: EntityReferenceMap, depth: number, - visitedReferences: Set + visitedReferenceMap: Map ) { const richText = updateFromEntryEditor.fields?.[name]; @@ -243,7 +241,7 @@ async function updateRichTextField( dataFromPreviewApp.fields[name] = richText; // Resolve the linked entries or assets within the rich text field for (const node of richText.content) { - await resolveRichTextLinks(node, entityReferenceMap, locale, depth, visitedReferences); + await resolveRichTextLinks(node, entityReferenceMap, locale, depth, visitedReferenceMap); } } } @@ -264,7 +262,7 @@ export async function updateEntity( locale: string, entityReferenceMap: EntityReferenceMap, depth: number, - visitedReferences: Set + visitedReferenceMap: Map ): Promise { if (dataFromPreviewApp.sys.id !== updateFromEntryEditor.sys.id) { return dataFromPreviewApp; @@ -287,7 +285,7 @@ export async function updateEntity( name as keyof Reference['fields'], entityReferenceMap, depth + 1, - visitedReferences + visitedReferenceMap ); } else if (field.type === 'Array' && field.items?.type === 'Link' && depth < MAX_DEPTH) { await updateMultiRefField( @@ -297,7 +295,7 @@ export async function updateEntity( name as keyof Reference['fields'], entityReferenceMap, depth + 1, - visitedReferences + visitedReferenceMap ); } else if (field.type === 'RichText') { await updateRichTextField( @@ -307,7 +305,7 @@ export async function updateEntity( locale, entityReferenceMap, depth, - visitedReferences + visitedReferenceMap ); } else if (field.type === 'ResourceLink') { //@TODO -- add live updates for resource links diff --git a/packages/live-preview-sdk/src/rest/index.ts b/packages/live-preview-sdk/src/rest/index.ts index dedb25d7..8c6753e8 100644 --- a/packages/live-preview-sdk/src/rest/index.ts +++ b/packages/live-preview-sdk/src/rest/index.ts @@ -1 +1,2 @@ export { updateEntity } from './entities'; +export type { Reference } from './entities';