Skip to content

Commit

Permalink
feat: remove merge function
Browse files Browse the repository at this point in the history
  • Loading branch information
2wce committed Nov 14, 2023
1 parent 894a413 commit a0b15b6
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 164 deletions.
1 change: 1 addition & 0 deletions packages/live-preview-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@vitejs/plugin-react-swc": "^3.0.0",
"contentful": "^10.3.4",
"contentful-management": "^11.0.1",
"flatted": "^3.2.9",
"graphql": "^16.6.0",
"graphql-tag": "^2.12.6",
"jsdom": "^22.0.0",
Expand Down
10 changes: 7 additions & 3 deletions packages/live-preview-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { type DocumentNode } from 'graphql';
import { version } from '../package.json';
import { getAllTaggedEntries } from './fieldTaggingUtils';
import {
sendMessageToEditor,
pollUrlChanges,
setDebugMode,
debug,
isInsideIframe,
pollUrlChanges,
sendMessageToEditor,
setDebugMode,
} from './helpers';
import { isValidMessage } from './helpers/validateMessage';
import { InspectorMode } from './inspectorMode';
Expand Down Expand Up @@ -268,3 +268,7 @@ export class ContentfulLivePreview {

export { LIVE_PREVIEW_EDITOR_SOURCE, LIVE_PREVIEW_SDK_SOURCE } from './constants';
export * from './messages';

export const newFunc = () => {
console.log('newFunc');
};
168 changes: 9 additions & 159 deletions packages/live-preview-sdk/src/liveUpdates.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Asset, Entry } from 'contentful';

import { stringify } from 'flatted';
import type {
ContentfulSubscribeConfig,
EditorMessage,
Expand All @@ -9,21 +10,18 @@ import type {
PostMessageMethods,
SubscribedMessage,
} from '.';
import * as gql from './graphql';
import { parseGraphQLParams } from './graphql/queryUtils';
import { clone, generateUID, sendMessageToEditor, StorageMap, debug } from './helpers';
import { StorageMap, debug, generateUID, sendMessageToEditor } from './helpers';
import { validateDataForLiveUpdates } from './helpers/validation';
import { LivePreviewPostMessageMethods } from './messages';
import * as rest from './rest';
import {
Argument,
ContentType,
Entity,
EntityWithSys,
EntityReferenceMap,
hasSysInformation,
Subscription,
GraphQLParams,
Subscription,
hasSysInformation,
} from './types';

interface MergeEntityProps {
Expand Down Expand Up @@ -56,170 +54,20 @@ export class LiveUpdates {
window.addEventListener('beforeunload', () => this.clearStorage());
}

private async mergeEntity({
contentType,
dataFromPreviewApp,
entityReferenceMap,
locale,
updateFromEntryEditor,
gqlParams,
}: Omit<MergeEntityProps, 'dataFromPreviewApp'> & {
dataFromPreviewApp: EntityWithSys;
}): Promise<{
data: Entity;
updated: boolean;
}> {
if ('__typename' in dataFromPreviewApp) {
// GraphQL
const data = await (dataFromPreviewApp.__typename === 'Asset'
? gql.updateAsset(dataFromPreviewApp, updateFromEntryEditor as Asset, gqlParams)
: gql.updateEntry({
contentType,
dataFromPreviewApp,
updateFromEntryEditor: updateFromEntryEditor as Entry,
locale,
entityReferenceMap,
gqlParams,
sendMessage: this.sendMessage,
}));

return {
data,
updated: true,
};
}

if (this.isCfEntity(dataFromPreviewApp)) {
// REST
const depth = 0;
const visitedReferenceMap = new Map<string, rest.Reference>();
return {
data: await rest.updateEntity(
contentType,
dataFromPreviewApp as Entry,
updateFromEntryEditor as Entry,
locale,
entityReferenceMap,
depth,
visitedReferenceMap,
this.sendMessage
),
updated: true,
};
}

return { updated: false, data: dataFromPreviewApp };
}

/**
* Merges the `dataFromPreviewApp` together with the `updateFromEntryEditor`
* If there is not direct match, it will try to merge things together recursively
* caches the result if cache is enabled and the entity has a `sys.id`.
* Caching should not be enabled for every entry,
* because nested references could be merged differently together and this could solve to data loss.
*/
private async mergeNestedReference(
{ dataFromPreviewApp, ...params }: MergeEntityProps,
useCache: boolean
): Promise<{ data: Entity; updated: boolean }> {
const dataFromPreviewappId = hasSysInformation(dataFromPreviewApp) && dataFromPreviewApp.sys.id;
const isCacheable = useCache && dataFromPreviewappId;

// Flag to detect if something got updated and trigger only the subscription's if necessary
// TODO: This is still not perfect as it doesn't check if anything got updated, only if something could have been merged.
let updated = false;
// If the entity is cacheable and it was once proceeded we use this one as base
let result: Entity =
(isCacheable ? this.storage.get(dataFromPreviewappId, params.locale) : undefined) ||
dataFromPreviewApp;

if (hasSysInformation(result) && dataFromPreviewappId === params.updateFromEntryEditor.sys.id) {
// Happy path, direct match from received and provided data
// Let's update it
const merged = await this.mergeEntity({ ...params, dataFromPreviewApp: result });
result = merged.data;
updated = merged.updated;
} else {
// No direct match, let's check if there is a nested reference and then update it
for (const key in result) {
if (result[key] && typeof result[key] === 'object') {
// TODO: set `useCache` to true if none of the parents could be cached
const match = await this.merge(
{ ...params, dataFromPreviewApp: result[key] as Argument },
false
);
result[key] = match.data;
updated = updated || match.updated;
}
}
}

if (isCacheable) {
// Cache the updated data for future updates
this.storage.set(dataFromPreviewappId, params.locale, result);
}

return { data: result, updated };
}

private async merge(
{ dataFromPreviewApp, ...params }: MergeArgumentProps,
useCache = true
): Promise<{
updated: boolean;
data: Argument;
}> {
if (Array.isArray(dataFromPreviewApp)) {
const data: Entity[] = [];
let updated = false;

for (const d of dataFromPreviewApp) {
const result = await this.mergeNestedReference(
{ ...params, dataFromPreviewApp: d },
useCache
);

data.push(result.data);
updated = updated || result.updated;
}

return { data, updated };
}

return this.mergeNestedReference({ ...params, dataFromPreviewApp }, useCache);
}

private isCfEntity(entity: unknown): entity is Asset | Entry {
return hasSysInformation(entity) && 'fields' in entity;
}

/** Receives the data from the message event handler and calls the subscriptions */
public async receiveMessage(message: MessageFromEditor): Promise<void> {
if (
('action' in message && message.action === 'ENTRY_UPDATED') ||
message.method === LivePreviewPostMessageMethods.ENTRY_UPDATED
) {
const { entity, contentType, entityReferenceMap } = message as EntryUpdatedMessage;
const { entity } = message as EntryUpdatedMessage;

await Promise.all(
[...this.subscriptions].map(async ([, s]) => {
try {
const { updated, data } = await this.merge({
// Clone the original data on the top level,
// to prevent cloning multiple times (time)
// or modifying the original data (failure potential)
dataFromPreviewApp: clone(s.data),
locale: s.locale || this.defaultLocale,
updateFromEntryEditor: entity,
contentType: contentType,
entityReferenceMap: entityReferenceMap,
gqlParams: s.gqlParams,
});

// Only if there was an update, trigger the callback to unnecessary re-renders
if (updated) {
s.callback(data);
}

s.callback(message.data);
} catch (error) {
this.sendErrorMessage({
message: (error as Error).message,
Expand Down Expand Up @@ -321,6 +169,8 @@ export class LiveUpdates {
locale,
entryId: sysId,
event: 'edit',
id,
config: stringify(config),
} as SubscribedMessage);

return () => {
Expand Down
5 changes: 4 additions & 1 deletion packages/live-preview-sdk/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ enum LivePreviewPostMessageMethods {
}

export {
StorePostMessageMethods,
LivePreviewPostMessageMethods,
RequestEntitiesMessage,
RequestedEntitiesMessage,
StorePostMessageMethods,
};
export type PostMessageMethods = LivePreviewPostMessageMethods | StorePostMessageMethods;

Expand Down Expand Up @@ -83,6 +83,8 @@ export type SubscribedMessage = {
entryId: string;
locale: string;
event: 'edit' | 'save';
id: string;
config: string;
};

export type ErrorMessage = {
Expand Down Expand Up @@ -156,6 +158,7 @@ export type MessageFromEditor = (
| DebugModeEnabledMessage
| RequestedEntitiesMessage
) & {
data: any;
method: PostMessageMethods;
/** @deprecated use source instead */
from: 'live-preview';
Expand Down
2 changes: 1 addition & 1 deletion packages/live-preview-sdk/src/saveEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class SaveEvent {

public receiveMessage(message: Omit<MessageFromEditor, 'from' | 'source'>): void {
if (message.method === LivePreviewPostMessageMethods.ENTRY_SAVED && this.subscription) {
const { entity } = message as EntrySavedMessage;
const { entity } = message as unknown as EntrySavedMessage;
const entries = getAllTaggedEntries();

if (entries.includes(entity.sys.id)) {
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4278,6 +4278,11 @@ flatted@^3.1.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==

flatted@^3.2.9:
version "3.2.9"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==

follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
Expand Down

0 comments on commit a0b15b6

Please sign in to comment.