apiTokens.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import {
  2. addErrorMessage,
  3. addLoadingMessage,
  4. addSuccessMessage,
  5. } from 'sentry/actionCreators/indicator';
  6. import {LinkButton} from 'sentry/components/button';
  7. import EmptyMessage from 'sentry/components/emptyMessage';
  8. import ExternalLink from 'sentry/components/links/externalLink';
  9. import LoadingError from 'sentry/components/loadingError';
  10. import LoadingIndicator from 'sentry/components/loadingIndicator';
  11. import Panel from 'sentry/components/panels/panel';
  12. import PanelBody from 'sentry/components/panels/panelBody';
  13. import PanelHeader from 'sentry/components/panels/panelHeader';
  14. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  15. import {t, tct} from 'sentry/locale';
  16. import type {InternalAppApiToken} from 'sentry/types/user';
  17. import {isDemoModeEnabled} from 'sentry/utils/demoMode';
  18. import {
  19. getApiQueryData,
  20. setApiQueryData,
  21. useApiQuery,
  22. useMutation,
  23. useQueryClient,
  24. } from 'sentry/utils/queryClient';
  25. import useApi from 'sentry/utils/useApi';
  26. import ApiTokenRow from 'sentry/views/settings/account/apiTokenRow';
  27. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  28. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  29. const PAGE_TITLE = t('User Auth Tokens');
  30. const API_TOKEN_QUERY_KEY = ['/api-tokens/'] as const;
  31. export function ApiTokens() {
  32. const api = useApi();
  33. const queryClient = useQueryClient();
  34. const {
  35. data: tokenList = [],
  36. isLoading,
  37. isError,
  38. refetch,
  39. } = useApiQuery<InternalAppApiToken[]>(API_TOKEN_QUERY_KEY, {
  40. staleTime: 0,
  41. enabled: !isDemoModeEnabled(),
  42. });
  43. const {mutate: deleteToken} = useMutation({
  44. mutationFn: (token: InternalAppApiToken) => {
  45. return api.requestPromise('/api-tokens/', {
  46. method: 'DELETE',
  47. data: {tokenId: token.id},
  48. });
  49. },
  50. onMutate: token => {
  51. addLoadingMessage();
  52. queryClient.cancelQueries({queryKey: API_TOKEN_QUERY_KEY});
  53. const previous = getApiQueryData<InternalAppApiToken[]>(
  54. queryClient,
  55. API_TOKEN_QUERY_KEY
  56. );
  57. setApiQueryData<InternalAppApiToken[]>(
  58. queryClient,
  59. API_TOKEN_QUERY_KEY,
  60. oldTokenList => {
  61. return oldTokenList?.filter(tk => tk.id !== token.id);
  62. }
  63. );
  64. return {previous};
  65. },
  66. onSuccess: _data => {
  67. addSuccessMessage(t('Removed token'));
  68. },
  69. onError: (_error, _variables, context) => {
  70. addErrorMessage(t('Unable to remove token. Please try again.'));
  71. if (context?.previous) {
  72. setApiQueryData<InternalAppApiToken[]>(
  73. queryClient,
  74. API_TOKEN_QUERY_KEY,
  75. context.previous
  76. );
  77. }
  78. },
  79. onSettled: () => {
  80. queryClient.invalidateQueries({queryKey: API_TOKEN_QUERY_KEY});
  81. },
  82. });
  83. if (isLoading) {
  84. return <LoadingIndicator />;
  85. }
  86. if (isError) {
  87. return <LoadingError onRetry={refetch} />;
  88. }
  89. const isEmpty = !Array.isArray(tokenList) || tokenList.length === 0;
  90. const action = (
  91. <LinkButton
  92. priority="primary"
  93. size="sm"
  94. to="/settings/account/api/auth-tokens/new-token/"
  95. >
  96. {t('Create New Token')}
  97. </LinkButton>
  98. );
  99. return (
  100. <SentryDocumentTitle title={PAGE_TITLE}>
  101. <SettingsPageHeader title={PAGE_TITLE} action={action} />
  102. <TextBlock>
  103. {t(
  104. "Authentication tokens allow you to perform actions against the Sentry API on behalf of your account. They're the easiest way to get started using the API."
  105. )}
  106. </TextBlock>
  107. <TextBlock>
  108. {tct(
  109. 'For more information on how to use the web API, see our [link:documentation].',
  110. {
  111. link: <ExternalLink href="https://docs.sentry.io/api/" />,
  112. }
  113. )}
  114. </TextBlock>
  115. <Panel>
  116. <PanelHeader>{t('Auth Token')}</PanelHeader>
  117. <PanelBody>
  118. {isEmpty && (
  119. <EmptyMessage>
  120. {t("You haven't created any authentication tokens yet.")}
  121. </EmptyMessage>
  122. )}
  123. {tokenList?.map(token => (
  124. <ApiTokenRow key={token.id} token={token} onRemove={deleteToken} canEdit />
  125. ))}
  126. </PanelBody>
  127. </Panel>
  128. </SentryDocumentTitle>
  129. );
  130. }
  131. export default ApiTokens;