Skip to content

Commit 4ff6380

Browse files
committed
fix: do not perform link preview enrichment if server-side enrichment is disabled
1 parent 1d9e5ae commit 4ff6380

File tree

6 files changed

+156
-70
lines changed

6 files changed

+156
-70
lines changed

src/messageComposer/linkPreviewsManager.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ export class LinkPreviewsManager implements ILinkPreviewsManager {
7979

8080
constructor({ composer, message }: LinkPreviewsManagerOptions) {
8181
this.composer = composer;
82-
this.state = new StateStore<LinkPreviewsManagerState>(initState({ message }));
82+
this.state = new StateStore<LinkPreviewsManagerState>(
83+
initState({ message: this.enabled ? message : undefined }),
84+
);
8385

8486
this.findAndEnrichUrls = debounce(
8587
this._findAndEnrichUrls.bind(this),
@@ -91,6 +93,10 @@ export class LinkPreviewsManager implements ILinkPreviewsManager {
9193
return this.composer.client;
9294
}
9395

96+
get channel() {
97+
return this.composer.channel;
98+
}
99+
94100
get previews() {
95101
return this.state.getLatestValue().previews;
96102
}
@@ -129,6 +135,10 @@ export class LinkPreviewsManager implements ILinkPreviewsManager {
129135
return this.composer.config.linkPreviews;
130136
}
131137

138+
get debounceURLEnrichmentMs() {
139+
return this.config.debounceURLEnrichmentMs;
140+
}
141+
132142
set debounceURLEnrichmentMs(
133143
debounceURLEnrichmentMs: LinkPreviewsManagerConfig['debounceURLEnrichmentMs'],
134144
) {
@@ -142,10 +152,25 @@ export class LinkPreviewsManager implements ILinkPreviewsManager {
142152
this.composer.updateConfig({ linkPreviews: { debounceURLEnrichmentMs } });
143153
}
144154

155+
get enabled() {
156+
/**
157+
* We have to check whether the message will be enriched server side (url_enrichment).
158+
* If not, then it does not make sense to do previews in composer.
159+
*/
160+
return (
161+
!!this.channel.getConfig()?.url_enrichment &&
162+
this.composer.config.linkPreviews.enabled
163+
);
164+
}
165+
145166
set enabled(enabled: LinkPreviewsManagerConfig['enabled']) {
146167
this.composer.updateConfig({ linkPreviews: { enabled } });
147168
}
148169

170+
get findURLFn() {
171+
return this.config.findURLFn;
172+
}
173+
149174
set findURLFn(fn: LinkPreviewsManagerConfig['findURLFn']) {
150175
this.composer.updateConfig({ linkPreviews: { findURLFn: fn } });
151176
}
@@ -159,11 +184,11 @@ export class LinkPreviewsManager implements ILinkPreviewsManager {
159184
}
160185

161186
initState = ({ message }: { message?: DraftMessage | LocalMessage } = {}) => {
162-
this.state.next(initState({ message }));
187+
this.state.next(initState({ message: this.enabled ? message : undefined }));
163188
};
164189

165190
private _findAndEnrichUrls = async (text: string) => {
166-
if (!this.config.enabled) return;
191+
if (!this.enabled) return;
167192
const urls = this.config.findURLFn(text);
168193

169194
this.shouldDiscardEnrichQueries = !urls.length;

src/messageComposer/messageComposer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ export class MessageComposer {
473473
return;
474474
}
475475
}
476-
if (!this.config.linkPreviews?.enabled || nextValue.text === previousValue?.text)
476+
if (!this.linkPreviewsManager.enabled || nextValue.text === previousValue?.text)
477477
return;
478478
if (!nextValue.text) {
479479
this.linkPreviewsManager.clearPreviews();

test/unit/MessageComposer/linkPreviewsManager.test.ts

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import {
66
DraftResponse,
77
LinkPreviewsManagerConfig,
88
MessageComposer,
9+
MessageComposerConfig,
910
StreamChat,
1011
} from '../../../src';
12+
import { DeepPartial } from '../../../src/types.utility';
13+
import { mergeWith } from '../../../src/utils/mergeWith';
1114

1215
const existingLinkUrl = 'https://existing.com';
1316
const linkUrl = 'https://example.com';
@@ -63,12 +66,19 @@ const enrichURLReturnValue = {
6366
duration: 1000,
6467
};
6568

69+
const DEFAULT_CONFIG: DeepPartial<MessageComposerConfig> = {
70+
linkPreviews: {
71+
debounceURLEnrichmentMs: 0,
72+
enabled: true,
73+
},
74+
};
75+
6676
const setup = ({
6777
composition,
6878
config,
6979
}: {
7080
composition?: DraftResponse | LocalMessage;
71-
config?: Partial<LinkPreviewsManagerConfig>;
81+
config?: Partial<LinkPreviewsManagerConfig> | null;
7282
message?: DraftMessage | LocalMessage;
7383
} = {}) => {
7484
// Reset mocks
@@ -79,11 +89,12 @@ const setup = ({
7989
mockClient.enrichURL = vi.fn().mockResolvedValue(enrichURLReturnValue);
8090

8191
const mockChannel = mockClient.channel('channelType', 'channelId');
92+
mockChannel.getConfig = vi.fn().mockImplementation(() => ({ url_enrichment: true }));
8293
const messageComposer = new MessageComposer({
8394
client: mockClient,
8495
composition,
8596
compositionContext: mockChannel,
86-
config: { linkPreviews: config },
97+
config: config === null ? {} : mergeWith(DEFAULT_CONFIG, { linkPreviews: config }),
8798
});
8899
return { mockClient, mockChannel, messageComposer };
89100
};
@@ -97,7 +108,7 @@ describe('LinkPreviewsManager', () => {
97108
it('should initialize with default config', () => {
98109
const {
99110
messageComposer: { linkPreviewsManager },
100-
} = setup();
111+
} = setup({ config: null });
101112
expect(linkPreviewsManager.config.enabled).toBe(true);
102113
expect(linkPreviewsManager.config.debounceURLEnrichmentMs).toBe(
103114
DEFAULT_LINK_PREVIEW_MANAGER_CONFIG.debounceURLEnrichmentMs,
@@ -143,16 +154,40 @@ describe('LinkPreviewsManager', () => {
143154
expect(linkPreviewsManager.previews.size).toBe(1);
144155
expect(linkPreviewsManager.previews.get(linkUrl)).toBeDefined();
145156
});
157+
158+
it('should not initialize with message containing link previews if disabled', () => {
159+
const composition: LocalMessage = {
160+
id: 'test-message-id',
161+
text: '',
162+
type: 'regular',
163+
created_at: new Date(),
164+
deleted_at: null,
165+
pinned_at: null,
166+
status: 'pending',
167+
updated_at: new Date(),
168+
attachments: [
169+
{
170+
og_scrape_url: linkUrl,
171+
title: 'Example Title',
172+
type: 'link',
173+
},
174+
],
175+
};
176+
177+
const {
178+
messageComposer: { linkPreviewsManager },
179+
} = setup({ composition, config: { enabled: false } });
180+
181+
expect(linkPreviewsManager.previews.size).toBe(0);
182+
});
146183
});
147184

148185
describe('getters', () => {
149186
it('should return loadingPreviews correctly', async () => {
150187
const {
151188
messageComposer: { linkPreviewsManager },
152189
mockClient,
153-
} = setup({
154-
config: { debounceURLEnrichmentMs: 0, enabled: true },
155-
});
190+
} = setup();
156191

157192
// Mock the enrichURL to never resolve
158193
mockClient.enrichURL = vi.fn().mockImplementation(() => new Promise(() => {}));
@@ -174,10 +209,7 @@ describe('LinkPreviewsManager', () => {
174209
it('should return loadedPreviews correctly', async () => {
175210
const {
176211
messageComposer: { linkPreviewsManager },
177-
mockClient,
178-
} = setup({
179-
config: { debounceURLEnrichmentMs: 0, enabled: true },
180-
});
212+
} = setup();
181213

182214
// Add a loaded preview
183215
linkPreviewsManager.findAndEnrichUrls('Check out https://example.com');
@@ -348,22 +380,51 @@ describe('LinkPreviewsManager', () => {
348380
});
349381

350382
describe('findAndEnrichUrls', () => {
351-
it('should not process URLs if disabled', async () => {
383+
it('should not process URLs if disabled back-end url_enrichment', async () => {
384+
const {
385+
messageComposer: { linkPreviewsManager },
386+
mockChannel,
387+
mockClient,
388+
} = setup();
389+
mockChannel.getConfig.mockReturnValueOnce({ url_enrichment: false });
390+
linkPreviewsManager.findAndEnrichUrls('Check out https://example.com');
391+
let enrichPromiseResolve;
392+
mockClient.enrichURL = vi.fn().mockImplementation(() => {
393+
return new Promise((resolve) => {
394+
enrichPromiseResolve = resolve;
395+
});
396+
});
397+
linkPreviewsManager.findAndEnrichUrls('Check out https://example.com');
398+
// Wait for the debounced function to be called
399+
await new Promise((resolve) => setTimeout(resolve, 0));
400+
expect(mockClient.enrichURL).not.toHaveBeenCalled();
401+
expect(linkPreviewsManager.previews.size).toBe(0);
402+
});
403+
404+
it('should not process URLs if disabled via the manager config', async () => {
352405
const {
353406
messageComposer: { linkPreviewsManager },
354407
mockClient,
355408
} = setup({ config: { enabled: false } });
356409
linkPreviewsManager.findAndEnrichUrls('Check out https://example.com');
410+
let enrichPromiseResolve;
411+
mockClient.enrichURL = vi.fn().mockImplementation(() => {
412+
return new Promise((resolve) => {
413+
enrichPromiseResolve = resolve;
414+
});
415+
});
416+
linkPreviewsManager.findAndEnrichUrls('Check out https://example.com');
417+
// Wait for the debounced function to be called
418+
await new Promise((resolve) => setTimeout(resolve, 0));
357419
expect(mockClient.enrichURL).not.toHaveBeenCalled();
420+
expect(linkPreviewsManager.previews.size).toBe(0);
358421
});
359422

360423
it('should process URLs and create link previews', async () => {
361424
const {
362425
messageComposer: { linkPreviewsManager },
363426
mockClient,
364-
} = setup({
365-
config: { debounceURLEnrichmentMs: 0, enabled: true },
366-
});
427+
} = setup();
367428
let enrichPromiseResolve;
368429
mockClient.enrichURL = vi.fn().mockImplementation(() => {
369430
return new Promise((resolve) => {
@@ -385,9 +446,7 @@ describe('LinkPreviewsManager', () => {
385446
it('should update link preview status to LOADED when enrichment succeeds', async () => {
386447
const {
387448
messageComposer: { linkPreviewsManager },
388-
} = setup({
389-
config: { debounceURLEnrichmentMs: 0, enabled: true },
390-
});
449+
} = setup();
391450
linkPreviewsManager.findAndEnrichUrls('Check out https://example.com');
392451

393452
// Wait for the debounced function to be called and the promise to resolve
@@ -402,9 +461,7 @@ describe('LinkPreviewsManager', () => {
402461
const {
403462
messageComposer: { linkPreviewsManager },
404463
mockClient,
405-
} = setup({
406-
config: { debounceURLEnrichmentMs: 0, enabled: true },
407-
});
464+
} = setup();
408465
mockClient.enrichURL.mockRejectedValueOnce(new Error('Enrichment failed'));
409466

410467
linkPreviewsManager.findAndEnrichUrls('Check out https://example.com');
@@ -420,9 +477,7 @@ describe('LinkPreviewsManager', () => {
420477
const {
421478
messageComposer: { linkPreviewsManager },
422479
mockClient,
423-
} = setup({
424-
config: { debounceURLEnrichmentMs: 0, enabled: true },
425-
});
480+
} = setup();
426481
linkPreviewsManager.findAndEnrichUrls('Check out https://example.com');
427482

428483
// Wait for the debounced function to be called
@@ -440,9 +495,7 @@ describe('LinkPreviewsManager', () => {
440495
it('should not keep existing link previews if source string does not include them anymore', async () => {
441496
const {
442497
messageComposer: { linkPreviewsManager },
443-
} = setup({
444-
config: { debounceURLEnrichmentMs: 0, enabled: true },
445-
});
498+
} = setup();
446499
const existingPreview = {
447500
og_scrape_url: 'https://existing.com ',
448501
status: LinkPreviewStatus.LOADED,
@@ -467,9 +520,7 @@ describe('LinkPreviewsManager', () => {
467520
it('should keep existing link previews', async () => {
468521
const {
469522
messageComposer: { linkPreviewsManager },
470-
} = setup({
471-
config: { debounceURLEnrichmentMs: 0, enabled: true },
472-
});
523+
} = setup();
473524
const existingPreview = {
474525
og_scrape_url: 'https://existing.com',
475526
status: LinkPreviewStatus.LOADED,

test/unit/MessageComposer/messageComposer.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,9 +925,12 @@ describe('MessageComposer', () => {
925925
});
926926

927927
it('should find and enrich URLs when text changes and link previews are enabled', () => {
928-
const { messageComposer } = setup({
928+
const { mockChannel, messageComposer } = setup({
929929
config: { linkPreviews: { enabled: true } },
930930
});
931+
mockChannel.getConfig = vi
932+
.fn()
933+
.mockImplementation(() => ({ url_enrichment: true }));
931934
const spy = vi.spyOn(messageComposer.linkPreviewsManager, 'findAndEnrichUrls');
932935

933936
messageComposer.registerSubscriptions();

0 commit comments

Comments
 (0)