ShortURL: Use RTK Query with the new k8s api (#111261)

This commit is contained in:
Ezequiel Victorero 2025-09-18 12:37:21 -03:00 committed by GitHub
parent c97a7bc2ce
commit 0ab7488305
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 73 additions and 26 deletions

View File

@ -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(() => {
Object.assign(navigator, {
clipboard: {
@ -29,6 +43,9 @@ beforeEach(() => {
document.execCommand = jest.fn();
config.featureToggles.useKubernetesShortURLsAPI = false;
// Clear any caches between tests
jest.clearAllMocks();
});
describe('createShortLink', () => {
@ -40,9 +57,19 @@ describe('createShortLink', () => {
describe('createShortLink using k8s API', () => {
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;
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');
});
});

View File

@ -11,7 +11,8 @@ import { getDashboardUrl } from 'app/features/dashboard-scene/utils/getDashboard
import { dispatch } from 'app/store/store';
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 { copyStringToClipboard } from './explore';
@ -32,30 +33,48 @@ function getRelativeURLPath(url: string) {
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 {
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
// this util function is being called from several places, will require a bigger refactor including some code that
// is deprecated.
const k8sShortUrl: ShortURL = await getBackendSrv().post(`${k8sShortURLBaseAPI}/shorturls`, {
spec: {
path: getRelativeURLPath(path),
},
});
return buildShortUrl(k8sShortUrl);
// Use RTK API - it handles caching/failures/retries automatically
const result = await dispatch(
generatedAPI.endpoints.createShortUrl.initiate({
shortUrl: {
spec: {
path: getRelativeURLPath(path),
},
},
})
);
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 {
// Old short URL API
const shortLink = await getBackendSrv().post(`/api/short-urls`, {
path: getRelativeURLPath(path),
});
return shortLink.url;
// Old API - use memoized function (preserves original behavior)
return await createShortLinkLegacy(path);
}
} catch (err) {
console.error('Error when creating shortened link: ', err);
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.
@ -70,17 +89,18 @@ const createShortLinkClipboardItem = (path: string) => {
};
export const createAndCopyShortLink = async (path: string) => {
if (typeof ClipboardItem !== 'undefined' && navigator.clipboard.write) {
navigator.clipboard.write([createShortLinkClipboardItem(path)]);
dispatch(notifyApp(createSuccessNotification('Shortened link copied to clipboard')));
} else {
const shortLink = await createShortLink(path);
if (shortLink) {
copyStringToClipboard(shortLink);
try {
if (typeof ClipboardItem !== 'undefined' && navigator.clipboard.write) {
await navigator.clipboard.write([createShortLinkClipboardItem(path)]);
dispatch(notifyApp(createSuccessNotification('Shortened link copied to clipboard')));
} 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);
}
};