mirror of https://github.com/grafana/grafana.git
				
				
				
			ShortURL: Use RTK Query with the new k8s api (#111261)
This commit is contained in:
		
							parent
							
								
									c97a7bc2ce
								
							
						
					
					
						commit
						0ab7488305
					
				|  | @ -19,6 +19,20 @@ jest.mock('@grafana/runtime', () => ({ | ||||||
|   }, |   }, | ||||||
| })); | })); | ||||||
| 
 | 
 | ||||||
|  | jest.mock('app/store/store', () => ({ | ||||||
|  |   dispatch: jest.fn((action) => { | ||||||
|  |     // Return the mocked result directly
 | ||||||
|  |     return Promise.resolve({ | ||||||
|  |       data: { | ||||||
|  |         metadata: { | ||||||
|  |           name: 'bewyw48durgu8d', | ||||||
|  |           namespace: '1', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |   }), | ||||||
|  | })); | ||||||
|  | 
 | ||||||
| beforeEach(() => { | beforeEach(() => { | ||||||
|   Object.assign(navigator, { |   Object.assign(navigator, { | ||||||
|     clipboard: { |     clipboard: { | ||||||
|  | @ -29,6 +43,9 @@ beforeEach(() => { | ||||||
| 
 | 
 | ||||||
|   document.execCommand = jest.fn(); |   document.execCommand = jest.fn(); | ||||||
|   config.featureToggles.useKubernetesShortURLsAPI = false; |   config.featureToggles.useKubernetesShortURLsAPI = false; | ||||||
|  | 
 | ||||||
|  |   // Clear any caches between tests
 | ||||||
|  |   jest.clearAllMocks(); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| describe('createShortLink', () => { | describe('createShortLink', () => { | ||||||
|  | @ -40,9 +57,19 @@ describe('createShortLink', () => { | ||||||
| 
 | 
 | ||||||
| describe('createShortLink using k8s API', () => { | describe('createShortLink using k8s API', () => { | ||||||
|   it('creates short link', async () => { |   it('creates short link', async () => { | ||||||
|  |     // Mock window.location for k8s API test
 | ||||||
|  |     const mockLocation = { | ||||||
|  |       protocol: 'https:', | ||||||
|  |       host: 'www.test.grafana.com', | ||||||
|  |     }; | ||||||
|  |     Object.defineProperty(window, 'location', { | ||||||
|  |       value: mockLocation, | ||||||
|  |       writable: true, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     config.featureToggles.useKubernetesShortURLsAPI = true; |     config.featureToggles.useKubernetesShortURLsAPI = true; | ||||||
|     const shortUrl = await createShortLink('d/edhmipji89b0gb/welcome?orgId=1&from=now-6h&to=now&timezone=browser'); |     const shortUrl = await createShortLink('d/edhmipji89b0gb/welcome?orgId=1&from=now-6h&to=now&timezone=browser'); | ||||||
|     expect(shortUrl).toBe('www.test.grafana.com/goto/bewyw48durgu8d?orgId=1'); |     expect(shortUrl).toBe('https://www.test.grafana.com/goto/bewyw48durgu8d?orgId=1'); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,7 +11,8 @@ import { getDashboardUrl } from 'app/features/dashboard-scene/utils/getDashboard | ||||||
| import { dispatch } from 'app/store/store'; | import { dispatch } from 'app/store/store'; | ||||||
| 
 | 
 | ||||||
| import { ShortURL } from '../../../../apps/shorturl/plugin/src/generated/shorturl/v1alpha1/shorturl_object_gen'; | import { ShortURL } from '../../../../apps/shorturl/plugin/src/generated/shorturl/v1alpha1/shorturl_object_gen'; | ||||||
| import { BASE_URL as k8sShortURLBaseAPI } from '../../api/clients/shorturl/v1alpha1/baseAPI'; | import { generatedAPI } from '../../api/clients/shorturl/v1alpha1/endpoints.gen'; | ||||||
|  | import { extractErrorMessage } from '../../api/utils'; | ||||||
| import { ShareLinkConfiguration } from '../../features/dashboard-scene/sharing/ShareButton/utils'; | import { ShareLinkConfiguration } from '../../features/dashboard-scene/sharing/ShareButton/utils'; | ||||||
| 
 | 
 | ||||||
| import { copyStringToClipboard } from './explore'; | import { copyStringToClipboard } from './explore'; | ||||||
|  | @ -32,30 +33,48 @@ function getRelativeURLPath(url: string) { | ||||||
|   return path.startsWith('/') ? path.substring(1, path.length) : path; |   return path.startsWith('/') ? path.substring(1, path.length) : path; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const createShortLink = memoizeOne(async function (path: string) { | // Memoized legacy API call - preserves original behavior
 | ||||||
|  | const createShortLinkLegacy = memoizeOne(async (path: string): Promise<string> => { | ||||||
|  |   const shortLink = await getBackendSrv().post(`/api/short-urls`, { | ||||||
|  |     path: getRelativeURLPath(path), | ||||||
|  |   }); | ||||||
|  |   return shortLink.url; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export const createShortLink = async function (path: string) { | ||||||
|   try { |   try { | ||||||
|     if (config.featureToggles.useKubernetesShortURLsAPI) { |     if (config.featureToggles.useKubernetesShortURLsAPI) { | ||||||
|       // TODO: this is not ideal, we should use the RTK API but we can't call a hook from here and
 |       // Use RTK API - it handles caching/failures/retries automatically
 | ||||||
|       // this util function is being called from several places, will require a bigger refactor including some code that
 |       const result = await dispatch( | ||||||
|       // is deprecated.
 |         generatedAPI.endpoints.createShortUrl.initiate({ | ||||||
|       const k8sShortUrl: ShortURL = await getBackendSrv().post(`${k8sShortURLBaseAPI}/shorturls`, { |           shortUrl: { | ||||||
|         spec: { |             spec: { | ||||||
|           path: getRelativeURLPath(path), |               path: getRelativeURLPath(path), | ||||||
|         }, |             }, | ||||||
|       }); |           }, | ||||||
|       return buildShortUrl(k8sShortUrl); |         }) | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       if ('data' in result && result.data) { | ||||||
|  |         return buildShortUrl(result.data); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if ('error' in result) { | ||||||
|  |         const errorMessage = extractErrorMessage(result.error); | ||||||
|  |         throw new Error(errorMessage || 'Failed to create short URL'); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       throw new Error('Failed to create short URL'); | ||||||
|     } else { |     } else { | ||||||
|       // Old short URL API
 |       // Old API - use memoized function (preserves original behavior)
 | ||||||
|       const shortLink = await getBackendSrv().post(`/api/short-urls`, { |       return await createShortLinkLegacy(path); | ||||||
|         path: getRelativeURLPath(path), |  | ||||||
|       }); |  | ||||||
|       return shortLink.url; |  | ||||||
|     } |     } | ||||||
|   } catch (err) { |   } catch (err) { | ||||||
|     console.error('Error when creating shortened link: ', err); |     console.error('Error when creating shortened link: ', err); | ||||||
|     dispatch(notifyApp(createErrorNotification('Error generating shortened link'))); |     dispatch(notifyApp(createErrorNotification('Error generating shortened link'))); | ||||||
|  |     throw err; // Re-throw so callers know it failed
 | ||||||
|   } |   } | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Creates a ClipboardItem for the shortened link. This is used due to clipboard issues in Safari after making async calls. |  * Creates a ClipboardItem for the shortened link. This is used due to clipboard issues in Safari after making async calls. | ||||||
|  | @ -70,17 +89,18 @@ const createShortLinkClipboardItem = (path: string) => { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const createAndCopyShortLink = async (path: string) => { | export const createAndCopyShortLink = async (path: string) => { | ||||||
|   if (typeof ClipboardItem !== 'undefined' && navigator.clipboard.write) { |   try { | ||||||
|     navigator.clipboard.write([createShortLinkClipboardItem(path)]); |     if (typeof ClipboardItem !== 'undefined' && navigator.clipboard.write) { | ||||||
|     dispatch(notifyApp(createSuccessNotification('Shortened link copied to clipboard'))); |       await navigator.clipboard.write([createShortLinkClipboardItem(path)]); | ||||||
|   } else { |  | ||||||
|     const shortLink = await createShortLink(path); |  | ||||||
|     if (shortLink) { |  | ||||||
|       copyStringToClipboard(shortLink); |  | ||||||
|       dispatch(notifyApp(createSuccessNotification('Shortened link copied to clipboard'))); |       dispatch(notifyApp(createSuccessNotification('Shortened link copied to clipboard'))); | ||||||
|     } else { |     } else { | ||||||
|       dispatch(notifyApp(createErrorNotification('Error generating shortened link'))); |       const shortLink = await createShortLink(path); | ||||||
|  |       copyStringToClipboard(shortLink); | ||||||
|  |       dispatch(notifyApp(createSuccessNotification('Shortened link copied to clipboard'))); | ||||||
|     } |     } | ||||||
|  |   } catch (error) { | ||||||
|  |     // createShortLink already handles error notifications, just log
 | ||||||
|  |     console.error('Error in createAndCopyShortLink:', error); | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue