Skip to content

Commit

Permalink
fix: allow overriding the targetOrigin for messaging []
Browse files Browse the repository at this point in the history
  • Loading branch information
chrishelgert committed Oct 16, 2023
1 parent 1597fdd commit a1f6188
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 79 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ ContentfulLivePreview.init({
enableInspectorMode: false, // This allows you to toggle the inspector mode which is on by default
enableLiveUpdates: false, // This allows you to toggle the live updates which is on by default
debugMode: false, // This allows you to toggle the debug mode which is off by default
targetOrigin: 'https://app.contentful.com', // This allows you to configure the allowed host of the live preview (default: ['https://app.contentful.com', 'https://app.eu.contentful.com'])
});
```

Expand Down
79 changes: 45 additions & 34 deletions packages/live-preview-sdk/src/__tests__/liveUpdates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,6 @@ vi.mock('../helpers/debug');
describe('LiveUpdates', () => {
const sendMessage = vi.spyOn(helpers, 'sendMessageToEditor');

beforeEach(() => {
sendMessage.mockImplementation(() => {
// noop
});
});

afterEach(() => {
vi.clearAllMocks();
});

const contentType = {
sys: { id: 'Test' },
fields: [
Expand All @@ -44,6 +34,7 @@ describe('LiveUpdates', () => {
},
],
} as unknown as ContentType;

const locale = 'en-US';
const updateFromEntryEditor1 = {
sys: { id: '1' },
Expand All @@ -54,8 +45,20 @@ describe('LiveUpdates', () => {
fields: { title: 'Data 3' },
} as unknown as Entry;

const targetOrigin = ['https://app.contentful.com'];

beforeEach(() => {
sendMessage.mockImplementation(() => {
// noop
});
});

afterEach(() => {
vi.clearAllMocks();
});

it('should listen to changes and calls the subscribed handlers', async () => {
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });
const data = { sys: { id: '1' }, title: 'Data 1', __typename: 'Demo' };
const callback = vi.fn();
liveUpdates.subscribe({ data, callback });
Expand Down Expand Up @@ -95,7 +98,7 @@ describe('LiveUpdates', () => {

describe('invalid subscription data', () => {
it('should notify because sys information is missing', () => {
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });
const data = { title: 'Data 1', __typename: 'Demo' };
const callback = vi.fn();

Expand All @@ -109,7 +112,7 @@ describe('LiveUpdates', () => {
});

it('no longer receives updates after unsubcribing', async () => {
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });
const data = { sys: { id: '1' }, title: 'Data 1', __typename: 'Demo' };
const callback = vi.fn();
const unsubscribe = liveUpdates.subscribe({ data, callback });
Expand Down Expand Up @@ -142,7 +145,7 @@ describe('LiveUpdates', () => {
});

it('ignores invalid messages', async () => {
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });
const data = { sys: { id: '1' }, title: 'Data 1', __typename: 'Demo' };
const callback = vi.fn();
liveUpdates.subscribe({ data, callback });
Expand All @@ -159,7 +162,7 @@ describe('LiveUpdates', () => {
});

it('doesnt call the subscribe handler if the data was not updated', () => {
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });
const data = { sys: { id: '99' }, title: 'Data 1', __typename: 'Demo' };
const callback = vi.fn();
liveUpdates.subscribe({ data, locale, callback });
Expand All @@ -178,7 +181,7 @@ describe('LiveUpdates', () => {
});

it('merges nested field updates', async () => {
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });
const callback = vi.fn();
liveUpdates.subscribe({ data: nestedDataFromPreviewApp, callback });
await liveUpdates.receiveMessage({
Expand All @@ -199,7 +202,7 @@ describe('LiveUpdates', () => {
});

it('merges nested collections', async () => {
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });
const callback = vi.fn();
liveUpdates.subscribe({
data: nestedCollectionFromPreviewApp,
Expand Down Expand Up @@ -240,35 +243,43 @@ describe('LiveUpdates', () => {

describe('sendMessageToEditor', () => {
it('sends a message to the editor for a subscription with GQL data', () => {
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });
const data = { sys: { id: '1' }, title: 'Data 1', __typename: 'Demo' };
const callback = vi.fn();
liveUpdates.subscribe({ data, callback });

expect(sendMessage).toHaveBeenCalledTimes(1);
expect(sendMessage).toHaveBeenCalledWith(LivePreviewPostMessageMethods.SUBSCRIBED, {
action: LivePreviewPostMessageMethods.SUBSCRIBED,
type: 'GQL',
locale,
entryId: '1',
event: 'edit',
});
expect(sendMessage).toHaveBeenCalledWith(
LivePreviewPostMessageMethods.SUBSCRIBED,
{
action: LivePreviewPostMessageMethods.SUBSCRIBED,
type: 'GQL',
locale,
entryId: '1',
event: 'edit',
},
['https://app.contentful.com']
);
});

it('sends a message to the editor for a subscription with REST data', () => {
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });
const data = { sys: { id: '1' }, fields: { title: 'Data 1' } };
const callback = vi.fn();
liveUpdates.subscribe({ data, callback });

expect(sendMessage).toHaveBeenCalledTimes(1);
expect(sendMessage).toHaveBeenCalledWith(LivePreviewPostMessageMethods.SUBSCRIBED, {
action: LivePreviewPostMessageMethods.SUBSCRIBED,
type: 'REST',
locale,
entryId: '1',
event: 'edit',
});
expect(sendMessage).toHaveBeenCalledWith(
LivePreviewPostMessageMethods.SUBSCRIBED,
{
action: LivePreviewPostMessageMethods.SUBSCRIBED,
type: 'REST',
locale,
entryId: '1',
event: 'edit',
},
['https://app.contentful.com']
);
});
});

Expand All @@ -282,7 +293,7 @@ describe('LiveUpdates', () => {
event: 'edit',
sysId: id,
};
const liveUpdates = new LiveUpdates({ locale });
const liveUpdates = new LiveUpdates({ locale, targetOrigin });

beforeEach(() => {
liveUpdates.subscribe(subscription);
Expand Down
2 changes: 0 additions & 2 deletions packages/live-preview-sdk/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,3 @@ export const MAX_DEPTH = 10;

export const LIVE_PREVIEW_EDITOR_SOURCE = 'live-preview-editor' as const;
export const LIVE_PREVIEW_SDK_SOURCE = 'live-preview-sdk' as const;

export const CONTENTFUL_ORIGINS = ['https://app.contentful.com', 'https://app.eu.contentful.com'];
14 changes: 9 additions & 5 deletions packages/live-preview-sdk/src/helpers/resolveReference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import type { Asset, Entry } from 'contentful';

import { generateTypeName } from '../graphql/utils';
import { ASSET_TYPENAME, EntityReferenceMap } from '../types';
import { sendMessageToEditor } from './utils';

const store: Record<string, EditorEntityStore> = {};

function getStore(locale: string): EditorEntityStore {
function getStore(
locale: string,
sendMessage: EditorEntityStore['sendMessage']
): EditorEntityStore {
if (!store[locale]) {
store[locale] = new EditorEntityStore({
entities: [],
sendMessage: sendMessageToEditor,
sendMessage,
subscribe: (method, cb) => {
// TODO: move this to a generic subscribe function on ContentfulLivePreview
const listeners = (
Expand Down Expand Up @@ -56,11 +58,13 @@ export async function resolveReference({
referenceId,
isAsset,
locale,
sendMessage,
}: {
entityReferenceMap: EntityReferenceMap;
referenceId: string;
isAsset?: boolean;
locale: string;
sendMessage: EditorEntityStore['sendMessage'];
}): Promise<{ reference: Entry | Asset; typeName: string }> {
const reference = entityReferenceMap.get(referenceId);

Expand All @@ -75,7 +79,7 @@ export async function resolveReference({
}

if (isAsset) {
const result = await getStore(locale).fetchAsset(referenceId);
const result = await getStore(locale, sendMessage).fetchAsset(referenceId);
if (!result) {
throw new Error(`Unknown reference ${referenceId}`);
}
Expand All @@ -86,7 +90,7 @@ export async function resolveReference({
};
}

const result = await getStore(locale).fetchEntry(referenceId);
const result = await getStore(locale, sendMessage).fetchEntry(referenceId);
if (!result) {
throw new Error(`Unknown reference ${referenceId}`);
}
Expand Down
10 changes: 7 additions & 3 deletions packages/live-preview-sdk/src/helpers/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { version } from '../../package.json';
import { CONTENTFUL_ORIGINS, LIVE_PREVIEW_SDK_SOURCE } from '../constants';
import { LIVE_PREVIEW_SDK_SOURCE } from '../constants';
import { PostMessageMethods } from '../messages';
import type { EditorMessage, MessageFromSDK } from '../messages';
import { debug } from './debug';
Expand All @@ -8,7 +8,11 @@ import { debug } from './debug';
* Sends the given message to the editor
* enhances it with the information necessary to be accepted
*/
export function sendMessageToEditor(method: PostMessageMethods, data: EditorMessage): void {
export function sendMessageToEditor(
method: PostMessageMethods,
data: EditorMessage,
targetOrigin: string[]
): void {
const message: MessageFromSDK = {
...data,
method,
Expand All @@ -20,7 +24,7 @@ export function sendMessageToEditor(method: PostMessageMethods, data: EditorMess

debug.log('Send message', message);

CONTENTFUL_ORIGINS.forEach((origin) => {
targetOrigin.forEach((origin) => {
window.top?.postMessage(message, origin);
});
}
Expand Down
56 changes: 40 additions & 16 deletions packages/live-preview-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,18 @@ import {

export const VERSION = version;

const DEFAULT_ORIGINS = ['https://app.contentful.com', 'https://app.eu.contentful.com'];

export interface ContentfulLivePreviewInitConfig {
locale: string;
debugMode?: boolean;
enableInspectorMode?: boolean;
enableLiveUpdates?: boolean;
/**
* Contentful host in which the website should be shown
* Can be `https://app.contentful.com` or `https://app.eu.contentful.com`
*/
targetOrigin?: string | string[];
}

export interface ContentfulSubscribeConfig {
Expand All @@ -54,6 +61,7 @@ export class ContentfulLivePreview {
static inspectorModeEnabled = true;
static liveUpdatesEnabled = true;
static locale: string;
static targetOrigin: string[];

// Static method to initialize the LivePreview SDK
static init(config: ContentfulLivePreviewInitConfig): Promise<InspectorMode | null> | undefined {
Expand All @@ -63,7 +71,13 @@ export class ContentfulLivePreview {
);
}

const { debugMode, enableInspectorMode, enableLiveUpdates, locale } = config;
const {
debugMode,
enableInspectorMode,
enableLiveUpdates,
locale,
targetOrigin = DEFAULT_ORIGINS,
} = config;

// Check if running in a browser environment
if (typeof window !== 'undefined') {
Expand All @@ -90,6 +104,8 @@ export class ContentfulLivePreview {

this.locale = locale;

this.targetOrigin = Array.isArray(targetOrigin) ? targetOrigin : [targetOrigin];

if (this.initialized) {
debug.log('You have already initialized the Live Preview SDK.');
return Promise.resolve(ContentfulLivePreview.inspectorMode);
Expand All @@ -101,7 +117,7 @@ export class ContentfulLivePreview {
}

if (this.liveUpdatesEnabled) {
this.liveUpdates = new LiveUpdates({ locale });
this.liveUpdates = new LiveUpdates({ locale, origins: this.targetOrigin });
this.saveEvent = new SaveEvent({ locale });
}

Expand Down Expand Up @@ -133,23 +149,31 @@ export class ContentfulLivePreview {

// navigation changes
pollUrlChanges(() => {
sendMessageToEditor(LivePreviewPostMessageMethods.URL_CHANGED, {
action: LivePreviewPostMessageMethods.URL_CHANGED,
taggedElementCount: document.querySelectorAll(`[${TagAttributes.ENTRY_ID}]`).length,
} as UrlChangedMessage);
sendMessageToEditor(
LivePreviewPostMessageMethods.URL_CHANGED,
{
action: LivePreviewPostMessageMethods.URL_CHANGED,
taggedElementCount: document.querySelectorAll(`[${TagAttributes.ENTRY_ID}]`).length,
} as UrlChangedMessage,
this.targetOrigin
);
});

// tell the editor that there's a SDK
const taggedElementCount = document.querySelectorAll(`[${TagAttributes.ENTRY_ID}]`).length;
sendMessageToEditor(LivePreviewPostMessageMethods.CONNECTED, {
action: LivePreviewPostMessageMethods.CONNECTED,
connected: true,
tags: taggedElementCount,
taggedElementCount,
locale: this.locale,
isInspectorEnabled: this.inspectorModeEnabled,
isLiveUpdatesEnabled: this.liveUpdatesEnabled,
} as ConnectedMessage);
sendMessageToEditor(
LivePreviewPostMessageMethods.CONNECTED,
{
action: LivePreviewPostMessageMethods.CONNECTED,
connected: true,
tags: taggedElementCount,
taggedElementCount,
locale: this.locale,
isInspectorEnabled: this.inspectorModeEnabled,
isLiveUpdatesEnabled: this.liveUpdatesEnabled,
} as ConnectedMessage,
this.targetOrigin
);

// all set up - ready to go
this.initialized = true;
Expand Down Expand Up @@ -228,7 +252,7 @@ export class ContentfulLivePreview {
debug.error('Please provide field id and entry id to openEntryInEditor.');
}

openEntryInEditorUtility(fieldId, entryId, locale || this.locale);
openEntryInEditorUtility(fieldId, entryId, locale || this.locale, this.targetOrigin);
}

/**
Expand Down
Loading

0 comments on commit a1f6188

Please sign in to comment.