@squiz/resource-browser 3.3.9 → 3.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Change Log
2
2
 
3
+ ## 3.3.11
4
+
5
+ ### Patch Changes
6
+
7
+ - 6c4d5b3: Support for proxying all dam requests
8
+
9
+ ## 3.3.10
10
+
11
+ ### Patch Changes
12
+
13
+ - 8d3c8e0: source alias support
14
+ - Updated dependencies [8d3c8e0]
15
+ - @squiz/resource-browser-ui-lib@1.2.6
16
+
3
17
  ## 3.3.9
4
18
 
5
19
  ### Patch Changes
@@ -4,4 +4,5 @@ export declare const useAuth: (authConfig: AuthenticationConfiguration | undefin
4
4
  isAuthenticated: boolean;
5
5
  login: () => void;
6
6
  refreshAccessToken: () => Promise<string>;
7
+ alwaysUseResourceRequestProxy: boolean;
7
8
  };
@@ -61,6 +61,11 @@ const useAuth = (authConfig) => {
61
61
  setIsAuthenticated(!!token);
62
62
  };
63
63
  (0, react_1.useEffect)(() => {
64
+ // Don't run internal processes if we are always using the resource request proxy
65
+ if (authConfig?.alwaysUseResourceRequestProxy) {
66
+ // Consistent return value for cleanup function
67
+ return () => { };
68
+ }
64
69
  const handleTabChange = () => {
65
70
  if (!document.hidden) {
66
71
  syncAuthState();
@@ -75,15 +80,30 @@ const useAuth = (authConfig) => {
75
80
  };
76
81
  }, []);
77
82
  (0, react_1.useEffect)(() => {
83
+ // Don't run internal processes if we are always using the resource request proxy
84
+ if (authConfig?.alwaysUseResourceRequestProxy) {
85
+ return;
86
+ }
78
87
  refreshAccessToken().catch(() => {
79
88
  setIsAuthenticated(false);
80
89
  });
81
90
  }, [authConfig, refreshAccessToken]);
91
+ // Return some dummy data if we are always using the resource request proxy
92
+ if (authConfig?.alwaysUseResourceRequestProxy) {
93
+ return {
94
+ authToken: null,
95
+ isAuthenticated: true,
96
+ login: () => { },
97
+ refreshAccessToken: () => Promise.resolve(''),
98
+ alwaysUseResourceRequestProxy: true,
99
+ };
100
+ }
82
101
  return {
83
102
  authToken,
84
103
  isAuthenticated,
85
104
  login: handleLogin,
86
105
  refreshAccessToken,
106
+ alwaysUseResourceRequestProxy: false,
87
107
  };
88
108
  };
89
109
  exports.useAuth = useAuth;
@@ -31,7 +31,8 @@ const AuthProvider_1 = require("../ResourceBrowserContext/AuthProvider");
31
31
  exports.PluginRender = (0, react_1.forwardRef)(({ render, inline, inlineType, ...props }, forwardRef) => {
32
32
  if (!render)
33
33
  return react_1.default.createElement(react_1.default.Fragment, null);
34
- const requiresAuth = Boolean(props.source?.configuration?.authUrl);
34
+ const requiresAuth = Boolean(props.source?.configuration?.authUrl) ||
35
+ Boolean(props.source?.configuration?.alwaysUseResourceRequestProxy);
35
36
  const content = inline && inlineType ? (react_1.default.createElement(ResourceBrowserInlineButton_1.ResourceBrowserInlineButton, { ref: forwardRef, inlineType: inlineType, ...props })) : (react_1.default.createElement(ResourceBrowserInput_1.ResourceBrowserInput, { ...props }));
36
37
  return requiresAuth ? react_1.default.createElement(AuthProvider_1.AuthProvider, { authConfig: props.source }, content) : content;
37
38
  });
@@ -5,6 +5,7 @@ interface AuthContextProps {
5
5
  isAuthenticated: boolean;
6
6
  login: () => void;
7
7
  refreshAccessToken: () => Promise<any>;
8
+ alwaysUseResourceRequestProxy: boolean;
8
9
  }
9
10
  export declare const AuthContext: React.Context<AuthContextProps | undefined>;
10
11
  export declare const useAuthContext: () => AuthContextProps;
package/lib/index.js CHANGED
@@ -64,6 +64,23 @@ exports.ResourceBrowser = react_1.default.forwardRef((props, forwardRef) => {
64
64
  const [mode, setMode] = (0, react_1.useState)(null);
65
65
  const { data: sources, isLoading, error: sourcesError, reload: reloadSources } = (0, useSources_1.useSources)({ onRequestSources, plugins });
66
66
  const plugin = source?.plugin || null;
67
+ // Find source by its id or alias
68
+ const findSourceById = (value, sources) => {
69
+ let newSource = sources.find((source) => source.id === value?.sourceId) || null;
70
+ if (!newSource) {
71
+ newSource =
72
+ sources.find((source) => {
73
+ if (!source.aliases)
74
+ return false;
75
+ return source.aliases.includes(value.sourceId);
76
+ }) || null;
77
+ }
78
+ return newSource;
79
+ };
80
+ // Check if the value is for the source
81
+ const isValueForSource = (value, source) => {
82
+ return value?.sourceId === source.id || (source.aliases && source.aliases.includes(value?.sourceId));
83
+ };
67
84
  // MainContainer will render a list of sources of one is not provided to it, callback to allow it to set the source once a user selects
68
85
  const handleSourceSelect = (0, react_1.useCallback)((source, mode) => {
69
86
  setSource(source);
@@ -79,7 +96,7 @@ exports.ResourceBrowser = react_1.default.forwardRef((props, forwardRef) => {
79
96
  // If there is a provided value try to use its source
80
97
  if (value) {
81
98
  // Search the sources for it matching against the value.source property
82
- newSource = sources.find((source) => source.id === value?.sourceId) || null;
99
+ newSource = findSourceById(value, sources);
83
100
  // If the source is null and we arent loading the sources
84
101
  if (newSource === null && !isLoading) {
85
102
  // Set an error as the passed in value's source wasnt returned by onRequestSources
@@ -110,8 +127,7 @@ exports.ResourceBrowser = react_1.default.forwardRef((props, forwardRef) => {
110
127
  setMode(null);
111
128
  // If there is a value passed in, reset the source so the preselected asset preview renders correctly
112
129
  if (value && value.sourceId !== source?.id) {
113
- const source = sources.find((source) => source.id === value?.sourceId) || null;
114
- setSource(source);
130
+ setSource(findSourceById(value, sources));
115
131
  }
116
132
  }
117
133
  }, [sources, isModalOpen]);
@@ -129,6 +145,6 @@ exports.ResourceBrowser = react_1.default.forwardRef((props, forwardRef) => {
129
145
  };
130
146
  }, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange, onRetry: handleReset }),
131
147
  plugins.map((thisPlugin) => {
132
- return (react_1.default.createElement(Plugin_1.PluginRender, { key: thisPlugin.type, ref: forwardRef, type: thisPlugin.type, render: thisPlugin.type === plugin?.type, inline: !!inline, inlineType: inlineType, ...props, value: value && source ? (value.sourceId === source.id ? value : null) : null, isOtherSourceValue: value && source ? (value.sourceId !== source.id ? true : false) : false, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, error: sourcesError || error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: thisPlugin.useResolveResource, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange, onRetry: handleReset }));
148
+ return (react_1.default.createElement(Plugin_1.PluginRender, { key: thisPlugin.type, ref: forwardRef, type: thisPlugin.type, render: thisPlugin.type === plugin?.type, inline: !!inline, inlineType: inlineType, ...props, value: value && source ? (isValueForSource(value, source) ? value : null) : null, isOtherSourceValue: value && source ? (!isValueForSource(value, source) ? true : false) : false, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, error: sourcesError || error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: thisPlugin.useResolveResource, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange, onRetry: handleReset }));
133
149
  })));
134
150
  });
package/lib/types.d.ts CHANGED
@@ -8,11 +8,13 @@ export type AuthenticationConfiguration = {
8
8
  redirectUrl: string;
9
9
  clientId: string;
10
10
  scope: string;
11
+ alwaysUseResourceRequestProxy?: boolean;
11
12
  };
12
13
  export interface ResourceBrowserSource {
13
14
  name?: string;
14
15
  id: string;
15
16
  type: ResourceBrowserPluginType;
17
+ aliases?: string[];
16
18
  }
17
19
  export interface ResourceBrowserSourceWithPlugin extends ResourceBrowserSource {
18
20
  plugin: ResourceBrowserPlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/resource-browser",
3
- "version": "3.3.9",
3
+ "version": "3.3.11",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "private": false,
@@ -28,7 +28,7 @@
28
28
  "@react-types/shared": "^3.23.1",
29
29
  "@squiz/dx-json-schema-lib": "^1.67.0",
30
30
  "@squiz/generic-browser-lib": "^1.67.5",
31
- "@squiz/resource-browser-ui-lib": "^1.2.5",
31
+ "@squiz/resource-browser-ui-lib": "^1.2.6",
32
32
  "clsx": "^2.1.0",
33
33
  "expiry-map": "^2.0.0",
34
34
  "p-memoize": "^4.0.4",
@@ -29,9 +29,9 @@ describe('useAuth', () => {
29
29
  await waitFor(() => {
30
30
  expect(result.current.authToken).toBe('initialAuthToken');
31
31
  expect(result.current.isAuthenticated).toBe(true);
32
+ expect(result.current.alwaysUseResourceRequestProxy).toBe(false);
32
33
  });
33
34
  });
34
-
35
35
  it('should do nothing if missing crucial props', async () => {
36
36
  const authConfigMissingProps = {
37
37
  clientId: 'example-client-id',
@@ -285,7 +285,7 @@ describe('useAuth', () => {
285
285
  });
286
286
 
287
287
  it('should throw an error when getCookieValue throws unexpectedly', () => {
288
- jest.spyOn(authUtils, 'getCookieValue').mockImplementation(() => {
288
+ const getCookieValueSpy = jest.spyOn(authUtils, 'getCookieValue').mockImplementation(() => {
289
289
  throw new Error('Unexpected error');
290
290
  });
291
291
  const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
@@ -293,5 +293,48 @@ describe('useAuth', () => {
293
293
  expect(() => renderHook(() => useAuth(authConfig))).toThrow('Unexpected error');
294
294
 
295
295
  consoleSpy.mockRestore();
296
+ getCookieValueSpy.mockRestore();
297
+ });
298
+
299
+ describe('alwaysUseResourceRequestProxy', () => {
300
+ const authConfigAlwaysUseResourceRequestProxy = { ...authConfig, alwaysUseResourceRequestProxy: true };
301
+ it('should initialize with alwaysUseResourceRequestProxy true', async () => {
302
+ const { result } = renderHook(() => useAuth(authConfigAlwaysUseResourceRequestProxy));
303
+
304
+ await waitFor(() => {
305
+ expect(result.current.authToken).toBe(null);
306
+ expect(result.current.isAuthenticated).toBe(true);
307
+ expect(result.current.alwaysUseResourceRequestProxy).toBe(true);
308
+ });
309
+
310
+ // Check that refreshAccessToken is not called when authConfig changes
311
+ // Set up a spy and test that refreshAccessToken is not called when authConfig changes
312
+ const refreshAccessTokenSpy = jest.spyOn(authUtils, 'refreshAccessToken');
313
+
314
+ // Change authConfig and rerender
315
+ const newConfig = { ...authConfigAlwaysUseResourceRequestProxy, clientId: 'changed-client-id' };
316
+ const { rerender } = renderHook((props) => useAuth(props), { initialProps: authConfigAlwaysUseResourceRequestProxy });
317
+ rerender(newConfig);
318
+
319
+ // refreshAccessToken should not be called
320
+ expect(refreshAccessTokenSpy).not.toHaveBeenCalled();
321
+ });
322
+
323
+ it('should not attach event listeners when alwaysUseResourceRequestProxy is true', async () => {
324
+ mockGetCookieValue.mockReturnValueOnce(null);
325
+ const { result } = renderHook(() => useAuth(authConfigAlwaysUseResourceRequestProxy));
326
+
327
+ mockGetCookieValue.mockReturnValue('visibleAuthToken');
328
+
329
+ Object.defineProperty(document, 'hidden', { configurable: true, value: false });
330
+
331
+ act(() => {
332
+ document.dispatchEvent(new Event('visibilitychange'));
333
+ });
334
+
335
+ await waitFor(() => {
336
+ expect(result.current.authToken).toBe(null);
337
+ });
338
+ });
296
339
  });
297
340
  });
@@ -65,6 +65,12 @@ export const useAuth = (authConfig: AuthenticationConfiguration | undefined) =>
65
65
  };
66
66
 
67
67
  useEffect(() => {
68
+ // Don't run internal processes if we are always using the resource request proxy
69
+ if (authConfig?.alwaysUseResourceRequestProxy) {
70
+ // Consistent return value for cleanup function
71
+ return () => {};
72
+ }
73
+
68
74
  const handleTabChange = () => {
69
75
  if (!document.hidden) {
70
76
  syncAuthState();
@@ -82,15 +88,32 @@ export const useAuth = (authConfig: AuthenticationConfiguration | undefined) =>
82
88
  }, []);
83
89
 
84
90
  useEffect(() => {
91
+ // Don't run internal processes if we are always using the resource request proxy
92
+ if (authConfig?.alwaysUseResourceRequestProxy) {
93
+ return;
94
+ }
95
+
85
96
  refreshAccessToken().catch(() => {
86
97
  setIsAuthenticated(false);
87
98
  });
88
99
  }, [authConfig, refreshAccessToken]);
89
100
 
101
+ // Return some dummy data if we are always using the resource request proxy
102
+ if (authConfig?.alwaysUseResourceRequestProxy) {
103
+ return {
104
+ authToken: null,
105
+ isAuthenticated: true,
106
+ login: () => {},
107
+ refreshAccessToken: () => Promise.resolve(''),
108
+ alwaysUseResourceRequestProxy: true,
109
+ };
110
+ }
111
+
90
112
  return {
91
113
  authToken,
92
114
  isAuthenticated,
93
115
  login: handleLogin,
94
116
  refreshAccessToken,
117
+ alwaysUseResourceRequestProxy: false,
95
118
  };
96
119
  };
@@ -81,4 +81,21 @@ describe('Plugin', () => {
81
81
  expect(AuthContext.AuthProvider).toHaveBeenCalledWith({ authConfig: sourceWithAuth, children: expect.anything() }, {});
82
82
  });
83
83
  });
84
+
85
+ it('Wraps content in AuthProvider when alwaysUseResourceRequestProxy is true', async () => {
86
+ const sourceWithAuth = {
87
+ id: '123',
88
+ type: 'dam' as ResourceBrowserPluginType,
89
+ configuration: {
90
+ alwaysUseResourceRequestProxy: true,
91
+ },
92
+ };
93
+
94
+ const props = { ...baseProps, source: sourceWithAuth };
95
+ render(<PluginRender render={true} inline={false} {...props} />);
96
+
97
+ await waitFor(() => {
98
+ expect(AuthContext.AuthProvider).toHaveBeenCalledWith({ authConfig: sourceWithAuth, children: expect.anything() }, {});
99
+ });
100
+ });
84
101
  });
@@ -22,7 +22,9 @@ export type PluginRenderType = ResourceBrowserInputProps & {
22
22
  export const PluginRender = forwardRef<HTMLButtonElement, PluginRenderType>(({ render, inline, inlineType, ...props }, forwardRef) => {
23
23
  if (!render) return <></>;
24
24
 
25
- const requiresAuth = Boolean((props.source as ResourceBrowserSourceWithConfig)?.configuration?.authUrl);
25
+ const requiresAuth =
26
+ Boolean((props.source as ResourceBrowserSourceWithConfig)?.configuration?.authUrl) ||
27
+ Boolean((props.source as ResourceBrowserSourceWithConfig)?.configuration?.alwaysUseResourceRequestProxy);
26
28
 
27
29
  const content =
28
30
  inline && inlineType ? (
@@ -27,6 +27,7 @@ describe('AuthContext', () => {
27
27
  isAuthenticated: true,
28
28
  login: jest.fn(),
29
29
  refreshAccessToken: jest.fn(),
30
+ alwaysUseResourceRequestProxy: false,
30
31
  };
31
32
 
32
33
  beforeEach(() => {
@@ -36,12 +37,13 @@ describe('AuthContext', () => {
36
37
 
37
38
  it('should provide auth state and functions to consumers', async () => {
38
39
  const TestComponent = () => {
39
- const { authToken, isAuthenticated, login } = useAuthContext();
40
+ const { authToken, isAuthenticated, login, alwaysUseResourceRequestProxy } = useAuthContext();
40
41
  return (
41
42
  <div>
42
43
  <span>{`Token: ${authToken}`}</span>
43
44
  <span>{`Authenticated: ${isAuthenticated}`}</span>
44
45
  <button onClick={login}>Login</button>
46
+ <span>{`Always use resource request proxy: ${alwaysUseResourceRequestProxy}`}</span>
45
47
  </div>
46
48
  );
47
49
  };
@@ -54,6 +56,7 @@ describe('AuthContext', () => {
54
56
 
55
57
  expect(screen.getByText(/Token: testAuthToken/)).toBeInTheDocument();
56
58
  expect(screen.getByText(/Authenticated: true/)).toBeInTheDocument();
59
+ expect(screen.getByText(/Always use resource request proxy: false/)).toBeInTheDocument();
57
60
 
58
61
  fireEvent.click(screen.getByText('Login'));
59
62
 
@@ -7,6 +7,7 @@ interface AuthContextProps {
7
7
  isAuthenticated: boolean;
8
8
  login: () => void;
9
9
  refreshAccessToken: () => Promise<any>;
10
+ alwaysUseResourceRequestProxy: boolean;
10
11
  }
11
12
 
12
13
  export const AuthContext = createContext<AuthContextProps | undefined>(undefined);
@@ -3,7 +3,8 @@
3
3
  "name": "Bynder #1",
4
4
  "id": "c90feac1-55f3-4e1f-9b56-c22829e3f510",
5
5
  "type": "dam",
6
- "group": "DAM"
6
+ "group": "DAM",
7
+ "aliases": ["bynder-789"]
7
8
  },
8
9
  {
9
10
  "name": "Bynder #2",
@@ -141,6 +141,24 @@ describe('Resource browser input', () => {
141
141
  });
142
142
  });
143
143
 
144
+ it('If an aliased resource is provided will default to its true Source and Plugin to match', async () => {
145
+ const source = mockSource({ type: 'dam', aliases: ['alias-source-id'] });
146
+ mockRequestSources.mockResolvedValueOnce([source, mockSource({ type: 'matrix' })]);
147
+ mockResolveResource.mockResolvedValueOnce(mockResource({ source }));
148
+ renderComponent({ value: { sourceId: 'alias-source-id', resourceId: '100' } });
149
+
150
+ await waitFor(() => {
151
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
152
+ expect.objectContaining({
153
+ source: calculateExpectedSource(source),
154
+ plugin: mockDamPlugin,
155
+ pluginMode: null,
156
+ }),
157
+ {},
158
+ );
159
+ });
160
+ });
161
+
144
162
  it('If a resource is provided but its Source cannot be found it will error', async () => {
145
163
  const source = mockSource({ type: 'dam' });
146
164
  mockRequestSources.mockResolvedValueOnce([source, mockSource({ type: 'matrix' })]);
@@ -510,6 +528,73 @@ describe('Resource browser input', () => {
510
528
  });
511
529
  });
512
530
 
531
+ it('onModalStateChange called with false will reset the Source and Plugin to the aliases true source if the value uses an alias', async () => {
532
+ const originalSource = mockSource({
533
+ id: 'original-source-id',
534
+ name: 'Original Source',
535
+ type: 'dam',
536
+ aliases: ['alias-source-id'],
537
+ });
538
+ const aliasSourceId = 'alias-source-id';
539
+ const valueWithAlias = {
540
+ sourceId: aliasSourceId,
541
+ resourceId: '123456',
542
+ };
543
+
544
+ const sourcesInput = [originalSource, mockSource({ id: '2' })];
545
+ const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
546
+ mockRequestSources.mockResolvedValueOnce(sourcesInput);
547
+
548
+ renderComponent({ value: valueWithAlias });
549
+
550
+ await waitFor(() => {
551
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
552
+ expect.objectContaining({
553
+ value: valueWithAlias,
554
+ sources: calculatedSources,
555
+ source: calculatedSources[0],
556
+ plugin: mockDamPlugin,
557
+ }),
558
+ {},
559
+ );
560
+ });
561
+
562
+ const { onModalStateChange, setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
563
+ // Invoke open and close the modal
564
+ act(() => {
565
+ onModalStateChange(true);
566
+ });
567
+
568
+ // Change the source
569
+ act(() => {
570
+ setSource(calculatedSources[1]);
571
+ });
572
+
573
+ await waitFor(() => {
574
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
575
+ expect.objectContaining({
576
+ source: calculatedSources[1],
577
+ plugin: mockDamPlugin,
578
+ }),
579
+ {},
580
+ );
581
+ });
582
+
583
+ act(() => {
584
+ onModalStateChange(false);
585
+ });
586
+
587
+ await waitFor(() => {
588
+ expect(RBI.ResourceBrowserInput).toHaveBeenLastCalledWith(
589
+ expect.objectContaining({
590
+ source: calculatedSources[0],
591
+ plugin: mockDamPlugin,
592
+ }),
593
+ {},
594
+ );
595
+ });
596
+ });
597
+
513
598
  it('onModalStateChange called with false will reset the Mode', async () => {
514
599
  const sourcesInput = [mockSource({ type: 'dam' }), mockSource()];
515
600
  mockRequestSources.mockResolvedValueOnce(sourcesInput);
@@ -780,5 +865,49 @@ describe('Resource browser input', () => {
780
865
  );
781
866
  });
782
867
  });
868
+
869
+ it('When resource browser is called with a value containing an alias source, Plugin is called with the original source not the alias value', async () => {
870
+ const originalSource = mockSource({
871
+ id: 'original-source-id',
872
+ name: 'Original Source',
873
+ type: 'dam',
874
+ aliases: ['alias-source-id'],
875
+ });
876
+ const aliasSourceId = 'alias-source-id';
877
+
878
+ mockRequestSources.mockResolvedValueOnce([originalSource]);
879
+
880
+ // Create a value that uses the alias source ID
881
+ const valueWithAlias = {
882
+ sourceId: aliasSourceId,
883
+ resourceId: '123456',
884
+ };
885
+
886
+ renderComponent({ value: valueWithAlias });
887
+
888
+ await waitFor(() => {
889
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
890
+ expect.objectContaining({
891
+ value: valueWithAlias,
892
+ source: calculateExpectedSource(originalSource), // Should be the original source, not alias
893
+ plugin: mockDamPlugin,
894
+ }),
895
+ {},
896
+ );
897
+ });
898
+
899
+ // Verify that the PluginRender is called with the original source
900
+ await waitFor(() => {
901
+ expect(PluginRenderSpy).toHaveBeenCalledWith(
902
+ expect.objectContaining({
903
+ render: true,
904
+ type: 'dam',
905
+ source: calculateExpectedSource(originalSource), // Should be the original source
906
+ isOtherSourceValue: false,
907
+ }),
908
+ null,
909
+ );
910
+ });
911
+ });
783
912
  });
784
913
  });
@@ -103,6 +103,15 @@ RestrictedSource.args = {
103
103
  searchEnabled: true,
104
104
  };
105
105
 
106
+ export const SourceByAlias = Template.bind({});
107
+ SourceByAlias.args = {
108
+ ...Primary.args,
109
+ value: {
110
+ resourceId: '1f7a25b4-380f-4540-9555-8be2dcab4019',
111
+ sourceId: 'bynder-789',
112
+ },
113
+ };
114
+
106
115
  export const SearchEnabled = Template.bind({});
107
116
  SearchEnabled.args = {
108
117
  ...Primary.args,
package/src/index.tsx CHANGED
@@ -60,6 +60,24 @@ export const ResourceBrowser = React.forwardRef<HTMLButtonElement, ResourceBrows
60
60
  const { data: sources, isLoading, error: sourcesError, reload: reloadSources } = useSources({ onRequestSources, plugins });
61
61
  const plugin = source?.plugin || null;
62
62
 
63
+ // Find source by its id or alias
64
+ const findSourceById = (value: ResourceBrowserUnresolvedResource, sources: ResourceBrowserSourceWithPlugin[]) => {
65
+ let newSource = sources.find((source) => source.id === value?.sourceId) || null;
66
+ if (!newSource) {
67
+ newSource =
68
+ sources.find((source) => {
69
+ if (!source.aliases) return false;
70
+ return source.aliases.includes(value.sourceId);
71
+ }) || null;
72
+ }
73
+ return newSource;
74
+ };
75
+
76
+ // Check if the value is for the source
77
+ const isValueForSource = (value: ResourceBrowserUnresolvedResource, source: ResourceBrowserSourceWithPlugin) => {
78
+ return value?.sourceId === source.id || (source.aliases && source.aliases.includes(value?.sourceId));
79
+ };
80
+
63
81
  // MainContainer will render a list of sources of one is not provided to it, callback to allow it to set the source once a user selects
64
82
  const handleSourceSelect = useCallback(
65
83
  (source: ResourceBrowserSourceWithPlugin, mode?: PluginLaunchMode) => {
@@ -80,7 +98,8 @@ export const ResourceBrowser = React.forwardRef<HTMLButtonElement, ResourceBrows
80
98
  // If there is a provided value try to use its source
81
99
  if (value) {
82
100
  // Search the sources for it matching against the value.source property
83
- newSource = sources.find((source) => source.id === value?.sourceId) || null;
101
+ newSource = findSourceById(value, sources);
102
+
84
103
  // If the source is null and we arent loading the sources
85
104
  if (newSource === null && !isLoading) {
86
105
  // Set an error as the passed in value's source wasnt returned by onRequestSources
@@ -116,8 +135,7 @@ export const ResourceBrowser = React.forwardRef<HTMLButtonElement, ResourceBrows
116
135
 
117
136
  // If there is a value passed in, reset the source so the preselected asset preview renders correctly
118
137
  if (value && value.sourceId !== source?.id) {
119
- const source = sources.find((source) => source.id === value?.sourceId) || null;
120
- setSource(source);
138
+ setSource(findSourceById(value, sources));
121
139
  }
122
140
  }
123
141
  }, [sources, isModalOpen]);
@@ -168,8 +186,8 @@ export const ResourceBrowser = React.forwardRef<HTMLButtonElement, ResourceBrows
168
186
  inline={!!inline}
169
187
  inlineType={inlineType}
170
188
  {...props}
171
- value={value && source ? (value.sourceId === source.id ? value : null) : null} // Don't pass value if value isn't for selected source
172
- isOtherSourceValue={value && source ? (value.sourceId !== source.id ? true : false) : false} // Block render of existing asset if not from this source
189
+ value={value && source ? (isValueForSource(value, source) ? value : null) : null} // Don't pass value if value isn't for selected source
190
+ isOtherSourceValue={value && source ? (!isValueForSource(value, source) ? true : false) : false} // Block render of existing asset if not from this source
173
191
  source={source}
174
192
  sources={sources}
175
193
  setSource={handleSourceSelect}
package/src/types.ts CHANGED
@@ -10,6 +10,9 @@ export type AuthenticationConfiguration = {
10
10
  redirectUrl: string;
11
11
  clientId: string;
12
12
  scope: string;
13
+
14
+ // Route all DAM requests through the DAM proxy
15
+ alwaysUseResourceRequestProxy?: boolean;
13
16
  };
14
17
 
15
18
  export interface ResourceBrowserSource {
@@ -19,6 +22,9 @@ export interface ResourceBrowserSource {
19
22
  id: string;
20
23
  // Source type e.g. Matrix, DAM etc determines what plugin will be used for resource selection
21
24
  type: ResourceBrowserPluginType;
25
+
26
+ //alias' for the source to identify plugin to use for the source
27
+ aliases?: string[];
22
28
  }
23
29
 
24
30
  export interface ResourceBrowserSourceWithPlugin extends ResourceBrowserSource {